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.
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
Thanks for the amazing post.
ReplyDeleteSQL Server Load Soap Api