Preface

In this article we will dive deep into Room component. In case you missed the previous articles or don’t know about architecture components then head on to my previous article Introduction to Android Architecture Components.

The code in this article is written in Kotlin. In case you don’t know Kotlin. Read this series of article to get started.

Introduction

Room is a persistence library designed to help you save your app data into the SQLite database which ships with every Android version.

Room is a robust SQL mapping library

Features

  • It provides local data persistence
  • Abstraction layer over existing SQLite database
  • Checks SQL query at compile time
  • Ability to observe for changes in the database using LiveData
  • Eliminates the boilerplate code
  • Plays well with RxJava

Persistence Spectrum

The spectrum shows some of the persistence libraries and where they fall in between SQL and Objects. The Room library falls in between SQL and Objects that’s because you need to write SQL.

Persistence Spectrum

Anatomy of Room

Room Architecture Diagram

Database

  • It defines the database holder
  • It is main access point to the database connection
  • It defines list of entities and database access objects (DAO)

Database Access Objects (DAO)

  • They are main component of Room because they are responsible for defining methods that access database
  • It defines the methods for interaction with the database
  • The implementation is auto-generated at compile-time

Entity

  • For each entity a database table is created
  • It represents a class which holds a database row

Adding it to your project

Add the following dependencies in your app or module build.gradle

compile "android.arch.persistence.room:runtime:1.0.0-rc1"// Java 
annotationProcessor "android.arch.persistence.room:compiler:1.0.0-rc1"
// Kotlin
kapt "android.arch.persistence.room:compiler:1.0.0-rc1"

PS: Release Candidate 1 (RC1) is the latest version of Architecture Components (except Paging) at the moment of writing this article.

Let’s code with Room!

Entity

The Entity class is marked with @Entity annotation. It defines the structure of the column.

User class with @Entity annotation

The above entity creates a table named users with 2 columns namely id — auto-generated primary key and name.

By default,

  1. Table name is same as class name; you can change it using tableName property in @Entity.
  2. A column is created for each field and same name that’s defined in the entity; you can change the column name using @ColumnInfo(name = "ANYTHING") annotation.
User entity with column info

Primary Key

It is required for every entity to have primary key. You can even have composite primary keys:

User entity with composite primary keys

Ignore column

In order to tell Room to ignore a particular field you need to annotate it with @Ignore

Since beta1 release, Room ignores transient fields by default unless they are annotated with @ColumnInfo, @Embedded or @Relation

User entity with ignore

PS: The table names in SQLite are case-insensitive

Database Access Objects (DAO)

The DAO is the one who talks with the database to perform CRUD operations based upon functions defined in the DAO. It can be either interface or abstract class as the implementation is written by Room.

DAO for user entity

I’m pretty sure it’s understandable except @Query annotation. During the Persistence spectrum we saw Room is in between SQL and Objects because it supports SQL. Well @Query is the place where you’ll mostly need to write SQL.

The SQL query is checked at the compile time itself so if you made a typo or the query is wrong then you’ll get a compile time error. You can even query multiple tables.

Observable queries

Usually we want to change the state of UI when data in the database is updated. Room allows that by providing observable queries which will notify when the data in the database is changed/updated. It is achieved using LiveData.

Query with LiveData

Room supports Flowable and Publisher from RxJava:

Missed my previous article about LiveData or need to know more then check the article below:

Database

The last component is database; it brings all the pieces of Room together viz; all the entities and their DAOs.

The version indicates the schema version. It needs to be incremented when the database schema is changed/altered.

Database

Getting the database instance:

Getting database instance

The database instance is expensive. It is recommended to keep it in a singleton maybe by using Dagger 2

Ideally, we need to tell Room when the database version is changed and provide a guide or steps to perform migration from old version to new version.

To find out more about how to implement database migrations and how they work under the hood, check out this post:

Relations

Object Relationships are a bit tricky in Room. It’s one of the thing which needs little improvement, nonetheless let’s dive into it.

Unlike other object-relational mapping libraries, Room doesn’t allow entities directly referencing each other due to lazy loading problem. That said, it allows relations using ForeignKey which is quite powerful but tricky.

Nested Objects

Sometimes we would want to decompose our object into sub-fields but keep them under one logical unit.

In layman terms, let’s say user is your entity object which has address which contains it’s own fields like city, and you want to store it under the same table called users because logically address belongs to a user.

Let’s look at code because we understand that better ;)

Users entity with embedded address
Address POJO

Note: There is no @Entity on top of Address class because we don’t want to create a separate address table in the database. Moreover, as mentioned earlier Room doesn’t support nested entity.

Schema of users table

This is how the table will be created in the database. The fields from Address class will be automatically embedded by Room.

Foreign Key

Let’s look at same example of user and address using ForeignKey — one to one relation.

In, the user entity we mention it’s foreign key relation with the Address entity by specifying the foreign key of User to primary key of Address .

User entity with foreign key

We define the same Address class only this time with @Entity annonation which needs PrimaryKey.

Address entity

Database schema

The entities which we defined above will be stored in this fashion in the database.

One to one relation between user and address

Now the important question arises, how do I fetch the data? We need to create an extra POJO to define what we need. This is done to tackle the lazy loading issue.

We define a new POJO to fetch user object and city name from addresses table.

UserWithAddress

PS: You can define POJO to fetch other things of addresses table or all of the fields. It depends upon what you need

Finally we need to write SQL query in the DAO.

SQL query for users and city (address)

And that’s how it’s done! I know it’s slightly tricky. It gets more tricky with one to many.

For more details check Relation and One to Many with Room.

Testing

I’ll be writing a series of articles soon; showcasing testing for each architecture components which includes Room.

Meanwhile, if your curious and can’t wait then you can read about testing Room database from official docs.

Pro-tips

  • Use inMemoryDatabaseBuilder() while developing/prototyping or testing your application. It clears the database on closing the connection to the database.
  • Use fallbackToDestructiveMigrations() if you are really sure about it. The good part is you don’t need to write migrations. The bad part is data will be purged from the database every time you increment the database version.
  • Use Repository pattern to sync the data between Room and Web (server).

Conclusion

I love the fact that Room is here to save us from the persistence problem and it supports reactive nature and all it’s goodies. I don’t like that we need to write the migration code that too in SQL. I wish it was automatic or semi-automatic; another thing is working with relation is tricky with Room which I think will get easier after v1.0.

Lastly, I’m glad that it uses SQLite database which is available on every Android device; preventing additional size indirectly helping us build slim APKs.

Room is like Retrofit for database

Update: What’s next?

In the next part we will look at RxJava, transactions Room and few more small things as pointed out by Andrei.

--

--

Akshay Chordiya
AndroidPub

Google Developer Expert @ Android | Android Engineer @ Clue | Instructor @Caster.IO