Skip to main content

Using Room Persistence Library for SQLite Database

This article is all about Using Room Persistence Library Android Architecture Components for SQLite Database management on Android device. Room is a Persistence Library which provides an abstract layer over SQLite Database. Using Room we utilize the full power of SQLite database without any compromisation with performance. Even it provides several benefits over using the SQLite database with API and plain SQL query.



Why should we use the Room for SQLite Database



Room provides a persistence layer over SQLite database.  We should use the Room for creating the cache for our application so that we can access the app future even if your app is offline. When the user comes online then data can be sync online over the internet and give the user a seamless user experience.

Benefit of using Room Persistence library Your App




  • One of the major benefits of the Room Persistence library is that it provides compile-time SQL validation. So there is no chance of runtime SQL exception as validation done at the initial stage during compilation of the app.

  • Room provide observer functionality so that any change in the database can be directly observed and explored as LiveData.

  • It also provides the functionality using which we don't need to work with raw SQL database.



Adding Room persistence library Your App



So first steps for learning the use of Room Android Architecture Components is that adding the library to the app.  So adding the library we need to add the dependency to your build.gradle file as given below.

build.gradle
build.gradle

implementation “android.arch.persistence.room:runtime:1.0.0”

annotationProcessor “android.arch.persistence.room:compiler:1.0.0”

Component of the Room persistence library



Room database has three component Database, Entity and DAO.



Database

This the main class of Room persistency library. We can create an instance of the Room database by extending the RoomDatabase and declare it as an abstract class. The class which is extending the RoomDatbase should add the annotation @Database.

Entity

In simple entity is the table inside our SQLite Database, which describes the column of the database. It is declared by giving the annotation @Entity.

DAO

DAO stands for Data Access Object and it can use for accessing the database. i.e all insert, update and delete are done from DAO class. We can create the DAO object by providing the annotation @Dao.

Room Database Articture

Create Entity for Room Database



There is three type of the element are required to manage the SQLite database with Room Persistence Library. Entity is the one of the very important component of that. We can create an Entity for our application by just adding the annotation @Entity . Multiple Entity can be create within our app per requirement of the app. One Entity represent one table within the Database. For example below is the code of a sample Entity class for user profile.

Java
Kotlin
Java

package com.nplix.roomdatabaseexample;


import android.arch.persistence.room.Entity;
import android.arch.persistence.room.PrimaryKey;
import android.support.annotation.NonNull;

/**
* Created by PK on 1/23/2018.
*/
@Entity()
public class ProfileEntity {
@PrimaryKey(autoGenerate = true)
@NonNull
public Integer ID;
public String Name;
public String Email;

@NonNull
public Integer getID() {
return ID;
}

public void setID(@NonNull Integer ID) {
this.ID = ID;
}

public String getName() {
return Name;
}

public void setName(String name) {
Name = name;
}

public String getEmail() {
return Email;
}

public void setEmail(String email) {
Email = email;
}
}

Kotlin

package com.nplix.profileexample


import android.arch.persistence.room.Entity
import android.arch.persistence.room.PrimaryKey
import android.support.annotation.NonNull

/**
* Created by PK on 1/23/2018.
*/
@Entity
class ProfileEntity {
@PrimaryKey(autoGenerate = true)
@NonNull
var id: Int=0;
var name:String="name"
var email:String="Email"

}

Above Entity class have three field ID, Name and Email of the User. By default Room create the table with the name of the class. So here in above example name of the our class is ProfileEntity so name of the table is ProfileEntity.

Column of the table is ID,Name and Email will be the name of the variable in the class. But, we may need to declare ID to generate the automatically and set it as primary key of the table. So we can add the annotation @PrimaryKey(autoGenerate = true)

In kotlin we have not added the getter and setter because it is not required at all. We can directly access the all the abject just using '.' operator.

Create DAO for accessing the the Room Database



Data Access Object and in short DAO Interface is responsible to perform all  query on behalf of you and can be called like simple Java and Kotlin function.  So all Update, Insert, Delete and Select operation will be performed using this DAO class.

Sample implementation of DAO interface in Kotlin and Java as given below.

Kotlin
Java
Kotlin

@Dao
public interface ProfileDao {
@Query("SELECT * FROM ProfileEntity")
abstract fun getAll(): List<ProfileEntity>


@Query("SELECT * FROM ProfileEntity WHERE ID IN (:userIds)")
abstract fun loadAllByIds(userIds: IntArray): List<ProfileEntity>

@Query("SELECT * FROM ProfileEntity WHERE Name LIKE :name LIMIT 1")
abstract fun findByName(name: String): ProfileEntity

@Insert
abstract fun insertAll(vararg profile: ProfileEntity)

@Delete
abstract fun delete(Profile: ProfileEntity)
}

Java

@Dao
public interface ProfileDao {
@Query("SELECT * FROM ProfileEntity")
List<ProfileEntity> getAll();


@Query("SELECT * FROM user WHERE id IN (:userIds)")
List<ProfileEntity> loadAllByIds(int[] userIds);

@Query("SELECT * FROM user WHERE Name LIKE :name LIMIT 1")
ProfileEntity findByName(String name);

@Insert
void insertAll(ProfileEntity... users);

@Delete
void delete(ProfileEntity user);
}

Create Database class for SQLite



So far we have created the Entity and DAO for our demo app. In this section we will create the Database by extending the RoomDatabase class.

This Database class is main class using which we will access the all function of our Entity and DAO class.

It is an abstract class and contain one abstract method which will return the an instance of DAO class.

We can create the Database class like below in Kotlin and Java.

Kotlin
Java
Kotlin

@Database(entities = arrayOf(ProfileEntity::class), version = 1)
abstract class ProfileDatabase : RoomDatabase() {
abstract fun profileDao(): ProfileDao
}


Java

@Database(entities = {ProfileEntity.class},version = 1)
public abstract class ProfileDatabase extends RoomDatabase{
public abstract ProfileDao profileDao();
}


Create Instance of RoomDatabase



We have created the all required components to manage the SQLite Database using the Room. Now it's time to connect the Database with our MainActivity. So that we can access the SQLite Database from our main activity to perform the all database related operation as per requirement of our application.

Kotlin
Java
Kotlin

//Create instance of Database
db = Room.databaseBuilder(getApplicationContext(),
ProfileDatabase.class, DB_NAME).build();


Java

db = Room.databaseBuilder(getApplicationContext(),
ProfileDatabase.class, DB_NAME).build();


Here it is important to note that any DB operation using Room Database can't be performed from main thread.

It is because if we use the database operation on main thread then it might be stuck our UI component for some time and provide bad UX to user.

So all DB operation like Insert, Update, etc should be done from child thread. We can create a Async Task for that.

However, Room provide one option using which we can perform the DB operation from main thread but that need to explicitly tell the Room Database . We will discuss that later, how to perform the DB operation from main thread using Room

Inserting Row in SQLite using Room



For inserting the row into the Database we need to create a Async Task and then use that Async Task to insert the content into the database.

Java
Java

private class InsertTask extends AsyncTask<ProfileEntity,Integer,Integer> {


@Override
protected Integer doInBackground(ProfileEntity… users) {
db.profileDao().insertAll(users);
return null;
}

@Override
protected void onPreExecute() {
super.onPreExecute();
}

@Override
protected void onPostExecute(Integer result) {
super.onPostExecute(result);
Log.d(“InsertTask”,“One Row Inserted”);

}
}

Then execute this Aync Task as given below to insert the content into the SQLite Database.

Java
Java

ProfileEntity entity=new ProfileEntity();
entity.setEmail(userEmail.getText().toString());
entity.setName(userName.getText().toString());


InsertTask insertTask=new InsertTask();
insertTask.execute(entity);

Select Row from SQLite Database using Room



To select the row from the database we can create the new Async Task for select operation like given below.

Java
Java

private class SelectAsyncTask extends AsyncTask<Void,Integer,List<ProfileEntity>> {


@Override
protected List<ProfileEntity> doInBackground(Void... voids) {
return db.profileDao().getAll();
}

@Override
protected void onPreExecute() {
super.onPreExecute();
}

@Override
protected void onPostExecute(List<ProfileEntity> profileEntities) {
super.onPostExecute(profileEntities);
Log.d("Select",profileEntities.size()+" row found");
adapter.setData(profileEntities);
adapter.notifyDataSetChanged();
}
}

In the similar way as we have done the insert and select operation we can do the insert and update as well.

All method are already we have created inside our DAO method we just need to call those method from an Async Task.

If you want learn more about the Android Architecture components, check the below Article.

How to use LiveData and ViewModel as Async Task Loader

Lifecycle Aware Data and Configuration change handling with LiveData

How to use View Model Architecture Components to preserve the data of the Activity and Fragment

Migrating SQLite Database with Room Persistence Library



Apps are getting modified very frequently and there is might be change in the schema of the database. But we  have not update the version of the database then we may observer the below error.

java.lang.IllegalStateException: Room cannot verify the data integrity. Looks like you’ve changed schema but forgot to update the version number. You can simply fix this by increasing the version number.

So me must change the database version in our Database class. After changing the database version if you try to run your app without migrating the database to latest version. You will get the error like given below.

java.lang.IllegalStateException: A migration from 1 to 2 is necessary. Please provide a Migration in the builder or call fallbackToDestructiveMigration in the builder in which case Room will re-create all of the tables.

Above error we are getting because we have change the database version but not added the migration script in our Database build statement.

So first of all, add the below line in your app level build.gradle file. The below code will export the your database schema into the json format and that will be used for migration of your database.

javaCompileOptions {
annotationProcessorOptions {
arguments = ["room.schemaLocation":
"$projectDir/schemas".toString()]
}
}


We have three column in our database and I want to add more column to the database "About us". So first of all we need to modify the our Profile Entity class and add the column.

public String About;
public String getAbout() {
return About;
}


public void setAbout(String about) {
About = about;
}


Next we have to update the ProfileDatabase class and need to update the database version in annotation of the database declaration as given below.

 @Database(entities = {ProfileEntity.class},version = 2)


public abstract class ProfileDatabase extends RoomDatabase{
public abstract ProfileDao profileDao();
}


Finally, we have to modify our database build statement inside main class like below.

 db = Room.databaseBuilder(getApplicationContext(),


        ProfileDatabase.class, DB_NAME).addMigrations(MIGRATION_1_2).build();


Where MIGRATION_1_2 is the our migration script as given below.

final Migration MIGRATION_1_2 = new Migration(1, 2) {
@Override
public void migrate(SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE `ProfileEntity` ADD COLUMN `About` TEXT");
}
};


Room Persistence Library provide the functionality to add multiple database migration script as per requirement. We just have to provide the start and end version of the database and write the migration script.

We may have a scenario where on some of the app database version 1 is running and one some device version2 is running and now we release the new app with version 3. So in this case we need to migrate the database from 1 to 2 and then from 2 to 3, otherwise app will get crash or we will loss the earlier stored data.

So to resolve this issue we need to add the multiple migration script in database build statement. We can it by creating the Migration script like MIGRATION_2_3 and providing the input as new Migration(2,3) and then add this migration into build statement like below.

 db = Room.databaseBuilder(getApplicationContext(),


ProfileDatabase.class, DB_NAME).addMigrations(MIGRATION_1_2,MIGRATION_2_3).build();


One more case we have where we want to clear the previous schema and all data from the database. So Room Persistence Library provide an option to fallback to destructive migration. We have just need to modify  the statement as given below.

db=Room.databaseBuilder(getApplicationContext(),ProfileDatabase.class,DB_NAME)
.fallbackToDestructiveMigration()
.build();


If you add the above statement to build your database instance then it clear the database completely and re-create the schema.

If you want you can download the source code of this example form GitHub

Comments

Post a Comment

Popular posts from this blog

Flutter How to Start Android Activity from Flutter View

Flutter and Dart is an excellent combination for creating the UI, but for accessing the platform-specific service we need to open platform-specific activity. So lets in this article we will explore how to start an android activity and access the service from Flutter View. Create a Project for this Android Activity Flutter View Demo Create a Project From File menu select the New Flutter Project Enter the project name Select the AndroidX support and click on next After the above, we step click on Finish We will have the following project structure created. Create the Second Activity in Android Just go to the android folder and open it in separate windows. We will have the following project structure. Create the Activity Just right-click on the Kotlin folder and create a blank activity from the menu. If you create the activity then you may be required to upgrade the Gradle and do some import. So Just click on update and wait for the project s

Kotlin Parcelable Array Objects Send To Activity

We know that IPC (Inter Process Communication) between the activity is an extremely important part of any application development. We often required that we need to send some data to other activity. For example, we may be required to send an array of data, data could be an Integer, String, Long, Double, Float or any other custom data objects. So, In this example, we are going to learn how to implement the Kotlin Parcelable Array object to send the data from one activity to second activity. What is Parcel? The parcel class is designed as a high-performance IPC transport. A Parcel can contain both flattened data that will be unflattened on the other side of the IPC, and references to live IBinde r objects that will result in the other side receiving a proxy IBinder connected with the original IBinder in the Parcel. Create Kotlin Parcelable Array Objects Parcelable is API for placing the arbitrary objects into the Parcel. In Actual in android app development, Parcelable is an interface

Create Custom EditText View in Android

We use the EditText for taking the input from the user and use it at several places in our project. We required to do lots of customization for each time and there are lots of redundant code we write. Writing and managing these redundant codes is very difficult for example if we want to change the look and feel of the view them we need to modify it at each place where our EditText is getting used. So to avoid these kinds of the problem we can create our own Custom EditText View by just. The EditText view is just an extension of the TextView with lots of editing option and properties that required for the user input. How To Create Custom EditText View For creating the Custom EditText we need to extend the AppCompatEditText and override all three constructors of the view as given below. import android.content.Context; import android.graphics.Typeface; import android.support.annotation.Nullable; import android.support.v7.widget.AppCompatEditText; import android.util.AttributeSet; public