Relations
Entity relations are declared using wrapper types. Learn how to form to-one and to-many relations with ObjectBox here.
Last updated
Entity relations are declared using wrapper types. Learn how to form to-one and to-many relations with ObjectBox here.
Last updated
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.
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:
Given these entities and their to-one relation, you can create a relation and persist it:
You can persist whole trees of object relations at once: If the customer object does not yet exist in the database, the ToOne
will put()
it. If it already exists, the ToOne
will only create the relation (but not put()
it).
For a comprehensive documentation of its features, visit the ToOne API docs.
You have to set the target of a relation to nil
and persist the changed object(s) via Box.put()
to remove a relation permanently. Which one you reset doesn't matter, though. You have these options:
Removing a relation never removes participating objects.
ToOne
is a Lazy Relation ProxyThe target object of a relation is not eagerly loaded from the store; it is loaded lazily. Until you request the ToOne.target
, it will not be read into main memory.
To define a to-many relation, you can use a property of type ToMany
. Like the ToOne class, the ToMany
class helps you to keep track of changes and to apply them to the database.
Note that to-many relations are resolved lazily on first access, and then cached in the source entity inside the ToMany
object. So subsequent calls to any method, like the count
of the ToMany
, do not query the database, even if the relation was changed elsewhere. To avoid the cache and trigger a fresh reload from the database, call reset()
on the ToMany
.
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.
For every ToOne
relation that you have, you can define a backlink. Backlinks are using the same relation information, but in the reverse direction. Thus, a backlink of a ToOne
will result in a list of potentially multiple objects: all entities pointing to the same entity.
Example: Two Order objects point to the same Customer using a ToOne
. The backlink is a ToMany
from the Customer referencing its two Order objects.
Let's extend the example from above to get the backlinks from Customer
to Order
:
Note you tell ObjectBox about your backlink using an // objectbox: backlink = "name"
annotation (where name is the name of the ToOne property that makes up the other end of the relation).
Once the backlink is set up, you can traverse the relation in both directions:
In database terminology, you create a (bi-directional) one-to-many (1:N) relationship by defining a ToOne with a backlink ToMany.
ToMany
ToMany
conforms to Swift's RandomAccessCollection
protocol. Thus, you can use it just like an array or similar collections and pass it around. And of course, you can create an Array if need be:
See also: ToMany API docs
Apart from being a RandomAccessCollection, a ToMany
relation is also a RangeReplaceableCollection
. That means you can use append(_:)
etc. on it just like on an Array
to modify it. We've also added a replace(_:)
method as a convenience to replace all entities referenced by the relation.
Once you've performed all the modifications you want, call applyToDb()
on the ToMany
to actually cause them to be written to the database. Note that ToMany
applies change tracking and thus only writes updated relations to the database.
When you change its contents, ToMany
will simply set the ToOne
relation in the removed entities to nil
, and will make added entities' ToOne
point at the object containing the ToMany
backlink. Note that, starting from version 1.4, you can add new (not yet persisted) objects, which applyToDb()
will put automatically :
Also, modifying a ToMany
backlink modifies the ToOne
of the referenced entities (in this example, newOrder
and oldOrder
) and will put()
those objects to actually write out the changed relation. To remove all references to an entity, you may pass an empty array to replace()
:
Removing a relation never removes the referenced objects from the database.
To define a many-to-many relation you simply add a property using the ToMany
class. Assuming a students and teachers example, this is how a simple Student
class that has a to-many relation to Teacher
entities can look like:
Adding the teachers of a student works exactly like with an array, or a one-to-many relation:
To get the teachers of a student we just access the list:
And if a student drops out of a class, we can remove a teacher:
Removing a relation never removes the referenced objects from the database.
Following the above example, you might want an easy way to find out what students a teacher has. Instead of having to perform a query, you can just add a to-many relation to the teacher and annotate it with the // objectbox: backlink
annotation:
This will tell ObjectBox that there is only one relation, teachers
, and that students
is just a reverse-lookup of this relation. In any other respect, a many-to-many backlink can be used just like its forward counterpart.
You can traverse relations in queries.
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: