Android Room Persistence Library: Relations in a Nested One-To-Many Relationship.

Ikhiloya Imokhai
The Startup
Published in
5 min readOct 24, 2019
source: Android Developers Blog

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.

The type of the field annotated with Relation must be a List or Set. By default, the Entity type is inferred from the return type.

There are tons of resources on Room persistence library, you might want to check them out.

Problem Statement

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 Relation annotation.

Note that @Relation annotation can be used only in Pojo classes, an Entity class 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 parentColumn , entityColumn and entity are used.

Let’s understand their meanings:

  1. 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.
  2. 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.
  3. entityColumn: This is the field path to match in the entity. This value will be matched against the value defined in parentColumn. For the PublisherDetails pojo the entityColumn references the publisherId of the Author entity. Recall that the Author entity has a relationship with the Publisher entity via the publisherId foreign key. So, what happens here is that the id of the embedded Publisher entity referenced by the parentColumn (publisher.id) is matched with the publisherId(foreign key) on the Author entity as specified by the entity field. By doing this a relationship is established between the Publisher and Author entity and the results are returned to the List as specified in the relation. Same explanation holds for the AuthorBookDetails pojo.

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:

toString() representation of PublisherDetails pojo

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:

toString() representation of Publisher entity

So that’s it. By using Relation annotation on pojos, you can automatically fetch relation entities. You can check out the code here.

Also you can check this article by Reza Bigdeli for another approach using Relation with LiveData.

Do well to drop your comments, feedback or a better approach for this kind of problem.

Thanks.

References

  1. Room Persistence Library
  2. Relation
  3. Android Room Persistence Library: Relations by Magda Miu
  4. Android Room: Handling Relations Using LiveData by Reza Bigdeli

--

--

Ikhiloya Imokhai
The Startup

Software Developer | imokhaiikhiloya@gmail.com | https://github.com/Ikhiloya | Reach out to me for Android/Java/Technical writing projects.