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.

// The variables "user" and "picture" are just regular entities
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 pictureIdProperty = user.addLongProperty("pictureId").getProperty();
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:

Property customerId = order.addLongProperty("customerId").notNull().getProperty();
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:

List orders = customer.getOrders();

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:

List orders1 = customer.getOrders();
int size1 = orders1.size();

Order order = new 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:

  1. 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.)
  2. Create a new entity object (on the many side)
  3. Set the foreign property of the new entity to the target entity
  4. Persist the new object using insert
  5. Add the new object to the to-many Java List

Example code:

List orders = customer.getOrders();

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:

List orders = customer.getOrders();

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:

Entity customer = schema.addEntity("Customer");

Entity order = schema.addEntity("Order");
order.setTableName("ORDERS"); // "ORDER" is a reserved keyword
Property orderDate = order.addDateProperty("date").getProperty();
Property customerId = order.addLongProperty("customerId").notNull().getProperty();
order.addToOne(customer, customerId);

ToMany customerToOrders = customer.addToMany(order, customerId);

Let’s assume we have an order entity. Using both relations, we could get the customer and all orders the customer has ever made:

List allOrdersOfCustomer = order.getCustomer().getOrders();

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:

Entity treeEntity = schema.addEntity("TreeEntity");
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:

TreeEntity parent = child.getParent();
List grandChildren = child.getChildren();

More examples

Check out the example project for a complete code example. Check out the ExampleDaoGenerator and the CustomerOrderTest classes.

Also, the DaoGenerator and DaoTest internal sub projects come with several relation tests, which may serve as further examples.

Spread the love