Android is changing the way developers code Android applications. As of now core android coding framework provides built-in support to work with raw SQL data content. With the introduction of Room Persistent Library for SQLite, the Android application development community will be revolutionized.

What is Room Persistent Library?

A Room Persistent Library is a member of Android Architecture Component which was introduced in Google I/O 2017 to harness the raw power of SQLite databases. A Room provides an abstraction layer over the SQLite database that helps in creation and usage of SQLite database, easily and conveniently.

Why Room Persistent Library?

There are umpteen number of reasons to choose Room Persistent Library like time and cost savings including faster fetching of data. But other more important ones are as described below.

  • Room Persistent library reduces the amount of Boilerplate code to convert between SQL queries and Java data objects. Which can save your time!
  • Room database checks SQL statements at the compile time while querying for data and live monitoring for the changes using LiveData
  • In Room database, all DML commands are annotated except SELECT command which works with @Query.
  • Room has enhanced security, easier to access and set up and is quick to start with new database!

Components in RPL

There are 3 major Components in Room Persistent Library setting as listed below.

#1.Database
Database is a holder class that uses the characters and annotation to define an abstract database class that extends RoomDatabase. The annotation defines the list of entities, and the class’s content defines the list of data access objects (DAOs) in the database.

#2.Entity
Entity is a Model class annotated with @Entity. For each entity, a database table is created to hold the items and all the variable will become column name for the table and name of the model class becomes name of the table.

#3.Data Access Object (DAO)
DAOs are the main component of the Room annotated with @Database and it represents the interface as a Data Access Object which defines the methods that access the database. DAOs must contain an abstract method that has 0 arguments and returns the class that is annotated with @Dao. It also helps to validate the SQL statement that runs.

Room Architecture Diagram

Room-Architecture-Diagram

Let’s get started

Add Google’s maven repository to your project-level build.gradle file
Code Snippet:


allprojects {
repositories {
jcenter()
maven { url 'https://maven.google.com' }
}
}

Add the following dependencies for Room in your app-level build.gradle file

  • Compile “android.arch.lifecycle:extensions:1.0.0-alpha1”
  • Annotation Processor “android.arch.persistence.room:compiler:1.0.0-alpha1”

Let’s try some of the useful annotations used in Entity class

Primary Key and Ignore


@PrimaryKey(autoGenerate = true)
private int id

Each entity must define at least 1 field as a primary key. To auto-increment the id which is your primary key, you need to set the autoGenerate to true. If the entity has a composite primary key, you can use the primaryKeys property of the @Entity annotation as shown below:

Code Snippet:


@Entity(tableName = "user", primaryKeys = {"firstName", "lastName"})
public class User {
 
private String firstName;
private String lastName;
@Ignore
private String address;
}
</pre></div>
</div> <!-- .et_pb_code --><div class="et_pb_module et_pb_text et_pb_text_32  et_pb_text_align_left et_pb_bg_layout_light">
<div class="et_pb_text_inner"><p>If an entity has fields that you don’t want to persist, you can annotate them using @Ignore.</p>
<p>Foreign Key<br /> Here, for entity Book, you can define its relationship to another entity User by using @ForeignKey annotation. To delete all books for a user if the corresponding instance of User is deleted you can include onDelete=CASCADE in the <a href="https://developer.android.com/reference/android/arch/persistence/room/ForeignKey.html">@ForeignKey</a> annotation</p></div>
</div> <!-- .et_pb_text --><div class="et_pb_module et_pb_code et_pb_code_15">
<div class="et_pb_code_inner"><b>Code Snippet:</b> <br /><br />
<pre class="prettyprint">
<xmp>
@Entity(tableName = "book",
foreignKeys = {@ForeignKey(entity = User.class,
parentColumns = "id",
childColumns = "user_id", onDelete = CASCADE)})
public class Book {
 
@PrimaryKey(autoGenerate = true)
private int bookId;
@ColumnInfo(name = "user_id")
private int userId;
@ColumnInfo(name = "book_name")
private String bookName;
}

Indices and Uniqueness
To speed up your queries, you can index certain fields. For that you you need to include indices property within @Entity annotation listing the column names you want to include in the index or composite index. You can add the uniqueness property by setting the unique property to true within @Index annotation.

Code Snippet:


@Entity(indices = {@Index(value = {"firstName", "lastName"},
unique = true)})
class User {
@PrimaryKey
private int id;
private String firstName;
private String lastName;
}

Nested Objects
You can de-compose an object into its sub fields within a table using @Embedded annotation. In below example Book class can include field of type Book, which holds fields named name and price. Include a Book field in the Author class that is annotated with @Embedded to store the composed columns separately in the table. Embedded fields can also include other embedded fields.

Code Snippet:


class Book {
private String name;
private int price;
}
 
@Entity
class Author {
@PrimaryKey
private int id;
private String firstName;
@Embedded
private Book book;
}


If an entity has multiple embedded fields of the same type, set the prefix property to keep each column unique.

Sample DAO Class


@Dao
public interface UserDao {
 
@Query("SELECT * FROM user WHERE age > :minAge")
User[] getAllUsersOlderThan(int minAge);
 
@Insert(onConflict = REPLACE)
void insertUser(User... users);
 
@Insert
void insertUsersAndFriends(User user, List friends);
 
@Update
void updateUser(User... users);
 
@Delete
void deleteUser(User... user);
}

If the @Insert method receives only 1 parameter, it can return a long, which is the new rowId for the inserted item. If the parameter is an array or a collection, it should return long[] or List instead.

Sample Database Class


@Database(entities = {User.class}, version = 1)
public abstract class UserDatabase extends RoomDatabase {
private static UserDatabase userDatabase;
 
public static UserDatabase getDatabase(Context context) {
if (userDatabase == null) {
 
userDatabase = Room.databaseBuilder(context.getApplicationContext(), UserDatabase.class,
"user-database").build();
}
return userDatabase;
}
 
public static void destroyInstance() {
userDatabase = null;
}
 
public abstract UserDao userDao();<!-- [et_pb_line_break_holder] �-> 
public abstract BookDao bookDao();
}

Type Converters
If you want to use custom data types other than primitive data types whose value you want to store in the database in a single column, you can use Type Converter which converts a custom class to and from type that Room can persist.

In the following example if you want to persist instance of Date, we can use TypeConverter.

Code Snippet:


public class DateConverters {
@TypeConverter
public static Date fromTimestamp(Long value) {
return value == null ? null : new Date(value);
}
 
@TypeConverter
public static Long dateToTimestamp(Date date) {
return date == null ? null : date.getTime();
}
}

In the next step, you add the @TypeConverters annotation to the AppDatabase class so that Room can use the converter defined for each entity and DAO in that AppDatabase

Code Snippet:


@Database(entities = {User.class}, version = 1)
@TypeConverters({DateConverters.class})
 
public abstract class AppDatabase extends RoomDatabase {
public abstract UserDao userDao();
}

Now, you can use your custom types in other queries

Code Snippet:


@Dao
public interface UserDao {
...
@Query("SELECT * FROM user WHERE totalDays BETWEEN :from AND :to")
List getTotalWorkDaysBetweenDates(Date from, Date to);
}

Database migration

After you add or change the features in your app, you need to modify the entity class to reflect the changes. When you don’t want to loss all your existing data after updating your app to the latest version, Room allows you to write Migration classes to preserve user data in this manner. Each Migration Class has startVersion and endVersion. At runtime, Room runs each Migration class’s migrate() method, using the correct order to migrate the database to a later version.
Code Snippet:


Room.databaseBuilder(getApplicationContext(), UserDatabase.class,
"user-database").addMigrations(MIGRATION_1_2, MIGRATION_2_3).build();
 
private static final Migration MIGRATION_1_2 = new Migration(1, 2) {
@Override
public void migrate(SupportSQLiteDatabase database) {
database.execSQL("CREATE TABLE `Fruit` (`id` INTEGER, "
+ "`name` TEXT, PRIMARY KEY(`id`))");
}
};
 
private static final Migration MIGRATION_2_3 = new Migration(2, 3) {
@Override
public void migrate(SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE Book "
+ " ADD COLUMN pub_year INTEGER");
 
}
};
 
Room.databaseBuilder(getApplicationContext(), UserDatabase.class,
"user-database").addMigrations(MIGRATION_1_2, MIGRATION_2_3).build();
 
private static final Migration MIGRATION_1_2 = new Migration(1, 2) {
@Override
public void migrate(SupportSQLiteDatabase database) {
database.execSQL("CREATE TABLE `Fruit` (`id` INTEGER, "
+ "`name` TEXT, PRIMARY KEY(`id`))");
}
};
 
private static final Migration MIGRATION_2_3 = new Migration(2, 3) {
@Override
public void migrate(SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE Book "
+ " ADD COLUMN pub_year INTEGER");
 
}
};

Here, if you are already done with migration from version 1 to 2 and version 2 to 3, then you don’t need to add migration from 1 to 3. Room will automatically handle the migration from version 1 to 3.

If you have any question on how to use a Room Database, Room Persistent Library or want to consult us for your mobile app development project then please connect with us on sales@anblicks.com. Our experienced app developers will assist you with your application development project queries.

Blog Written By Chintan Mehta

Hadoop to Snowflake New Web