greenDAO manyToMany reflexive relation - android

I am trying to figure out how to model this relation for Android in GreenDao.
We have a NavigationNode, which can have ManytoMany other NavigationNodes.
In Rails we modelled this with a 'has_many :through' with a NavigationPath join table:
class NavigationPoint < ActiveRecord::Base
has_many :navigation_paths, dependent: :destroy
has_many :paths, through: :navigation_paths, source: :navigation_point, dependent: :destroy
end
class NavigationPath < ActiveRecord::Base
attr_accessible :navigation_point_id, :connected_point_id
belongs_to :navigation_point, foreign_key: :connected_point_id
end
And in iOS we just set it as a relation to self on NavigationNode:
Relationship Destination Inverse
-------------------------------------------
paths NavigationNode paths
But I can't seem to find the right configuration for greenDAO.
Any help / hint would be appreciated, thanks
Update:
I implemented a temporary solution for now:
The DAO for NavigationPath (the join table):
Entity navigationPath = schema.addEntity("NavigationPath");
navigationPath.setSuperclass(SUPER_CLASS);
navigationPath.setIsJoinTable(true);
navigationPath.addIdProperty();
Property fromNavigationNodeProperty = navigationPath.addLongProperty("NavigationNodeID").getProperty();
Property toNavigationNodeProperty = navigationPath.addLongProperty("toNavigationNodeID").getProperty();
ToMany paths = navigationNode.addToMany(navigationPath, fromNavigationNodeProperty, "navigationPaths");
Index indexNavigationPath = new Index();
indexNavigationPath.addProperty(fromNavigationNodeProperty);
indexNavigationPath.addProperty(toNavigationNodeProperty);
indexNavigationPath.makeUnique();
navigationPath.addIndex(indexNavigationPath);
And here the custom relation method in NavigationPath:
public List<NavigationNode> getPaths(String where) {
if (daoSession == null) {
throw new DaoException("Entity is detached from DAO context");
}
NavigationPathDao pathDao = daoSession.getNavigationPathDao();
QueryBuilder pathsQuery = pathDao.queryBuilder().where(NavigationPathDao.Properties.NavigationNodeID.eq(id));
List<NavigationPath> navigationPaths = pathsQuery.list();
List<NavigationNode> navigationNodesNew = new ArrayList<NavigationNode>();
for (NavigationPath path : navigationPaths) {
Long id = path.getToNavigationNodeID();
NavigationNode node = myDao.load(id);
if (node != null)
navigationNodesNew.add(node);
}
return navigationNodesNew;
}
It's a pretty naive implementation, but for now it is working.
But again, tips are appreciate.

From greenDao website:
Many-to-Many Relations (n:m)
In databases, n:m relations are modeled using a join table. The join
table holds entries having rows with foreign keys to each of the
relating tables. While greenDAO does not support n:m relations
directly as of now, you can model the join table as a separate entity.
In practice, you often have “relation entities” with additional
properties, so you might want to do so anyway. In future releases,
greenDAO might introduce direct support of n:m relations.
It seems that isn't possible at this moment without a relation entity, maybe you could try that.

Related

Room DB - Running insertion into M2M Relation inside Transaction

I am inserting a new book into my book table and after trying to assign it to a many-to-many relation table. Imo this should run in a transaction.
(Because if the m2m insertion fails, the information about the realtionship is lost). My code now looks as follows and fails as i cannot access the BookUserXRefDao.insert(bookUser); query due to static context errors.
Is there an easy way to fix this?
#Transaction
public void insertBook(Book theBook, List<Integer> userIds){
long newBookId= insert(theBook);
//Insert into the m2m relation
BookUserXRef[] bookUser = new BookUserXRef[userIds.size()];
for (int i = 0; i < userIds.size(); i++) {
BookUserXRef[i] = new BookUserXRef(newBookId,userIds.get(i));
}
BookUserXRefDao.insert(bookUser);
}
Just realized that i can access the Singleton Database Instance from within my transaction.
Therefore i could just use
AppDb.getAppDb().BookUserXRefDao().insert(bookUser);
That solved the problem.

How to implement relationships in Greendao?

I am new to Greendao.I am writing a generator for generating entities in greendao.So,I have two entities Hospital and patient. They have a one to many relationship between them. So,a hospital can have many patients but one patient can have only one hospital. Now Property hospitalId = patient.addLongProperty("hospitalId").getProperty(); this adds a hospitalid column to patient table. and
ToMany hospitalToPatients = hospital.addToMany(patient, hospitalId);
This line creates a one to many relationship between hospital and patient.So what is the use of the lines patient.addToOne(hospital, hospitalId); and hospitalToPatients.setName("patients"); How to implement one to one,one to many,many to one and many to many relationships in greendao ?
PS: I copied this code from http://www.vertabelo.com/blog/technical-articles/a-comparison-of-android-orms
public class ProjectGenerator {
public static void main(String[] args) throws Exception {
Schema schema = new Schema(1, "com.example.project");
// hospital table
Entity hospital = schema.addEntity("Hospital");
hospital.addIdProperty();
hospital.addStringProperty("name");
// patient table
Entity patient = schema.addEntity("Patient");
patient.addIdProperty();
patient.addStringProperty("name");
Property hospitalId = patient.addLongProperty("hospitalId").getProperty();
// patient has a one assigned hospital
patient.addToOne(hospital, hospitalId);
// hospital has many patients
ToMany hospitalToPatients = hospital.addToMany(patient, hospitalId);
hospitalToPatients.setName("patients");
// trigger generation with path to the Android project
new DaoGenerator().generateAll(schema, "../project/src/main/java");
}
}
So what is the use of the lines patient.addToOne(hospital, hospitalId)
This line is creating a oneToOne relation between hospital and patient .
hospitalToPatients.setName("patients")
This is just setting the name of foreign key .
As you can see, you have already implemented implement one to one,one to many relationship in your example . patient.addToOne is an example of OneToOne relationships . hospital.addToMany is an example of OneToMany relationships . And greenDao doesn't support ManyToMany relationship directly for more details you can read this .

"WHERE IN" statement using greendao Android ORM

I am using greendao to work with sqlite database in my app.
I need to have such feature as where in condition
I am looking for such method or any other possible ways ,but without using raw query.
I need to perform such queries SELECT * FROM news WHERE id_company IN (SELECT id FROM company WHERE state=1.
Please suggest what is the best way to perform such query using GreenDAO ORM.
You could use Lists.transform() from Guava as described here link
List<Company> companies = session.getCompanyDao()
.queryBuilder()
.where(CompanyDao.properties.state.eq(1))
.list();
Function<Company, Integer> companyToId = new Function<Company,Integer>() {
public String apply(Company c) { return c.getId(); }
};
List<Integer> ids = Lists.transform(companies, compnayToId);
session.getNewsDao()
.queryBuilder()
.where(NewsDao.Properties.id_company.in(ids))
.list();

ORMLite joins queries and Order by

I'm tring to make join in two tables and get all columns in both, I did this:
QueryBuilder<A, Integer> aQb = aDao.queryBuilder();
QueryBuilder<B, Integer> bQb = bDao.queryBuilder();
aQb.join(bQb).prepare();
This equates to:
SELECT 'A'.* FROM A INNER JOIN B WHERE A.id = B.id;
But I want:
SELECT * FROM A INNER JOIN B WHERE A.id = B.id;
Other problem is when taking order by a field of B, like:
aQb.orderBy(B.COLUMN, true);
I get an error saying "no table column B".
When you are using the QueryBuilder, it is expecting to return B objects. They cannot contain all of the fields from A in B. It will not flesh out foreign sub-fields if that is what you mean. That feature has not crossed the lite barrier for ORMLite.
Ordering on join-table is also not supported. You can certainly add the bQb.orderBy(B.COLUMN, true) but I don't think that will do what you want.
You can certainly use raw-queries for this although it is not optimal.
Actually, I managed to do it without writing my whole query as raw query. This way, I didn't need to replace my query builder codes (which is pretty complicated). To achieve that, I followed the following steps:
(Assuming I have two tables, my_table and my_join_table and their daos, I want to order my query on my_table by the column order_column_1 of the my_join_table)
1- Joined two query builders & used QueryBuilder.selectRaw(String... columns) method to include the original table's + the columns I want to use in foreign sort. Example:
QueryBuilder<MyJoinTable, MyJoinPK> myJoinQueryBuilder = myJoinDao.queryBuilder();
QueryBuilder<MyTable, MyPK> myQueryBuilder = myDao.queryBuilder().join(myJoinQueryBuilder).selectRaw("`my_table`.*", "`my_join_table`.`order_column` as `order_column_1`");
2- Included my order by clauses like this:
myQueryBuilder.orderByRaw("`order_column_1` ASC");
3- After setting all the select columns & order by clauses, it's time to prepare the statement:
String statement = myQueryBuilder.prepare().getStatement();
4- Get the table info from the dao:
TableInfo tableInfo = ((BaseDaoImpl) myDao).getTableInfo();
5- Created my custom column-to-object mapper which just ignores the unknown column names. We avoid the mapping error of our custon columns (order_column_1 in this case) by doing this. Example:
RawRowMapper<MyTable> mapper = new UnknownColumnIgnoringGenericRowMapper<>(tableInfo);
6- Query the table for the results:
GenericRawResults<MyTable> results = activityDao.queryRaw(statement, mapper);
7- Finally, convert the generic raw results to list:
List<MyTable> myObjects = new ArrayList<>();
for (MyTable myObject : results) {
myObjects.add(myObject);
}
Here's the custom row mapper I created by modifying (just swallowed the exception) com.j256.ormlite.stmt.RawRowMapperImpl to avoid the unknown column mapping errors. You can copy&paste this into your project:
import com.j256.ormlite.dao.RawRowMapper;
import com.j256.ormlite.field.FieldType;
import com.j256.ormlite.table.TableInfo;
import java.sql.SQLException;
public class UnknownColumnIgnoringGenericRowMapper<T, ID> implements RawRowMapper<T> {
private final TableInfo<T, ID> tableInfo;
public UnknownColumnIgnoringGenericRowMapper(TableInfo<T, ID> tableInfo) {
this.tableInfo = tableInfo;
}
public T mapRow(String[] columnNames, String[] resultColumns) throws SQLException {
// create our object
T rowObj = tableInfo.createObject();
for (int i = 0; i < columnNames.length; i++) {
// sanity check, prolly will never happen but let's be careful out there
if (i >= resultColumns.length) {
continue;
}
try {
// run through and convert each field
FieldType fieldType = tableInfo.getFieldTypeByColumnName(columnNames[i]);
Object fieldObj = fieldType.convertStringToJavaField(resultColumns[i], i);
// assign it to the row object
fieldType.assignField(rowObj, fieldObj, false, null);
} catch (IllegalArgumentException e) {
// log this or do whatever you want
}
}
return rowObj;
}
}
It's pretty hacky & seems like overkill for this operation but I definitely needed it and this method worked well.

greendao Left join dilemma

I'm using greenDao and I need to extract data from several tables similar to left-join funcionality. Here's a cite from my schema generator:
private static void genRetailers(Schema schema) {
// create retailer entity
Entity retailerEntity = schema.addEntity("Retailer");
retailerEntity.addIdProperty().notNull();
retailerEntity.addStringProperty("title");
Entity shopEntity = schema.addEntity("Shop");
shopEntity.addIdProperty().notNull();
shopEntity.addStringProperty("address");
Property retailerId = shopEntity.addLongProperty("retailerId")
.getProperty();
// (1) Retailer < - > (*) Shop
retailerEntity.addToMany(shopEntity, retailerId);
}
When I do:
return (ArrayList<Retailer>) mDaoSession.getRetailerDao()
.queryBuilder().list();
I only get contents of the Retailer table itself, however I also need Shop entity values which're null. Only after I call getShops() the entities get filled. I need to fill entities right at the first query. How is it done ?
Thanks.
Use the queryDeep method:
return mDaoSession.getRetailerDao().queryDeep(null);

Categories

Resources