How to use Room database with Jetpack compose + MVVM
Last updated Dec 18, 2021 In this Jetpack compose tutorial we will learn how to use Room database with Jetpack compose in Android application.
Room is one of android architecture component, which is an abstract layer for the SqLite Database
By using the room we can handle the SQLite Database is easily
Room provides the following benefits
- SQL queries will verify on compilation time
- To reduce the boiler place code by adding annotations properties
- Streamlined database migration paths
Read more about Room Database with Kotlin
Let's get started
Step 1: Create android application in android studio
Step 2: Follow step for setup Jetpack Compose with Android Studio
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
}
kapt {
correctErrorTypes = true
}
dependencies {
def room_version = "2.3.0"
implementation "androidx.room:room-ktx:$room_version"
kapt "androidx.room:room-compiler:$room_version"
}
|
Primary components
There are three major components in Room:
- Entity : Entity Represents a table in the database. To create a table with entity we need to annotate class with @Entity
- DAO Data : Access Object is used to access and manage the Data. DAO is an interface wich is annotate with @DAO
- Database : It works as database holder, A class annotated with @Database and extends with Roomdatabase
Data entity
The following code defines a Customer data entity. Each instance of Customer represents a row in a customer table in the app's database
@Entity(tableName = "customer")
data class Customer(
@PrimaryKey(autoGenerate = true)
val id: Int = 0,
@ColumnInfo(name = "name")
val name: String?,
@ColumnInfo(name = "gender")
val gender: String?,
@ColumnInfo(name = "email_id")
val emailId: String?
)
|
Data access object (DAO)
The following code defines a DAO called CustomerDao. CustomerDao provides the methods that the rest of the app uses to interact with data in the customer table
@Dao
interface CustomerDao {
@Query("SELECT * FROM customer")
fun fetchAllCustomer(): LiveData>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertCustomer(customer: Customer)
@Query("DELETE FROM Customer where id = :id")
suspend fun deleteCustomerById(id: Int)
@Query("DELETE FROM customer")
suspend fun deleteAllCustomer()
}
|
Database
The following code defines an AppDatabase class, Database class which is abstract class annotated by @Database. @Database annoation having the fileds
entities -> represents the tables which we are going to use in the Database.
version -> represetns databse version
exportSchema -> represents, do we need to export our database schema into specified folder location, by default its value is true
@Database(entities = [Customer::class], version = 1, exportSchema = false)
abstract class AppDatabase : RoomDatabase() {
abstract fun customerDao(): CustomerDao
companion object {
@Volatile
private var INSTANCE: AppDatabase? = null
fun getDatabase(context: Context): AppDatabase {
val tempInstance = INSTANCE
if (tempInstance != null) {
return tempInstance
}
synchronized(this) {
val instance = Room.databaseBuilder(context.applicationContext,
AppDatabase::class.java, "jetpack")
.build()
INSTANCE = instance
return instance
}
}
}
}
|
Repository
In this Compose Room Database example we are going to manage the Data by using the repository patterns
class CustomerRepository(application: Application) {
private var customerDao: CustomerDao
init {
val database = AppDatabase.getDatabase(application)
customerDao = database.customerDao()
}
val readAllCustomer: LiveData> = customerDao.fetchAllCustomer()
suspend fun insertUser(customer: Customer) {
customerDao.insertCustomer(customer)
}
suspend fun deleteCustomerById(id: Int) {
customerDao.deleteCustomerById(id)
}
suspend fun deleteAllCustomer() {
customerDao.deleteAllCustomer()
}
init {
val database = AppDatabase.getDatabase(application)
customerDao = database.customerDao()
}
}
|
Viewmodel
class CustomerViewmodel(appObj: Application) : AndroidViewModel(appObj) {
private val customerRepository: CustomerRepository = CustomerRepository(appObj)
fun fetchAllCustomer(): LiveData> {
return customerRepository.readAllCustomer
}
fun insertCustomer(customer: Customer) {
viewModelScope.launch {
customerRepository.insertUser(customer = customer)
}
}
fun deleteCustomerById(id: Int) {
viewModelScope.launch {
customerRepository.deleteCustomerById(id)
}
}
}
|
Here is the activity code where we will create ViewModel instance and handle the Database results and display it on listview
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
val customerViewModel = ViewModelProvider(this).get(CustomerViewmodel::class.java)
setContent {
MaterialTheme {
// A surface container using the 'background' color from the theme
Surface(color = MaterialTheme.colors.background) {
CustomerList(customerViewModel)
}
}
}
}
}
@Composable
fun CustomerList(customerViewModel: CustomerViewmodel) {
val customerList = customerViewModel.fetchAllCustomer().observeAsState(arrayListOf())
Scaffold(modifier = Modifier.fillMaxSize(),
floatingActionButton = {
ExtendedFloatingActionButton(
backgroundColor = Color.Red,
text = {
Text(text = "Customer", color = Color.White)
},
onClick = {
val name = UUID.randomUUID().toString()
customerViewModel.insertCustomer(
Customer(
name = name,
gender = "Male",
emailId = "xyz@gmail.com"
),
)
}, icon = {
Icon(
imageVector = Icons.Default.Add,
contentDescription = "image",
tint = Color.White
)
})
},
content = {
LazyColumn(content = {
items(
items = customerList.value,
itemContent = {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp), content = {
val color =
Color(
Random.nextInt(256),
Random.nextInt(256),
Random.nextInt(256)
)
Box(
content = {
Text(
text = (it.name ?: "")[0].uppercase(),
fontSize = 24.sp,
color = color
)
}, modifier = Modifier
.size(80.dp)
.border(width = 1.2.dp, color = color, shape = CircleShape),
contentAlignment = Alignment.Center
)
Spacer(modifier = Modifier.size(16.dp))
Column(
modifier = Modifier.weight(2F),
content = {
Spacer(modifier = Modifier.size(8.dp))
Text(
text = it.name?.uppercase() ?: "",
color = color,
fontSize = 16.sp,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Text(
text = "${it.gender}",
color = Color.Black,
fontSize = 14.6.sp
)
Text(
text = "${it.emailId}",
color = Color.Gray,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
})
Spacer(modifier = Modifier.size(16.dp))
Icon(
imageVector = Icons.Default.Delete,
contentDescription = "image",
tint = Color.Red, modifier = Modifier
.size(30.dp)
.clickable(onClick = {
customerViewModel.deleteCustomerById(it.id)
})
)
})
})
})
})
}
|
 |
Complete Compose Room Database with MVVM code here
package com.example.jetpack
import android.app.Application
import android.content.Context
import android.os.Bundle
import android.view.WindowManager
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Delete
import androidx.compose.runtime.Composable
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import com.example.jetpack.ui.theme.JetPackTheme
import com.example.jetpack.viewmodels.CustomerViewmodel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.room.*
import com.example.jetpack.dao.CustomerDao
import com.example.jetpack.datamodels.AppDatabase
import com.example.jetpack.datamodels.Customer
import com.example.jetpack.repository.CustomerRepository
import com.example.jetpack.widget.CustomerList
import kotlinx.coroutines.launch
import java.util.*
import kotlin.random.Random
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
val customerViewModel = ViewModelProvider(this).get(CustomerViewmodel::class.java)
setContent {
MaterialTheme {
// A surface container using the 'background' color from the theme
Surface(color = MaterialTheme.colors.background) {
CustomerList(customerViewModel)
}
}
}
}
}
@Composable
fun CustomerList(customerViewModel: CustomerViewmodel) {
val customerList = customerViewModel.fetchAllCustomer().observeAsState(arrayListOf())
Scaffold(modifier = Modifier.fillMaxSize(),
floatingActionButton = {
ExtendedFloatingActionButton(
backgroundColor = Color.Red,
text = {
Text(text = "Customer", color = Color.White)
},
onClick = {
val name = UUID.randomUUID().toString()
customerViewModel.insertCustomer(
Customer(
name = name,
gender = "Male",
emailId = "xyz@gmail.com"
),
)
}, icon = {
Icon(
imageVector = Icons.Default.Add,
contentDescription = "image",
tint = Color.White
)
})
},
content = {
LazyColumn(content = {
items(
items = customerList.value,
itemContent = {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp), content = {
val color =
Color(
Random.nextInt(256),
Random.nextInt(256),
Random.nextInt(256)
)
Box(
content = {
Text(
text = it.name[0].uppercase(),
fontSize = 24.sp,
color = color
)
}, modifier = Modifier
.size(80.dp)
.border(width = 1.2.dp, color = color, shape = CircleShape),
contentAlignment = Alignment.Center
)
Spacer(modifier = Modifier.size(16.dp))
Column(
modifier = Modifier.weight(2F),
content = {
Spacer(modifier = Modifier.size(8.dp))
Text(
text = it.name.uppercase(),
color = color,
fontSize = 16.sp,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Text(
text = "${it.gender}",
color = Color.Black,
fontSize = 14.6.sp
)
Text(
text = "${it.emailId}",
color = Color.Gray,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
})
Spacer(modifier = Modifier.size(16.dp))
Icon(
imageVector = Icons.Default.Delete,
contentDescription = "image",
tint = Color.Red, modifier = Modifier
.size(30.dp)
.clickable(onClick = {
customerViewModel.deleteCustomerById(it.id)
})
)
})
})
})
})
}
@Database(entities = [Customer::class], version = 1, exportSchema = false)
abstract class AppDatabase : RoomDatabase() {
abstract fun customerDao(): CustomerDao
companion object {
@Volatile
private var INSTANCE: AppDatabase? = null
fun getDatabase(context: Context): AppDatabase {
val tempInstance = INSTANCE
if (tempInstance != null) {
return tempInstance
}
synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java, "jetpack"
)
.build()
INSTANCE = instance
return instance
}
}
}
}
@Entity(tableName = "customer")
data class Customer(
@PrimaryKey(autoGenerate = true)
val id: Int = 0,
@ColumnInfo(name = "name")
val name: String="",
@ColumnInfo(name = "gender")
val gender: String?,
@ColumnInfo(name = "email_id")
val emailId: String?
)
@Dao
interface CustomerDao {
@Query("SELECT * FROM customer")
fun fetchAllCustomer(): LiveData>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertCustomer(customer: Customer)
@Query("DELETE FROM Customer where id = :id")
suspend fun deleteCustomerById(id: Int)
}
class CustomerRepository(application: Application) {
private var customerDao: CustomerDao
init {
val database = AppDatabase.getDatabase(application)
customerDao = database.customerDao()
}
val readAllCustomer: LiveData> = customerDao.fetchAllCustomer()
suspend fun insertUser(customer: Customer) {
customerDao.insertCustomer(customer)
}
suspend fun deleteCustomerById(id: Int) {
customerDao.deleteCustomerById(id)
}
init {
val database = AppDatabase.getDatabase(application)
customerDao = database.customerDao()
}
}
class CustomerViewmodel(appObj: Application) : AndroidViewModel(appObj) {
private val customerRepository: CustomerRepository = CustomerRepository(appObj)
fun fetchAllCustomer(): LiveData> {
return customerRepository.readAllCustomer
}
fun insertCustomer(customer: Customer) {
viewModelScope.launch {
customerRepository.insertUser(customer = customer)
}
}
fun deleteCustomerById(id: Int) {
viewModelScope.launch {
customerRepository.deleteCustomerById(id)
}
}
}
|
Conclusion: In this jetpack Compose tutorial we covered how to use Room Database with MVVM repository pattern.