In this article, we will learn about Room Persistence Library, Architectural component of Android. We’ll see how to use room to manage our SQLite database in this Android room database example.
Output:
The app’s data can be saved on users’ devices in a variety of ways. We can store data in SQLite tables, shared preferences, and a variety of other ways on the user’s device. In this article, we will look at how to save data, read data, update data, and delete data in Room Database on Android. On Android, we will perform CRUD operations in the Room Database, and also we’ll look at how to perform CRUD operations in the Room Database in Android.
Room is a persistence library, part of the Android Architecture Components. It makes it easier to work with SQLiteDatabase objects in your app, decreasing the amount of boilerplate code and verifying SQL queries at compile time.
Contents
What is Room Database?
Room is a database layer that is built on top of an SQLite database. Room handles routine tasks that you would normally handle with an SQLiteOpenHelper. To use the room: Make a public abstract class that extends the RoomDatabase class. Use annotations to declare database entities and set the version number.
Room is now thought to be a better option for data persistence than SQLiteDatabase. It simplifies working with SQLiteDatabase objects in your app by reducing boilerplate code and verifying SQL queries at compile time.
Room Components
Room has three main components of Room DB :
- Entity
- Dao
- Database
- Entity: An entity is a modal class that has the annotation @Entity. This class contains variables that will serve as our columns, and the class itself serves as our table.
@Entity data class User( @PrimaryKey val id: Int, @ColumnInfo(name = "first_name") val firstName: String?, @ColumnInfo(name = "last_name") val lastName: String? )
- DAO: The full form of DAO is a Database access object, which is an interface class that allows us to perform various operations on our database.
@Dao interface UserDao { @Query("SELECT * FROM user") fun getAll(): List<User> @Query("SELECT * FROM user WHERE uid IN (:userIds)") fun loadAllByIds(userIds: IntArray): List<User> @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) }
- Database: This is an abstract class that will hold all of our database entries, which we will refer to as Entities. At runtime, you can acquire an instance of Database by calling Room.databaseBuilder() or Room.inMemoryDatabaseBuilder().
@Database(entities = arrayOf(User::class), version = 1) abstract class AppDatabase : RoomDatabase() { abstract fun userDao(): UserDao }
Entities annotations:
- @Entity — every model class with this annotation will have a mapping table in DB.
- @PrimaryKey — as the name suggests, this annotation points to the entity’s primary key. If set to true, SQLite will generate a unique id for the column @PrimaryKey(autoGenerate = true).
- @ColumnInfo — Allows for the specification of custom information about a column @ColumnInfo(name = “column name”)
- @Ignore — the field will not be persisted by Room
- @Embeded — nested fields can be directly referenced in SQL queries
Benefits of Room Database
- SQL queries are validated at compile time. Each @Query and @Entity is checked at compile time, which protects your app from runtime crashes. It not only checks syntax but also missing tables.
- Boilerplate code
- It is simple to integrate with other Architecture components (like LiveData)
- Room adds an abstraction layer over SQLite to allow for fluent database access while retaining SQLite’s full power. Room is now thought to be a better option for data persistence than SQLiteDatabase.
Difference between Room and SQLite
Room is an Object Relational Mapping (ORM) library. The room will, in other words, map our database objects to Java objects. Room adds an abstraction layer over SQLite to allow for fluent database access while retaining SQLite’s full power.
- There is no compile-time verification of raw SQLite queries in the case of SQLite. However, SQL validation is performed at compile time in Room.
- To convert between SQL queries and Java data objects, you must use a lot of boilerplate code. Room, on the other hand, maps our database objects to Java Objects without the use of boilerplate code.
- When your schema changes, you must manually update the affected SQL queries. This issue is resolved by the use of a room.
- For data observation, Room is designed to work with LiveData and RxJava, whereas SQLite does not.
SQL vs NoSQL
Point of difference | SQL | No-SQL |
---|---|---|
Data Stored | In Tabular form | POJO objects or Documents |
Structure | RDBMS | key-value pairs |
Schema | Fixed schema | dynamic, records can be added on the fly |
Scalable | RDBMS | key-value pairs |
Android Support | SQLite | Room(semi-sql), GreenDAO, Realm |
Room Database Example:
Step by Step Explanation:
Step 1: Create a New Project in Android Studio(Empty Activity).
Step 2: The first thing we’ll do is add all of the necessary dependencies in build.gradle(Module:app) and kotlin-kapt plugin as given below:
plugins { … id ‘kotlin-kapt’ } dependencies { implementation “androidx.appcompat:appcompat:$rootProject.appCompatVersion” implementation “androidx.activity:activity-ktx:$rootProject.activityVersion” // Dependencies for working with Architecture components // You’ll probably have to update the version numbers in build.gradle (Project) // Room components implementation “androidx.room:room-ktx:$rootProject.roomVersion” kapt “androidx.room:room-compiler:$rootProject.roomVersion” androidTestImplementation “androidx.room:room-testing:$rootProject.roomVersion” // Lifecycle components implementation “androidx.lifecycle:lifecycle-viewmodel-ktx:$rootProject.lifecycleVersion” implementation “androidx.lifecycle:lifecycle-livedata-ktx:$rootProject.lifecycleVersion” implementation “androidx.lifecycle:lifecycle-common-java8:$rootProject.lifecycleVersion” // Kotlin components implementation “org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version” api “org.jetbrains.kotlinx:kotlinx-coroutines-core:$rootProject.coroutines” api “org.jetbrains.kotlinx:kotlinx-coroutines-android:$rootProject.coroutines” // UI implementation “androidx.constraintlayout:constraintlayout:$rootProject.constraintLayoutVersion” implementation “com.google.android.material:material:$rootProject.materialVersion” implementation “androidx.recyclerview:recyclerview:1.2.1” // For control over item selection of both touch and mouse driven selection implementation “androidx.recyclerview:recyclerview-selection:1.1.0” // Testing testImplementation “junit:junit:$rootProject.junitVersion” androidTestImplementation “androidx.arch.core:core-testing:$rootProject.coreTestingVersion” androidTestImplementation (“androidx.test.espresso:espresso-core:$rootProject.espressoVersion”, { exclude group: ‘com.android.support’, module: ‘support-annotations’ }) androidTestImplementation “androidx.test.ext:junit:$rootProject.androidxJunitVersion” } |
plugins { id 'com.android.application' id 'kotlin-android' id 'kotlin-kapt' } android { compileSdkVersion 30 buildToolsVersion "30.0.3" defaultConfig { applicationId "com.example.notes_app_" minSdkVersion 16 targetSdkVersion 30 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } packagingOptions { exclude 'META-INF/atomicfu.kotlin_module' } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } kotlinOptions { jvmTarget = '1.8' } } dependencies { implementation "androidx.appcompat:appcompat:$rootProject.appCompatVersion" implementation "androidx.activity:activity-ktx:$rootProject.activityVersion" // Dependencies for working with Architecture components // You'll probably have to update the version numbers in build.gradle (Project) // Room components implementation "androidx.room:room-ktx:$rootProject.roomVersion" kapt "androidx.room:room-compiler:$rootProject.roomVersion" androidTestImplementation "androidx.room:room-testing:$rootProject.roomVersion" // Lifecycle components implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$rootProject.lifecycleVersion" implementation "androidx.lifecycle:lifecycle-livedata-ktx:$rootProject.lifecycleVersion" implementation "androidx.lifecycle:lifecycle-common-java8:$rootProject.lifecycleVersion" // Kotlin components implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$rootProject.coroutines" api "org.jetbrains.kotlinx:kotlinx-coroutines-android:$rootProject.coroutines" // UI implementation "androidx.constraintlayout:constraintlayout:$rootProject.constraintLayoutVersion" implementation "com.google.android.material:material:$rootProject.materialVersion" implementation "androidx.recyclerview:recyclerview:1.2.1" // For control over item selection of both touch and mouse driven selection implementation "androidx.recyclerview:recyclerview-selection:1.1.0" // Testing testImplementation "junit:junit:$rootProject.junitVersion" androidTestImplementation "androidx.arch.core:core-testing:$rootProject.coreTestingVersion" androidTestImplementation ("androidx.test.espresso:espresso-core:$rootProject.espressoVersion", { exclude group: 'com.android.support', module: 'support-annotations' }) androidTestImplementation "androidx.test.ext:junit:$rootProject.androidxJunitVersion" }
Add the version of dependencies in build.gradle(Project:app) file as shown below:
ext { activityVersion = ‘1.2.3’ appCompatVersion = ‘1.3.0’ constraintLayoutVersion = ‘2.0.4’ coreTestingVersion = ‘2.1.0’ coroutines = ‘1.5.0’ lifecycleVersion = ‘2.3.1’ materialVersion = ‘1.3.0’ roomVersion = ‘2.3.0’ // testing junitVersion = ‘4.13.2’ espressoVersion = ‘3.1.0’ androidxJunitVersion = ‘1.1.2’ } |
// Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { ext.kotlin_version = "1.5.10" repositories { google() mavenCentral() } dependencies { classpath "com.android.tools.build:gradle:4.2.1" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } allprojects { repositories { google() mavenCentral() jcenter() // Warning: this repository is going to shut down soon } } task clean(type: Delete) { delete rootProject.buildDir } ext { activityVersion = '1.2.3' appCompatVersion = '1.3.0' constraintLayoutVersion = '2.0.4' coreTestingVersion = '2.1.0' coroutines = '1.5.0' lifecycleVersion = '2.3.1' materialVersion = '1.3.0' roomVersion = '2.3.0' // testing junitVersion = '4.13.2' espressoVersion = '3.1.0' androidxJunitVersion = '1.1.2' }
Sync your project after adding the above dependencies, and you’re ready to go.
Step: 3 Now comes to the important part: we must construct an entity for each table we require. Create a new class for the entity, named it Note, and add the code given below to it.
package com.example.notes_app_ import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.PrimaryKey @Entity(tableName = "notes_table") data class Note( @ColumnInfo(name = "text") var text: String, @ColumnInfo(name = "phone") var Phone: String, @ColumnInfo(name = "Amount") var Amount:String ) { @PrimaryKey(autoGenerate = true) var id = 0 }
- As you can see in the above code we have annotated the class with @Entity this is our table, and for the column id we have used @PrimaryKey(autoGenerate = true) this means this id will be auto increment, for other columns we used @ColumnInfo(name = “columnname”)
Step 4: Create a new class for Dao (Data Access Object). So create an interface named as NoteDao and add the code given below to it.
package com.example.notes_app_ import androidx.lifecycle.LiveData import androidx.room.* @Dao interface NoteDao { @Insert(onConflict = OnConflictStrategy.IGNORE) suspend fun insert(note: Note) @Delete suspend fun delete(note: Note) @Query("Select * from notes_table order by id ASC") fun getAllNotes(): LiveData<List<Note>> }
- You can see above we defined all the methods needed for the Create, Read, and Delete operation.
Step 5: Create a database class, named it as NoteDatabase, and add the code given below to it.
package com.example.notes_app_ import android.content.Context import androidx.room.Database import androidx.room.Room import androidx.room.RoomDatabase @Database(entities = [Note::class], version = 1, exportSchema = false) abstract class NoteDatabase : RoomDatabase() { abstract fun getNoteDao(): NoteDao companion object { // Singleton prevents multiple instances of database opening at the // same time. @Volatile private var INSTANCE: NoteDatabase? = null fun getDatabase(context: Context): NoteDatabase { // if the INSTANCE is not null, then return it, // if it is, then create the database return INSTANCE ?: synchronized(this) { val instance = Room.databaseBuilder( context.applicationContext, NoteDatabase::class.java, "note_database" ).build() INSTANCE = instance // return instance instance } } } }
Step 6: Create a new repository class, named it as NoteRepository, and add the given below code to it.
package com.example.notes_app_ import androidx.lifecycle.LiveData class NoteRepository(private val noteDao: NoteDao) { val allNotes: LiveData<List<Note>> = noteDao.getAllNotes() suspend fun insert(note: Note) { noteDao.insert(note) } suspend fun delete(note: Note) { noteDao.delete(note) } }
Step 7: Create a new class for ViewModel and named it as NoteViewModel and add the code given below to it.
package com.example.notes_app_ import android.app.Application import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData import androidx.lifecycle.viewModelScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch class NoteViewModel(application: Application) : AndroidViewModel(application) { private val repository: NoteRepository val allNotes: LiveData<List<Note>> init { val dao = NoteDatabase.getDatabase(application).getNoteDao() repository = NoteRepository(dao) allNotes = repository.allNotes } fun deleteNote(note: Note) = viewModelScope.launch(Dispatchers.IO) { repository.delete(note) } fun insertNote(note: Note) = viewModelScope.launch(Dispatchers.IO) { repository.insert(note) } }
Step 8: In activity_main.xml file design, a layout for the notes application so add the code given below into it.
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <EditText android:id="@+id/input" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="16dp" android:hint="Enter Name here" android:textSize="16sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.001" /> <EditText android:id="@+id/inputphone" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="16dp" android:hint="Enter Number here" android:inputType="number" android:textSize="16sp" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@id/input" /> <EditText android:id="@+id/inputAmount" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="16dp" android:hint="Enter Amount here" android:inputType="number" android:textSize="16sp" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@id/inputphone" /> <Button android:id="@+id/addbtn" style="@style/TextAppearance.AppCompat.Widget.Button.Inverse" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="16dp" android:background="@color/teal_200" android:onClick="submitData" android:text="submit" android:textColor="#FFFFFF" app:layout_constraintTop_toBottomOf="@id/inputAmount" /> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="0dp" android:layout_marginTop="16dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintTop_toBottomOf="@id/addbtn" app:layout_constraintVertical_bias="1.0" tools:listitem="@layout/item_note" /> </androidx.constraintlayout.widget.ConstraintLayout>
Step 9: RecyclerView Layout design– We will display all the tasks added in a RecyclerView, and for this, we need one more layout file. So create a layout file and name it item_note and write the following XML code inside.
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" xmlns:tools="http://schemas.android.com/tools" android:layout_margin="12dp" android:background="#abcdef"> <TextView android:id="@+id/textitem" android:layout_width="0dp" android:layout_height="wrap_content" android:padding="12dp" tools:text="Sagar" android:textColor="#212121" android:textSize="16sp" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toLeftOf="@id/deletebtn" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/textitemphone" android:layout_width="0dp" android:layout_height="wrap_content" android:padding="12dp" android:textColor="#212121" android:textSize="16sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="@+id/textitem" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/textitem" app:layout_constraintVertical_bias="0.115" tools:text="1234567890" /> <TextView android:id="@+id/textitemAmount" android:layout_width="0dp" android:layout_height="wrap_content" android:padding="12dp" tools:text="50$" android:textColor="#212121" android:textSize="16sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="@+id/textitem" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/textitemphone" /> <ImageView android:id="@+id/deletebtn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="16dp" android:src="@drawable/ic_baseline_delete_24" app:layout_constraintBottom_toBottomOf="@id/textitem" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="@id/textitem" /> </androidx.constraintlayout.widget.ConstraintLayout>
Step 10: Create an Adapter class for recyclerView, named it as NoteRVAdapter, and add the code given below to it.
package com.example.notes_app_ import android.content.Context import android.view.LayoutInflater import android.view.OrientationEventListener import android.view.View import android.view.ViewGroup import android.widget.ImageView import android.widget.TextView import androidx.recyclerview.widget.RecyclerView import org.w3c.dom.Text class NotesRVAdapter(private val context: Context, private val listener: INotesRVAdapter) : RecyclerView.Adapter<NotesRVAdapter.NoteViewHolder>() { private val allNotes = ArrayList<Note>() inner class NoteViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val textView: TextView = itemView.findViewById<TextView>(R.id.textitem) val phoneView: TextView = itemView.findViewById<TextView>(R.id.textitemphone) val deleteButton: ImageView = itemView.findViewById<ImageView>(R.id.deletebtn) val AmountView: TextView = itemView.findViewById(R.id.textitemAmount) fun bind(note: Note) { textView.text = note.text phoneView.text = note.Phone AmountView.text= note.Amount } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NoteViewHolder { val viewHolder = NoteViewHolder(LayoutInflater.from(context).inflate(R.layout.item_note, parent, false)) viewHolder.deleteButton.setOnClickListener { listener.onItemClicked(allNotes[viewHolder.adapterPosition]) } return viewHolder } override fun onBindViewHolder(holder: NoteViewHolder, position: Int) { val currentNote = allNotes[position] holder.bind(currentNote) // holder.textView.text = currentNote.text // holder.phoneView.text = currentNote.Phone } fun updateList(newList: List<Note>) { allNotes.clear() allNotes.addAll(newList) notifyDataSetChanged() } override fun getItemCount(): Int { return allNotes.size } } interface INotesRVAdapter { fun onItemClicked(note: Note) }
Step 11: Now come to MainActivity file and add the code given below to it.
package com.example.notes_app_ import android.os.Bundle import android.view.View import android.widget.EditText import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView class MainActivity : AppCompatActivity(), INotesRVAdapter { lateinit var viewModel: NoteViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) findViewById<RecyclerView>(R.id.recyclerView).layoutManager = LinearLayoutManager(this) val adapter = NotesRVAdapter(this, this) findViewById<RecyclerView>(R.id.recyclerView).adapter = adapter viewModel = ViewModelProvider( this, ViewModelProvider.AndroidViewModelFactory.getInstance(application) ).get(NoteViewModel::class.java) viewModel.allNotes.observe(this, Observer { list -> list?.let { adapter.updateList(it) } }) } override fun onItemClicked(note: Note) { viewModel.deleteNote(note) Toast.makeText(this,"${note.text} DELETED ",Toast.LENGTH_LONG).show() } fun submitData(view: View) { val noteText = findViewById<EditText>(R.id.input).text.toString() val phoneText =findViewById<EditText>(R.id.inputphone).text.toString() val AmountText = findViewById<EditText>(R.id.inputAmount).text.toString() if (noteText.isNotEmpty()){ viewModel.insertNote(Note(noteText,phoneText,AmountText)) Toast.makeText(this,"$noteText ,$phoneText SUBMITED ",Toast.LENGTH_LONG).show() } } }
The code given below is of AndroidManifest.xml file:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.notes_app_"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.NOTES_APP_"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
Run the application now. You will get the output shown in the output section.
Notes Application Source Code:
We’ve completed the development of the Notes application. You can get the Source code from the given below button.
We hope that this guide will assist you in understanding all about the concepts of Room Database in android. We have concentrated on making a basic, meaningful, and easy-to-learn guide to the concepts of the Room database with suitable examples. Still, if you have any problems regarding this, please post them in the comments section, we will be glad to assist you.
Room database android, room database in android, room database in android, android room, room database, android room database, Room database android, room database in android, room database in android, android room, room database, android room database, Room database android, room database in android, room database in android, android room, room database, android room database, room database in android room database in android.
Pingback: Android Architecture Pattern (MVVM, MVC, MVP) - Developers Dome
Pingback: Using Hilt Dependency Injection in Android App | Notes App