How To Store Data Using Room Persistence Library in Android

Nov 01, 2021
hackajob Staff

We've had so many requests for Android tutorials, so today we've decided to dive deep and delve into Room Persistence Library in Android, one of the hottest topics around.

We already know the questions you want to know: what is Room Persistence Library? Why is it needed in the first place? How do you setup ROOM in your application and perform CRUD operations with it? What benefits does it provide over traditional SQLite databases? And of course – our favourite – what are the key components? Buckle up, and let's get into it!

WHAT IS ROOM?

Room is a persistent library in Android that allows fluent access to the database by creating an abstract layer over SQLite. It simplifies the process of adding a structured SQL database to your app. It also makes it easier to define the database objects and access the app data faster by caching it. It keeps a large amount of structured data within the app without using the internet service (yes, we know - super helpful!)

Below are the key components in a typical Room Library:

  • Database
  • Entity
  • DAO

DATABASE

It contains the holder which serves as the access point for the underlying SQLite to your app’s cached data. It is an abstract class annotated with the @Database annotation, extending the Room Database. Please note: it must include an abstract method that returns the Data Access Object class and the list of entities the database will contain.

ENTITY

Each table is represented as a separate Entity in your Database. It can be one or more classes, annotated with the @Entity annotation, which defines the structure of a table used to store instances of the annotated class.

DAO

Database Access Objects contain the methods used to access the Database. It is annotated class (or classes) with the @Dao annotation, which defines the methods used to modify or query the database.

HOW IT WORKS

For Room, an Object Relational Mapping (ORM) is necessary for storing structured data. Each record is stored as a variable within the object defined by the class i.e., table. The database stores the data as a relational in rows and columns.

For storing data, traditional databases require translation between the application’s data and the relational data. It is an error-prone and time-consuming aspect of SQLite. The room allows the use of annotations within the class definition by mapping class variables to a table column, and methods to query statements. This is a simplified process and you do not need to maintain a separate list for table and column names. Database interaction is achieved with special methods.

Additionally, Query annotations harness the full power of SQLite. The compiler verifies each query at compile time and if there is an invalid clause or statement, an error occurs instead of a runtime failure.

WHY ROOM?

Room has additional benefits over SQLite as it leverages the SQLite API directly. Some noteworthy advantages are given below:

  • It provides Compile-time verification of SQL queries (SQLite does not)
  • It has convenient annotations that reduce repetitive boilerplate code
  • It provides a clean template-like structured code

CONFIGURE ROOM

Room persistence library is available at Google’s Maven Repository as a part of the Android Architecture Components suite of the library. You can add the Room to your application by declaring it in your module’s build.gradle file. There are two build files in your android project. The first file in your main folder is a project-level Gradle file and within the app folder of your project, there is another build.gradle file which is your modular level Gradle file.

To add Room, first, you have to make sure that your project-level build.gradle file includes Google’s Maven repository within all project repositories node just like this:

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

Now open your modular Gradle file and make the following dependencies in the repositories node:

dependencies {
Val room version = "2.3.0"

implementation("androidx.room:room-runtime:$roomVersion")annotationProcessor("androidx.room:room-compiler:$roomVersion")

// To use Kotlin annotation processing tool (kapt)kapt("androidx.room:room-compiler:$roomVersion")

// To use Kotlin Symbolic sing (KSP)ksp("androidx.room:room-compiler:$roomVersion")

// optional - Kotlin Extensions and Coroutines support for Roomimplementation("androidx.room:room-ktx:$roomVersion")

// optional - RxJava2 support for Roomimplementation("androidx.room:room-rxjava2:$roomVersion")

// optional - RxJava3 support for Roomimplementation("androidx.room:room-rxjava3:$roomVersion")

// optional - Guava support for Room, including Optional and ListenableFutureimplementation("androidx.room:room-guava:$roomVersion")

// optional - Test helperstestImplementation("androidx.room:room-testing:$roomVersion")

// optional - Paging 3 Integrationimplementation("androidx.room:room-paging:2.4.0-beta01")
}

Now sync your project to ensure that the dependencies are registered. Wait for it to complete and then move onto creating necessary classes.

SAMPLE IMPLEMENTATION

The room has the simplest of implementations. It can be created with a single entity and a DAO. No, we're not kidding, it really is that simple.

ENTITY

An entity class must be declared by keeping the following conditions true:

  • It must be annotated with @Entity,
  • It should be defined as a data class,
  • Object variables for the primary key must be annotated with @PrimaryKey, while the variables for the rest of the columns should be annotated with @ColumnInfo.

Here is the Kotlin code for creating the User entity with two string variables i.e. firstName and lastName.

@Entity
data class User(
@PrimaryKey val uid: Int,
@ColumnInfo(name = "first_name") val firstName: String?, @ColumnInfo(name = "last_name") val lastName: String?
)

DAO

Now we must create our DAO by satisfying the following conditions:

  • It must be an interface annotated with @Dao,
  • It must have at least one method.
  • Data retrieval query must be annotated with @Query
  • Insert statement must have @Insert annotation
  • Delete statement must be annotated with @Delete

We have Kotlin code for creating a Database Access Object class with methods to perform CRUD operations with the User’s database.

@Dao
interface UserDao {

@Query("SELECT * FROM user")
fun getAll(): List

@Query("SELECT * FROM user WHERE uid IN (:userIds)")
fun loadAllByIds(userIds: IntArray): List

@Query("SELECT * FROM user WHERE first_name LIKE :first AND " +
"last_name LIKE :last LIMIT 1")
fun findByName(first: String, last: String): User

@Insert
fun insertAll(vararg users: User)

@Delete
fun delete(user: User)
}

APPDATABASE

Now we have some code to define an AppDatabase class, which defines the database configuration. The database class must satisfy the following conditions:

  • It must be an abstract class extending RoomDatabase class.
  • The class must be annotated with a @Database annotation, including the list of all the entities associated with the database.
  • This database class must define an abstract method with zero arguments for each DAO class associated with the database. This abstract method must return an instance of the DAO class.

Here is the Kotlin implementation for the AppDatabase class.

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

private static volatile AppDatabase INSTANCE;
private static final int NUMBER_OF_THREADS = 1;
static final ExecutorService databaseWriteExecutor =
    Executors.newFixedThreadPool(NUMBER_OF_THREADS);

static AppDatabase getDatabase(final Context context) {
if (INSTANCE == null) {
synchronized (AppDatabase.class) {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
AppDatabase.class, "user_database")
.build();
}
}
}
return INSTANCE;
}
}

After defining the Data Entity, the DAO, and the database object, you should create an instance of the database. After creating the instance of the database, You can use the abstract methods from the AppDatabase class to get an instance of the DAO. This enables you to use the methods from the DAO to interact with the database.

Inside the Kotlin Activity class, get the instance of AppDatabase and call the appropriate functions. The following example selects all data from the table and stores it inside a list of users:

@Override
public void run() {
List users = AppDatabase.getDatabase(
getApplicationContext()
).userDao().getAll();
}

CONCLUSION

So that's it! Well done on learning how to store data using Room Persistence Library and its key components. We also looked at how to set up the Room for your project, and we recommend being careful about the project-level and modular-level build.gradle files. Both have their specific declarations.

Like what you've read or want more like this? Let us know! Email us here or DM us: Twitter, LinkedIn, Facebook, we'd love to hear from you.