Using @Relation

If you have a POJO class — one that does not directly have the @Entity annotation — you can use @Relation to automatically retrieve entities related to… something in the POJO.

For example, in other Android ORMs, one might expect that Category would have methods, fields, or something to get at the parent Category (where there is one) or the child Category instances (where there are some). However, that is not supported by Room and @Entity, but it is supported by separate POJO classes.

To that end, we can set up a CategoryTuple:

package com.commonsware.android.room.dao;

import android.arch.persistence.room.Relation;
import java.util.List;

public class CategoryTuple {
  public final String id;
  public final String title;
  public final String parentId;

  public CategoryTuple(String id, String title, String parentId) {
    this.id=id;
    this.title=title;
    this.parentId=parentId;
  }

  @Relation(parentColumn="id", entityColumn="parentId")
  public List<Category> children;

  @Relation(parentColumn="parentId", entityColumn="id")
  public List<Category> parents;
}

Here, we have two @Relation annotations. These go on fields, not methods, and they indicate fields that Room should fill in when a @Query returns instances of this POJO. The field type needs to be a List or Set of the related entity, not the POJO. Hence, children and parents are lists of Category instances, not CategoryTuple.

The two required properties on @Relation are parentColumn and entityColumn. entityColumn is the name of a column in the entity’s table; parentColumn is the name of a field in the POJO representing the parent entity. In this case, the entity for both is Category, as we are working with a self-referential relation. In the generated code, Room is going to run a query that finds all objects whose entityColumn has the value pulled from this POJO’s parentColumn field. More specifically:

For a 1:N relation, Room’s restriction on @Relation data types (must be List or Set) means that both the 1 side and the N side get represented by collection fields… even though one should only ever have at most one element.

If there are no matching entities (e.g., no parent for the root Category, no children for a leaf Category), the resulting field is either null or an empty collection.

But now, our DAO methods will not only set up the POJOs but all entities that are called for by the @Relation fields:

  @Transaction
  @Query("SELECT * FROM categories WHERE parentId IS NULL")
  CategoryTuple findRootCategoryTuple();

  @Transaction
  @Query("SELECT * FROM categories WHERE parentId=:parentId")
  List<CategoryTuple> findChildCategoryTuples(String parentId);

However, this involved a lot of copying. CategoryTuple has the same fields as Category. It would not have to have all of those fields, of course, as a POJO need not have fields for all columns in the table. But, still, it seems to be a bit wasteful.

Another related approach is to create a “POJO” subclass of the entity… such as this CategoryShadow:

package com.commonsware.android.room.dao;

import android.arch.persistence.room.Relation;
import java.util.List;

public class CategoryShadow extends Category {
  public CategoryShadow(String id, String title, String parentId) {
    super(id, title, parentId);
  }

  @Relation(parentColumn="id", entityColumn="parentId")
  public List<Category> children;
}

Even though CategoryShadow inherits from Category, and even though Category is an entity, Room treats CategoryShadow as a POJO, and we can have @Relation fields, such as the children one shown. If you need most or all of the fields from the entity, this subclass approach involves less code duplication than does the standalone-POJO approach.


Prev Table of Contents Next

This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.