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 the
children
field, Room will query thecategories
table to return all rows where theparentId
column equals theid
of thisCategoryTuple
- For the
parent
field, Room will query thecategories
table to return all rows where theid
column equals theparentId
of thisCategoryTuple
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.