Android Room Persistence Library: Relations in a Nested One-To-Many Relationship.
Relation is a convenience annotation which can be used in a Pojo to automatically fetch relation entities. When the Pojo is returned from a query, all of its relations are also fetched by Room.
There are tons of resources on Room persistence library, you might want to check them out.
The scenario we would explore in this article is that of a hypothetical publishing company that has several authors whose books they publish. Furthermore, these Authors may have one or more Books published by the publishing firm.
The above relationship can be best described as a nested one-to-many relationship. That is, the Publisher entity has a one-to-many relationship with the Author entity which also maintains a one-to-many relationship with the Book entity.
Thankfully, we can model this relationship using Room persistence library which is part of the android architecture components. Using Room, we can have three entities and then specify the relationships using foreign keys.
The complexity arises when we try to query for the Publisher entity and then expect to get all of its relation entities i.e. its list of Authors, as well as the list of Books for each author.
How then do we solve this complexity?
The first approach that comes to mind is to query for the publisher entity, get its id and then go to the Author entity and query for all authors using the publisher id. Also, for every author, we’d need to also use the author id to query for all books with an author id. Heck! You can see the level of complexity using this approach as the queries and loops might have a performance overhead.
Relation to the Rescue
The second and by far a more efficient approach is to use Relation annotation to query for the entity relationship.
For our use case, we need to create two Pojos called PublisherDetails and AuthorBookDetails in which all fields are fetched from the
entity defined in the
@Relationannotation can be used only in Pojo classes, an
Entityclass cannot have relations.
Notice how we’ve used the @Embedded annotation for both the Publisher and Author entities to signal that there are nested fields and as such Room will properly construct the Publisher and Author class.
Also notice, how the
entity are used.
Let’s understand their meanings:
- entity : This is the entity to fetch from. For the PublisherDetails pojo, this is the Author entity while for the AuthorBookDetails pojo, it is the Book entity.
- parentColumn: This is a reference field in the parent pojo, say the PublisherDetails or AuthorBookDetails pojo. This is particularly useful when you want to access a sub-item of an embedded field. For instance, in the PublisherDetails pojo, notice that the Publisher entity is Embedded. So setting the parentColumn to id means we want to access the id of the embedded Publisher entity i.e.
publisher.id. Same explanation holds for the AuthorBookDetails pojo.
- entityColumn: This is the field path to match in the entity. This value will be matched against the value defined in parentColumn. For the
PublisherDetailspojo the entityColumn references the
publisherIdof the Author entity. Recall that the Author entity has a relationship with the
Publisherentity via the
publisherIdforeign key. So, what happens here is that the id of the embedded
Publisherentity referenced by the parentColumn (
publisher.id) is matched with the
publisherId(foreign key) on the
Authorentity as specified by the entity field. By doing this a relationship is established between the
Authorentity and the results are returned to the List as specified in the relation. Same explanation holds for the
Next is to write a dao query method to fetch the
Publisher entity, but this time the return object would be the
PublisherDetails pojo (by default, inferred from the the return type).
If we observe the LiveData returned from the query above, we’d get a list of PublisherDetails object. Calling a
toString() on the list gives the following:
From the above, we can see that the object relationship is not what we want. That is, the PublisherDetails pojo contains a publisher object with authors as null while the AuthorBookDetails pojo contains an author object with books as null.
So we need a way to get the single object relationship we require. For this we need to create constructors in the Publisher and Author entities which would handle the mapping of the PublisherDetails to the Publisher and Author entities respectively. This would eliminate null values and build a single object relationships between the entities.
Add the following to the Publisher entity:
Using the constructor above, we can use Java 8 stream functionality to create our desired object structure like so:
From the above, we can now get a single Publisher object that contains a list of authors as well as list of books for each authors. A
toString() on the the list of publishers would yield:
You can't perform that action at this time. You signed in with another tab or window. You signed out in another tab or…
Do well to drop your comments, feedback or a better approach for this kind of problem.