Database tables may relate to each other using 1:1, 1:n, or m:n relations. If you are new to database relations, it’s a good idea to catch up before we discus ORM specifics. Here are some random links discussing relations in general.
In greenDAO, entities relate using to-one or to-many relations. For example, if you want to model a 1:n relation in greenDAO, you will have a to-one and a to-many relation. However, note that the to-one and a to-many relations are not connected with each other, so you will have to update both.
Relation Names and multiple Relations
Each relation has a name, which is used for the corresponding property in the generated entity hosting the relationship. The default name is the name of the target entity. This name can be overridden by using the setName method. Keep in mind that the default name cannot not unique if an entity has multiple relations to the same target entity. In this case you must specify relation names explicitly.
Modelling To-One Relations
In your greenDAO generator model you must model a property for the foreign key (ID) value. Using this property, you can add a to-one relation using Entity.addToOne.
Example: A user has a picture.
Property pictureIdProperty = user.addLongProperty("pictureId").getProperty();
user.addToOne(picture, pictureIdProperty);
This will result in a User entity having a Picture property (getPicture/setPicture), and you can work directly with Picture objects.
The getter Methods of to-one relations resolve the target entity lazily on the first access. Subsequent accesses will return the previously resolved object directly.
Note that the foreign key property (“pictureId”) and the entity object property (“picture”) are tied together. If you change the the pictureId, the next getPicture() call will resolve the Picture entity for the updated ID. Also, if you set a new Picture entity, the pictureId property will be updated as well.
greenDAO supports also eager loading to-one relations. It will resolve an entity with all to-one relations with a single database query. This is great for performance if you need the related entities anyway. Currently, you use loadDeep and queryDeep methods of the generated DAO to use this feature (may change in the future).
Relation Names and multiple Relations
Each relation has a name, which is used for the corresponding property in the generated entity hosting the relationship. The default name is the name of the target entity. This name can be overridden by using the setName method. The default name is not unique if an Entity has multiple relations to the same target entity. In this case you must specify relation names explicitly.
Let’s expand the previous example, and say the user also has a thumbnail picture. Because both the main picture and the thumbnail refer to the same entity Picture, there would be a name collision. Thus, we rename the second relation to “thumbnail”:
Property thumbnailIdProperty = user.addLongProperty("thumbnailId").getProperty();
user.addToOne(picture, pictureIdProperty);
user.addToOne(picture, thumbnailIdProperty, "thumbnail");
Modelling To-Many Relations
To-many relations are modeled like to-one relations, except that the foreign key is placed in the destination table. Let’s have a look at a customer/order example. A customer can place multiple orders, so we have an to-many relation. In the database we create the 1:N relation by adding a customer ID column to the order table. Like this, it’s possible to query all orders for a customer using the customer’s ID.
Modelling the to-many relation in greenDAO is very close to the database approach. First, you need to add a property in the destination entity to reference the source entity of the to-many relation. Then you can add a to-many relation to the source entity using the just added property in the destination entity.
Let’s assume we have a customer and an order entity, and we want to connect orders to a customer. The following code adds the to-many relation to the customer entity:
ToMany customerToOrders = customer.addToMany(order, customerId);
customerToOrders.setName("orders"); // Optional
customerToOrders.orderAsc(orderDate); // Optional
Like this, we can simply call the generated getOrders() method of the Customer class to get the orders:
Resolving and Updating To-Many Relations
To-many relations are resolved lazily on the first request. After that, the related entities are cached in the source entity inside a List object. Subsequent calls to the get method of the relation do not query the database.
Note that updating to-many relations require some additional work. Because to-many lists are cached, they are not updated when related entities are added to the database. The following code illustrates the behavior:
int size1 = orders1.size();
Order order = new Order();
order.setCustomerId(customer.getId());
daoSession.insert(order);
Listorders2 = customer.getOrders();
// size1 == orders2.size(); // NOT updated
// orders1 == orders2; // SAME list object
Because of caching, you should add new relation entities manually to the to-many list of the source entity. This is how to insert new entities that are part of a to-many relation:
- Get the to-many Java List (This has to be done this before persisting the new entity, because we do not know if we get a cached for fresh result. Like this, we know it’s cached now.)
- Create a new entity object (on the many side)
- Set the foreign property of the new entity to the target entity
- Persist the new object using insert
- Add the new object to the to-many Java List
Example code:
newOrder.setCustomerId(customer.getId());
daoSession.insert(newOrder);
orders.add(newOrder);
Note that getOrders is called before insert to ensure the list is cached. If getOrders would be called after insert, newOrder would occur in the list twice if the orders are not cached before.
Likewise, you can delete related entities:
daoSession.delete(newOrder);
orders.remove(newOrder);
Sometimes, it may be cumbersome or even impossible to update all to-many relations manually after related entities were added or removed. To the rescue, greenDAO has reset methods to clear the cached list. If a to-many relation may have changed potentially, you can force greenDAO to reload the list of related entities:
List orders2 = customer.getOrders();
Bi-Directional 1:N Relations
Sometimes you want to navigate 1:N relations in both directions. In greenDAO, you have to add a to-one and a to-many relation to achieve this. The following example shows the complete modelling of the customer and order entities, we used as an example before. This time, we use the customerId property for creating both relations:
customer.addIdProperty();
customer.addStringProperty("name").notNull();
Entity order = schema.addEntity("Order");
order.setTableName("ORDERS"); // "ORDER" is a reserved keyword
order.addIdProperty();
Property orderDate = order.addDateProperty("date").getProperty();
Property customerId = order.addLongProperty("customerId").notNull().getProperty();
order.addToOne(customer, customerId);
ToMany customerToOrders = customer.addToMany(order, customerId);
customerToOrders.setName("orders");
customerToOrders.orderAsc(orderDate);
Let’s assume we have an order entity. Using both relations, we could get the customer and all orders the customer has ever made:
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.
Modelling Tree Relations (Example)
You can model a tree relation by modelling an entity having a to-one and a to-many relation pointing to itself:
treeEntity.addIdProperty();
Property parentIdProperty = treeEntity.addLongProperty("parentId").getProperty();
treeEntity.addToOne(treeEntity, parentIdProperty).setName("parent");
treeEntity.addToMany(treeEntity, parentIdProperty).setName("children");
The generated entity lets you navigate to its parent and children:
List grandChildren = child.getChildren();
More examples
Check out the example project for a complete code example. Check out the and the classes.
Also, the and project in the open source distribution comes with several relation tests, which may serve as further examples.