Using Android RoomDatabase

Using Android RoomDatabase

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.