Room: One to Many

Hello. Outside of 2018 and for almost a year now, Google has been actively working on Architecture Components . Good documentation and examples allow you to start using new components without any problems and difficulties. But there is always a fly in the ointment in the barrel of code honey . The notes below do not claim to be true, but they may save two or three hours of googling and viewing the library code.

One to many


Everything is simple here: the documentation says how to organize classes in order to get a one-to-many relationship between entities. We take 2 entities:

@Entity
public class DialogPojo {
    @NonNull
    @PrimaryKey
    String id;
//other fields
}
@Entity(
        foreignKeys = @ForeignKey(entity = DialogPojo.class,
                parentColumns = "id",
                childColumns = "dialogId",
                onDelete = ForeignKey.CASCADE),
        primaryKeys = {"dialogId", "tag"})
public class TagPojo {
    @NonNull
    String dialogId;
    @NonNull
    String tag;
//other fields
}

and bind them into a single entity:

public class DialogWithTags {
    @Embedded
    public DialogPojo dialog;
    @Relation(parentColumn = "id", entity = TagPojo.class, entityColumn = "dialogId")
    public List tags;
//other fields
}

To get a new entity of type DialogWithTags, you need to use Dao, which will load data from the DialogPojo table, while tags from the linked table (entity = TagPojo.class) are automatically loaded:

@Dao
public interface DialogDao {
    @Query("SELECT * FROM DialogPojo WHERE id = :dialogId")
    LiveData loadDialogBy(String dialogId);
    @Query("SELECT * FROM DialogPojo WHERE id = :dialogId")
    DialogWithTags getDialogBy(String dialogId);
}

Using this knowledge it is already possible to collect the death star application. However, in the process of work, questions may arise, the answers to which are best known at the preparatory stages.

Integrity


Oddly enough, but a request for DialogWithTags does not guarantee data integrity. That is, it is possible that DialogPojo is already loaded, but the TagPojo list is not. A warning about possible problems appears at the compilation stage of the program. And who reads these warnings? To ensure data integrity, you must add the Transaction annotation to the request.

@Dao
public interface DialogDao {
    @Transaction @Query("SELECT * FROM DialogPojo WHERE id = :dialogId")
    LiveData loadDialogBy(String dialogId);
    @Transaction @Query("SELECT * FROM DialogPojo WHERE id = :dialogId")
    DialogWithTags getDialogBy(String dialogId);
}

Preservation


Unfortunately, saving the DialogWithTags model just won't work. It is necessary to save the data separately and, preferably, in one transaction, for example:

@Dao
public abstract class DialogDao {
    @Transaction
    public void insert(DialogWithTags dialogWithTags) {
        insert(dialogWithTags.dialog);
        for(TagPojo tag: dialogWithTags.tags) {
            insert(tag);
        }
    }
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    public abstract long insert(DialogPojo dialog);
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    public abstract long insert(TagPojo tag);
    ....
}

Live data


The biggest disappointment awaits when using LiveData. Data will be live only for the Embedded dialog field. Changes to tags will not be tracked. Of course, you can declare the tags field as LiveData, but do not forget that LiveData will only return data if at least one observer is registered.

Conclusion


Like any framework, Architecture Components solves a number of problems, saves developers from boilerplate code and makes life more beautiful , but also adds its own problems. In my case, the framework has successfully implemented the project and feels great there.

Also popular now: