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

 

Download Source code

 

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

 

Compose Room Database with MVVM

 

 

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)
                                        })
                                )
                            })
                    })
            })
        })

}

 

Jetpack Compose MVVM Room Database

 

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.

 

Article Contributed By :
https://www.rrtutors.com/site_assets/profile/assets/img/avataaars.svg

8703 Views