Android Room Persistence Library: Entity, Dao, Database

In February 2018 I was happy to be one of the speakers at MobOS in Cluj-Napoca. It was a nice experience, I received a lot of questions and I was impressed about the big number of the Android developers. My presentation was about Room so in this article I will cover a part of the concepts included in my slides.

android-room-componets.png

Handling an SQLite database in Android implies some challenges like:

  • Boilerplate code
  • SQL queries checked at runtime
  • Database operations on the main thread
  • Unmaintainable code
  • Untestable code

At Google I/O 2017, the Android team lunched Room Persistence Library, an SQLite object mapper as part of the Architecture Components.

What we’re providing is a set of guidelines that can help you architect an Android application to work best with the unique ways that Android interacts.  (Android and Architecture)

Dependencies:

In order to start our journey using Room first of all we will need to define the dependencies in our Gradle files:

allprojects {
    repositories {
        jcenter()
        google()
    }
}

implementation 
"android.arch.persistence.room:runtime:1.1.0-alpha1"
annotationProcessor 
"android.arch.persistence.room:compiler:1.1.0-alpha1"

To use the latest version of Room check here the updates and also an important thing to mention is that Room works on the apps that have as minSDK API level 14.

Room contains 3 main components that help us, the developers, to have a better experience when using a database in our app:

  1. Entity
  2. Dao
  3. Database

1. Entity

@Entity(tableName = "Company")
public class Company {

   @PrimaryKey(autoGenerate = true)
   @ColumnInfo(name = "id")
   private int companyId;

   @ColumnInfo(name = "name")
   private String name;

   @Ignore   
   Bitmap picture;

@Entity – by using this annotation, Room will know that this class will be a table in the database and the tableName will define the name of the table

1.1 @PrimaryKey – is used to set up the primary key of the table. We could have also a primary key that is composed by many columns and in this case the annotation will be used on the top of the class, inside of the @Entity

1.2 @ColumnInfo – defines the name of the column from the table if we don’t want to use the name of that field

1.3 @Ignore – the field or the constructor that has this annotation will be ignored by Room, so it will not be used. Also if a field is transient, it is ignored unless it is annotated with @ColumnInfo, @Embedded or @Relation

1.4 @TypeConverter – give us the possibility to “translate” a custom type from Java/Kotlin to SQLite.

public class DateConverter {
   @TypeConverter
   public static Date toDate(Long timestamp) {
       return timestamp == null ? null : new Date(timestamp);
   }

   @TypeConverter
   public static Long toTimestamp(Date date) {
       return date == null ? null : date.getTime();
   }
}

Depending on the scenario. this @TypeConverter could be defined at different levels:

  • Field level
@ColumnInfo(name = "date_updated")
@TypeConverters(DateConverter.class)
private Date itemUpdatedDate;
  • Dao level
@Dao
@TypeConverters(DateConverter.class)
public interface CompanyDao 
  • Database level
@Database(entities = {Company.class}, version = 1)
@TypeConverters(DateConverter.class)
public abstract class AppDatabase extends RoomDatabase 

2. Dao

@Dao
public interface CompanyDao {
   @Query("SELECT * FROM Company")
   List getAllCompanies();

   @Insert
   void insertCompany(Company company);

   @Update
   void updateCompany(Company company);

   @Delete
   void deleteCompany(Company company);
}

@Dao – by using this annotation Room knows that it should provide the implementation for our interface by generating code for the defined methods. In this interface we could define the CRUD operations for our entity and also any other operations that are necessary in order to obtain the data.

Also Room offers us query validation at compilation time, so if we will write incorrectly the name of a table or a field we will know if after the code will be compiled.

For the next code:

@Dao
public interface DepartmentDao {
   @Insert
   void insertAll(List departments);
}

Room will generate the next implementation:

public class DepartmentDao_Impl implements DepartmentDao {
   private final RoomDatabase __db;

   public void insertAll(List departments) {
   this.__db.beginTransaction();
   try {
       this.__insertionAdapterOfDepartment.insert(departments);
       this.__db.setTransactionSuccessful();
   } finally {
       this.__db.endTransaction();
   }
}
}

3. Database

The @Database component combines the entities and the dao interfaces.

@Database(entities = {Company.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {   
   private static AppDatabase INSTANCE;

   public abstract CompanyDao companyDao();
   public static AppDatabase getAppDatabase(Context context) {
       if (INSTANCE == null) {
           INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
                      AppDatabase.class, 
                      "company-db")                           
                      .build();
       }
       return INSTANCE;
   }

Inside of the @Database annotation we should define the list of the entities we want to be saved in the database and also the version of the current database. This number will help us to make the migrations in Room.

At this level we are using an abstract class that extends RoomDatabase. Inside of it we define the abstract methods in order to have references to our Dao classes and also we should create an instance of the database. In this case the instance is created using the Singleton pattern and the way to generate it is similar to the Retrofit builder.

We have 2 possibilities to build the database by using:

  • databaseBuilder that creates a RoomDatabase.Builder for a persistent database
  • inMemoryDatabaseBuilder that creates a RoomDatabase.Builder for a in-memory database, if we don’t want to persist the data

Enjoy! Happy coding and feel free to leave a comment if something is not clear or if you have questions. 🙂

P.S.: I will create a set of articles about Room in the future. Until then you could check the introduction about this library.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s