ObjectBox Relations

Prefer to dive right into code? Check the relation project in the example repo.

Objects may reference other objects, for example using a simple reference or a list of objects. In database terms, we call those references relations. The object defining the relation we call the source object, the referenced object we call target object. So the relation has a direction.

If there is one target object, we call the relation to-one. And if there can be multiple target objects, we call it to-many. Relations are lazily initialized: the actual target objects are fetched from the database when they are first accessed. Once the target objects are fetched, they are cached for further accesses.

To-One Relations

You define a to-one relation using the ToOne class, a smart proxy to the target object. It gets and caches the target object transparently. For example, an order is typically made by one customer. Thus, we could model the  Order class to have a to-one relation to the Customer like this:

To get the actual customer object, call getTarget() on the ToOne instance. This will do a database call on the first access (lazy loading). It uses look-up by ID, which is very fast in ObjectBox. If you only need the ID instead of the whole target object, call getTargetId() instead. It can be more efficient because it does not touch the database at all.

To set the related customer object, call setTarget()  on the ToOne instance and put the order object:

If the customer object does not yet exist in the database, the ToOne will insert it. See further below for details about updating relations.

Initialization Magic

Did you notice that the ToOne field customer  was never initialized in the code example above? Why can the code still use customer without any NullPointerException? Because the field actually is initialized – the initialization code just is not visible in your sources.

The ObjectBox Gradle plugin will transform your entity class to do the proper initialization in constructors before your code is executed. Thus, even in your constructor code, you can just assume ToOne  and ToMany/ List  properties have been initialized and are ready for you to use.

To-Many Relations

To define a to-many relation, you can use a property of type List or the ToMany  class. As the ToOne class, the ToMany  class helps you to keep track of changes and to apply them to the database. If you do not need or want that, use type List  and take care of applying database changes yourself.

Note that to-many relations are resolved lazily on the first request, and then cached in the source entity inside the ToMany object. So subsequent calls to the get method of the relation do not query the database.

There is a slight difference if you require a one-to-many (1:N) or many-to-many (N:M) relation. A 1:N relation is like the example above where a customer can have multiple orders, but an order is only associated with a single customer. An example for an N:M relation are students and teachers: students can have classes by several teachers but a teacher can also instruct several students.

One-to-Many (1:N)

To define a one-to-many relation, you need to annotate your relation property with @Backlink. It links back to a to-one relation in the target object. Using the customer and orders example and assuming we use the Order class from above (with the to-one relation to a customer), we can add a customer class that has a to-many relation to the customers orders:

Note: If there are multiple to-one relations matching the entity type in your target entity, you need to specify the name of the to-one relation. For example: @Backlink(to = "customerId") .

Many-to-Many (N:M)

To define a many-to-many relation you simply add a property using the ToMany  class. Assuming our students and teachers example from above and that Teacher  is an entity as well, this is how a simple student class that has a to-many relation to teachers can look like:

Updating Relations

The ToOne and ToMany classes assist you to persist the relation state. They keep track of changes and apply them to the database once you put the entity owning the relation. ObjectBox supports relation updates for new (not yet persisted; ID is zero) and existing (persisted before; ID is non-zero) entities.

Updating ToOne

The ToOne class offers the following methods to update the relation:

  • setTarget(entity) makes the given entity (new or existing) the new relation target; pass null  to clear the relation
  • setTargetId(entityId) sets the relation to the given ID of an existing target entity; pass 0 (zero) to clear the relation

Updating ToMany

The ToMany class implements the java.lang.List  interface while adding change tracking for entities. If you add entities to an ToMany object, those are scheduled to be put in the database. Similarily, if you remove entities form the ToMany object, those are also scheduled to be put. Note that removing entities from the List does not actually remove the entity from the database; just the relation is cleared. Do not forget to put the owning entity to apply changes tracked by ToMany objects to the database.

Note: if you have set @Id(assignable = true)  and are setting an ID to a not yet put source entity, adding entities to or removing entities from the ToMany will currently fail. As a workaround you can attach the box to the entity before modifying ToManys by calling for example customerBox.attach(customer)  (before line 1 in the example above).

Example: Modelling Tree Relations

You can model a tree relation with a to-one and a to-many relation pointing to itself:

The generated entity lets you navigate its parent and children:

Spread the love