The Room library is used to harness the full power of SQLite by providing an abstract layer over the SQLite. The library helps create a cache of your apps data on the device that your app is running on. This cached data allows users to see consistent copy of information regardless of whether there is internet connection or not. This library contains three major components
Database - Serves as the main access point to the underlying connection to your app's data.
Entity - Represents the table in the database
Dao - Contains the abstract methods used to access the database.
To use the library , first add these dependencies to the build.gradle
file inside the Dependencies block
dependencies{
def room_version = "2.2.5"
//Room
implementation "androidx.room:room-runtime:$room_version"
implementation "androidx.room:room-ktx:$room_version"
kapt "androidx.room:room-compiler:$room_version"
}
Creating an Entity
Entity defines a set of fields . Entities basically represents a table in the associated database to hold data items.
@Entity
data class Note(
@PrimaryKey(autoGenerate=true)
val id:Int,
val title:String,
val note:String
)
@Entity
annotation shows that the class is a table in the database. By default Room uses the class name as the table name, but you can set a different name by using the tableName
property, i.e @Entity(tableName="table_name")
.The @PrimaryKey(autoGenerate=true)
annotation declares the note id
as the primary key and the autoGenerate=true
tells us that the id
is automatically generated. The id
, title
and the note
are column names but can be changed to different names by using @ColumnInfo(name="column_name")
Dao
The data access objects include methods that offer abstract access the app's database. The DAOs help separate the different components of the database architecture.
@Dao
interface NoteDao {
@Insert
fun addNote(note: Note)
@Query("SELECT * FROM note ORDER by id DESC")
fun getAllNotes():List<Note>
@Insert
fun addMultipleNotes(vararg note: Note)
@Update
fun updateNote(note: Note)
@Delete
fun deleteNote(note: Note)
}
The @Insert
annotation does exactly as it states. It creates an implementation that inserts all parameters to the database. @Update
modifies a database entity, the @Delete
removes an entity passed as a parameter and @Query
loads all data from the app's database e.g @Query("SELECT * FROM note ORDER by id DESC")
. This query returns a list of notes from the database and orders them by id in a descending order.
Creating the Database
A database class should use the @Database
annotation to mark the class as a RoomDatabase. Inside this annotation we define all the entities and the database version. In our case we only have one entity. A database class should be abstract and extend RoomDatabase. To change the database schema create the migration class and change the version number. In this post however, we wont do that.
@Database(entities = [Note::class], version = 1)
abstract class NoteDatabase:RoomDatabase() {
abstract fun getNoteDao():NoteDao
companion object{
@Volatile private var instance : NoteDatabase? = null
private val LOCK = Any()
operator fun invoke(context: Context) = instance?: synchronized(LOCK){
instance?: buildDatabase(context).also {
instance=it
}
}
private fun buildDatabase(context: Context) = Room.databaseBuilder(
context.applicationContext,
NoteDatabase::class.java,
"notedatabase"
).build()
}
}
The abstract function getNoteDao
returns the NoteDao
which gives access to the Entity. Inside the companion object
we build the database. we create an instance of the database and annotate it with @Volatile
to make it available to all the threads. The invoke
function checks if the instance is null. If it is not null we return the instance immediately. If it is null we again check in the synchronized block. If inside the synchronized block the instance is null, we call the buildDatabase
function to build our database.