How to store objects in Android Room?

ildar ishalin picture ildar ishalin · Jun 24, 2017 · Viewed 13k times · Source

Basically, there are two things I don't understand: objects with objects and objects with lists of objects

Say I receive a list of objects from the server. Each of them looks like this:

@Entity
public class BigObject {
    @PrimaryKey
    private int id;
    private User user;
    private List<SmallObject> smallObjects;
}

with these two objects as fields:

@Entity
public class User {
    @PrimaryKey
    private int id;
    private String name;
    @TypeConverters(GenderConverter.class)
    public MyEnums.Gender gender;
}

@Entity
public class SmallObject {
    @PrimaryKey (autoGenerate = true)
    private int id;
    private String smallValue;
}

They are more complicated than this, so I can't use @TypeConverters as Room suggests:

error: Cannot figure out how to save this field into database. You can consider adding a type converter for it.

How do I store this data structure in Room?

Answer

Jack Dalton picture Jack Dalton · Jul 3, 2017

I think the best way to answer this is a breif overview in storing structures...

Lists

Room does not support storing lists that are nested inside of a POJO. The recommended way to store lists is to use the foreign key approach. Store the List of objects in a seperate table (in this case a smallObjects table) with a foreign key to their related parent object (in this case "big_object_id"). It should look something like this...

@Entity
public class BigObject {
    @PrimaryKey
    private int id;
    private User user;
    @Ignore
    private List<SmallObject> smallObjects;
}

@Entity(foreignKeys = {
            @ForeignKey(
                entity = BigObject.class,
                parentColumns = "id",
                childColumns = "big_object_fk"
            )})
public class SmallObject {
    @PrimaryKey (autoGenerate = true)
    private int id;
    private String smallValue;
    @ColumnInfo(name = "big_object_fk")
    private int bigObjectIdFk
}

Note that we have added the @Ignore annotaiton to List<SmallObject> as we want to ignore the field during Room persistance (as lists are not supported). It now exists so that when we request our list of related small objects from the DB we can still store them in the POJO.

To my knowledge this will mean you are making two queries.

BigObject b = db.BigObjectDao.findById(bOId);
List<SmallObject> s = db.smallObjectDao.findAllSOforBO(bOId);
b.setsmallObjects(s);

It appears that there is a short hand for this in the form of @Relation

Type Converters

These are for cases where you have a complex data structure that can be flattend without losing information, and stored in a single column. A good example of this is the Date object. A Date object is complex and holds a lot of values, so storing it in the database is tricky. We use a type converter to extract the milli representation of a date object and store that. We then convert the millis to a date object on the way out thus keeping our data intact.

Embedded

This is used when you want to take the fields of all nested POJOs in your parent POJO and flatten them out to store in one table. an example :

- name
- age
- location
    - x 
    - y
- DOB

..when embedded this structure would be stored in the database as :

- name 
- age 
- location_x
- location_y
- DOB

In a sense Embedded exists to save you time creating type converters for every nested object that contains primary type fields like String, int, float, etc...