This content originally appeared on Level Up Coding - Medium and was authored by PA_Kaur
Understanding Room Database and its related terms is crucial for efficient mobile app development. This article explains the basics of Room Database, its components, and how to use it effectively in your Android applications. But before that we’ll see some basic things about databases.
Database and its related terms for better understanding
A database is a collection of organized data that is stored and accessed electronically. It is simply where data is kept.
A Database Management System (DBMS) is software that helps you interact with the database. It also referred to as Non-relational DBMS. It allows you to add, retrieve, update, and delete data efficiently and securely. Think of the database as a library of books (data), and the DBMS as the librarian who helps you find and manage those books. Examples of DBMS include Microsoft Access and Oracle.
A Relational Database Management System (RDBMS) is a software system that organizes data into tables with rows and columns, and supports relationships between different data sets. Examples of RDBMS include MySQL and PostgreSQL.
The difference between DBMS and RDBMS is that DBMS stores data as files without relationships in data sets, whereas RDBMS uses tables to create and manage relationships between data sets.
Query
In Both DBMS and RDBMS software, we need a way to query our data in databases, updating records, and managing database structures. Different databases uses different querying mechanisms and techniques depending upon the types of databases. In most RDBMS based databases SQL (Structured Query Language) is used for querying and managing data. And in non-relational DBMS based databases uses different query languages or APIs tailored to the specific data model (document, key-value, column-family, graph, etc.). In non-relational based DBMS like MongoDB Uses a query language based on JSON-like documents and Redis Uses its own set of commands to query and manipulate data stored in key-value pairs.
Database used in Mobiles
As we know the OS that we use in computers can’t be used in mobiles. For mobiles we need a smaller and lightweight version of OS as mobiles are smaller as compared to computers. Similarly, we can’t use the same database in mobiles that we use in computers. For that reason SQLite was developed to work in mobile devices.
We use SQLite in mobile applications because it is small, fast, and doesn’t need any setup, making it perfect for mobile devices. Other databases like MySQL are larger and require a server, which is less practical for mobile use.
SQLite is a relational lightweight database that stores data in a single file and does not require a separate server to operate.
Room Database is a part of the Android Jetpack libraries and provides an abstraction layer over SQLite to allow for more convenient and efficient way to harness the full power of SQLite. It helps you create and manage a SQLite database for your Android application.
Why we use Room Database over SQLite directly when Room itself under the hood uses SQLite?
Room is more convenient than using SQLite directly because
- Compile-time verification of SQL queries: Ensures that your SQL queries are properly formed and that they refer to valid columns.
- Easier data migration: Helps manage schema changes across different versions of your database.
- Built-in support for Live Data and Kotlin Coroutines: Makes it easier to perform asynchronous database operations and observe database changes.
- Simplified database operations: Reduces boilerplate code by handling common tasks like setting up the database connection, performing CRUD operations, etc.
Key things of Room DB that we will cover further are:
- Entities
- DAO (Data Access Object)
- Database
To use Room in your Android project, follow these steps:
Step 1. Add Dependencies in your build.gradle file:
dependencies {
implementation "androidx.room:room-runtime:2.5.0"
kapt "androidx.room:room-compiler:2.5.0"
implementation "androidx.room:room-ktx:2.5.0" // for Kotlin extensions
}
Step 2. Create entities or tables:
Entities: An entity represents a table within the database. It is typically defined as a class annotated with @Entity, where each field corresponds to a column in the table.
NOTE:
Data class and entity class can be same and different also. It depends on you. But generally I prefer it to be same.
The convention is to keep the table name in small case.
@Entity(tableName = "users")
data class User(
@PrimaryKey(autoGenerate = true) val id: Int,
@ColumnInfo(name = "first_name") val firstName: String,
@ColumnInfo(mobile = "mobile_number") val mobileNumber: String,
)
Step 3. Create DAO (Data Access Object) interface:
DAO (Data Access Object): DAO is an interfaces used to define methods that provide the primary way of interacting with the data in your Room database. They are annotated with @Dao and contain methods for accessing the database, which can be annotated with @Insert, @Update, @Delete, and @Query.
All the Db operations should be performed on background thread. If I execute them on main thread then there will be ANR (Application not responding) error. So for our logic to be executed on background threads we have used Kotlin Coroutines here.
@Dao
interface UserDao {
@Insert
suspend fun insert(user: User)
@Update
suspend fun update(user: User)
@Delete
suspend fun delete(user: User)
/**
* Notice all the above methods are marked with suspend keyword
* so that they can execute on background threads but the
* below method isn't marked with suspend keyword. The
* reason being that it is Room DB's feature that it checks if
* it's return type is of LiveData type then it automatically
* works on background thread. So we don't have any need to use
* suspend keyword.
*/
@Query("SELECT * FROM users")
fun getAllUsers(): LiveData<List<User>>
}
Step 4. Create the Database:
Database: The database class serves as the main access point to the underlying connection to your app’s persisted relational data. It is annotated with @Database and includes an abstract method that has zero arguments and returns the DAO class.
/**
* @param entities is the list of all entities i.e, tables that
* you wanted to store in a database.
*
* @param version is the version number of your database.
*/
@Database(entities = [User::class], version = 1)
/**
* Here you can mention your Type converters also. e.g;
* @TypeConverters(Convertors::class)
*/
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
/**
* The best practice is to use the singleton patteren for Database
* object.
*
* companion object is used for creating singleton objects.
*
* @volatile annotation is used so that it can send update to all
* other threads whosoever is using the database object that the
* variable value has changed.
*
* synchronized keyword is used so that it will not allow if
* multiple threads tries to create the database object at the
* same time.
*/
companion object {
@volatile
private var INSTANCE: AppDatabase ?= null
fun getDatabase (context: Context) : AppDatabase {
if(INSTANCE == null) {
synchronized (this) {
INSTANCE = Room.databaseBuilder(context.applicationContext, AppDatabase :: class, "AppDB").build()
}
}
return INSTANCE!!
}
}
}
Step 5. Use the DAO:
var database : AppDatabase = AppDatabase.getDatabase(this)
GlobalScope.launch {
database.userDao().insert(User(firstName = "John", mobileNumber = "123456789"))
}
## To further advance your knowledge of Room DB, let’s understand the concept of Type Convertors and Room DB migration.
Type Convertors :
SQLite database supports only following data types:
- NULL
- INTEGER
- REAL // for storing Floating Points
- TEXT
- BLOB // for storing Binary data of large files like images etc.
Now If I want to store any other data types then I have to use convertors.
Suppose I want to save Date in database then these convertors will convert Date to Long and when fetch the Date from the DB then these convertors will convert Long to Date.
class Convertors {
@TypeConvertor
fun fromDateToLong (value: Date) : Long {
return value.time
}
@TypeConvertor
fun fromLongToDate (value: Long) : Date {
return Date(value)
}
}
@Entity(tableName = "users")
data class User(
@PrimaryKey(autoGenerate = true) val id: Int,
@ColumnInfo(name = "first_name") val firstName: String,
@ColumnInfo(mobile = "mobile_number") val mobileNumber: String,
@ColumnInfo(date = "current_date") val currentDate: Date
)
Now add @TypeConverters(Convertors::class) annotation on Database class.
@Database(entities = [User::class], version = 1)
@TypeConverters(Convertors::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
companion object {
@volatile
private var INSTANCE: AppDatabase ?= null
fun getDatabase (context: Context) : AppDatabase {
if(INSTANCE == null) {
synchronized (this) {
INSTANCE = Room.databaseBuilder(context.applicationContext, AppDatabase :: class, "AppDB").build()
}
}
return INSTANCE!!
}
}
}
After that you can use it like given below.
class Convertors {
GlobalScope.launch {
database.userDao().insert(User(firstName = "John", mobileNumber = "Doe", currentDate = Date()))
}
}
Room Database Migration:
When there are schema changes in your Android app. For example, if you want to add a new column to the User entity then perform migrations in Room Database. Migrations helps the existing users who are already using the app to update the database without losing any data.
Understand the concept diagrammatically.
Steps for Room Database Migration:
Step 1. Define the New Schema:
@Entity(tableName = "users")
data class User(
@PrimaryKey(autoGenerate = true) val id: Int,
val firstName: String,
val mobileNumber: String,
val age: INTEGER // newly added column
)
Step 2. Update version number in Database Class:
@Database(entities = [User::class], version = 2)
Step 3. Update version number then create a Migration Object and include the Migration in the Database Builder:
//Update version number here
@Database(entities = [User::class], version = 2)
@TypeConverters(Convertors::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
// create a Migration Object here
companion object {
val migration_1_2 = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
// Perform the migration. For example, add a new column:
database.execSQL("ALTER TABLE users ADD COLUMN age INTEGER NOT NULL DEFAULT(0)")
}
}
}
companion object {
@volatile
private var INSTANCE: AppDatabase ?= null
fun getDatabase (context: Context) : AppDatabase {
if(INSTANCE == null) {
synchronized (this) {
INSTANCE = Room.databaseBuilder(context.applicationContext, AppDatabase :: class, "AppDB")
.addMigrations(migration_1_2) // include the Migration in the Database Builder here
.build()
}
}
return INSTANCE!!
}
}
}
If you found this article useful then please start following me. You can also find me on LinkedIn and GitHub
Take care and keep dreaming big :)
Room Database in Android was originally published in Level Up Coding on Medium, where people are continuing the conversation by highlighting and responding to this story.
This content originally appeared on Level Up Coding - Medium and was authored by PA_Kaur
PA_Kaur | Sciencx (2024-07-14T17:24:18+00:00) Room Database in Android. Retrieved from https://www.scien.cx/2024/07/14/room-database-in-android/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.