Find the CRUD operations in Room Database in Attendance Tracker Android app (Kotlin) – Part 3
Tags: kotlin, room, android, xml
Published: 2022-03-16 09:35:42



![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/68wfueonvj5zih8obqmo.png)
**Introduction**

In this article, we can learn about the **CRUD** (Create, Read, Update and Delete) operations in Room Database in Attendance Tracker app.

Room is an **ORM** (Object Relational Mapping) library. In other words, Room will map our database objects to Java objects. Room provides an abstraction layer over SQLite to allow fluent database access while harnessing the full power of SQLite. Room can provide to create sqlite databases and perform the operations like create, read, update and delete.

So, I will provide the series of articles on this Attendance Tracker App, in upcoming articles I will integrate other Huawei Kits.

If you are new to this application, follow my previous articles

[Integration of Huawei Account Kit of Obtaining Icon Resources feature in Attendance Tracker Android app (Kotlin) - Part 1](https://forums.developer.huawei.com/forumPortal/en/topic/0201810952098220013?fid=0101187876626530001)

[Find Log in via SMS and Forgot password? Features using Huawei Account Kit, and Ads Kit in Attendance Tracker Android app (Kotlin) – Part 2](https://forums.developer.huawei.com/forumPortal/en/topic/0201817532838860004?fid=0101187876626530001)

**Components of Room DB**

1. Entity

2. Dao

3. Database

**
- Entity
**

Represents a table within the database. Room creates a table for each class that has **@Entity** annotation, the fields in the class correspond to columns in the table. Therefore, the entity classes tend to be small model classes that does not contain any logic.

**Entities annotations**

Before we get started with modelling our entities, we need to know some useful annotations and their attributes.

@Entity - every model class with this annotation will have a mapping table in DB.

- foreignKeys — names of foreign keys
- indices — list of indicates on the table
- primaryKeys — names of entity primary keys
- tableName

**@PrimaryKey **- as name indicates, this annotation points is the primary key of the entity. autoGenerate - if set to true, then SQLite will be generating a unique id for the column.

**Example:** @PrimaryKey(autoGenerate = true)

**@ColumnInfo** - allows specifying custom information about column.

**Example:** @ColumnInfo(name = “column_name”)

**@Ignore** - field will not be persisted by Room.

**@Embeded** - nested fields can be referenced directly in the SQL queries.

**
- Dao
**

**DAOs**(Data Access Objects) are responsible for defining the methods that access the database. In the initial SQLite, we use the Cursor objects. With Room, we do not need all the Cursor related code and can simply define our queries using annotations in the Dao class.

**
- Database
**

Contains the database holder and serves as the main access point for the underlying connection to your app’s persisted, relational data.

To create a database, we need to define an abstract class that extends RoomDatabase. This class is annotated with @Database, lists the entities contained in the database, and the DAOs which access them.

The class which is annotated with **@Database** should satisfy the following conditions:

Be an abstract class that extends RoomDatabase.
Include the list of entities associated with the database within the annotation.
Contain an abstract method that has 0 arguments and returns the class that is annotated with @Dao.

**Requirements**

1. Any operating system (MacOS, Linux and Windows).

2. Must have a Huawei phone with HMS 4.0.0.300 or later.

3. Must have a laptop or desktop with Android Studio, Jdk 1.8, SDK platform 26 and Gradle 4.6 and above installed.

4. Minimum API Level 24 is required.

5. Required EMUI 9.0.0 and later version devices.

**How to integrate HMS Dependencies**

- First register as Huawei developer and complete identity verification in Huawei developers website, refer to register a [Huawei ID](https://developer.huawei.com/consumer/en/doc/start/registration-and-verification-0000001053628148).
- Create a project in android studio, refer [Creating an Android Studio Project](https://developer.huawei.com/consumer/en/codelab/HMSPreparation/index.html#1).
- Generate a **SHA-256 certificate fingerprint**.
- To generate **SHA-256 certificate fingerprint**. On right-upper corner of android project click **Gradle**, choose **Project Name > Tasks > android**, and then click **signingReport**, as follows.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cehna4ejsr7c4wk8l4xj.png)

Note: Project Name depends on the user created name.
- [Create an App in AppGallery Connect](https://developer.huawei.com/consumer/en/codelab/HMSPreparation/index.html#0).

- Download the **agconnect-services.json** file from **App information**, copy and paste in android **Project **under **app **directory, as follows.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lwa2etgekah114mmovcs.png)

- Enter **SHA-256 certificate fingerprint** and click **Save **button, as follows.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jkmta6xb0vn9sxbimt1j.png)

- Add the below maven URL in **build.gradle(Project)** file under the repositories of **buildscript**, **dependencies** and **allprojects**, refer [Add Configuration](https://developer.huawei.com/consumer/en/codelab/HMSPreparation/index.html#6).
`maven { url 'http://developer.huawei.com/repo/' }
classpath 'com.huawei.agconnect:agcp:1.6.0.300'
`
- Add the below plugin and dependencies in **build.gradle(Module)** file.
`apply plugin: id 'com.huawei.agconnect'
apply plugin: id 'kotlin-kapt'
// Huawei AGC
implementation 'com.huawei.agconnect:agconnect-core:1.6.0.300'
// Room Database
implementation "androidx.room:room-runtime:2.4.2"
kapt "androidx.room:room-compiler:2.4.2"
implementation "androidx.room:room-ktx:2.4.2"
androidTestImplementation "androidx.room:room-testing:2.4.2"
// Lifecycle components
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1"
// Recyclerview
implementation 'androidx.recyclerview:recyclerview:1.2.1'`

- Now Sync the gradle.


**Let us move to development**

I have created a project on Android studio with empty activity let us start coding.

Create a **DataRecord.kt** class annotated with **@Entity** to create a table for each class.
`@Entity(tableName = "datarecords")
data class DataRecord(
    @PrimaryKey(autoGenerate = true)
    val id: Long,
    val name: String,
    val age: String,
    val phone: String
)`

Create a **DataRecordDao.kt** interface class annotated with @Dao and responsible for defining the methods that access the database.
`@Dao
interface DataRecordDao {

    @Query("SELECT * from datarecords")
    fun getall(): LiveData<List<DataRecord>>

    @Insert(onConflict = OnConflictStrategy.ABORT)
    suspend fun insert(item: DataRecord)

    @Query("SELECT * FROM datarecords WHERE datarecords.id == :id")
    fun get(id: Long): LiveData<DataRecord>

    @Update
    suspend fun update(vararg items: DataRecord)

    @Delete
    suspend fun delete(vararg items: DataRecord)

}`

Create a **AppRoomDatabase.kt** abstract class that extends **RoomDatabase **annotated with @Database to lists the entities contained in the database, and the DAOs which access them.
`@Database(entities = [DataRecord::class], version = 1)
abstract class AppRoomDatabase : RoomDatabase() {

    abstract fun datarecordDao(): DataRecordDao

    companion object {
        @Volatile
        private var INSTANCE: RoomDatabase? = null

        fun getDatabase(context: Context): AppRoomDatabase {
            val tempInstance = INSTANCE
            if (tempInstance != null) {
                return tempInstance as AppRoomDatabase
            }
            synchronized(this) {
                val instance = Room.databaseBuilder(context.applicationContext,AppRoomDatabase::class.java,
                    "datarecords_database").fallbackToDestructiveMigration()
                    .build()
                INSTANCE = instance
                return instance
            }
        }
    }

}`

Create a **Repository.kt** class to find the functions.
`class Repository(private val dataDao: DataRecordDao) {

    val allItems: LiveData<List<DataRecord>> = dataDao.getall()
    fun get(id: Long):LiveData<DataRecord> {
        return dataDao.get(id)
    }
    suspend fun update(item: DataRecord) {
        dataDao.update(item)
    }
    suspend fun insert(item: DataRecord) {
        dataDao.insert(item)
    }
    suspend fun delete(item: DataRecord) {
        dataDao.delete(item)
    }

}`

Create a **ViewModel.kt** class that extends **AndroidViewModel** and provides the **Repository **functions.
`class ViewModel(application: Application): AndroidViewModel(application) {

    private val repository: Repository
    val allItems: LiveData<List<DataRecord>>

    init {
        Log.d(TAG, "Inside ViewModel init")
        val dao = AppRoomDatabase.getDatabase(application).datarecordDao()
        repository = Repository(dao)
        allItems = repository.allItems
    }

    fun insert(item: DataRecord) = viewModelScope.launch {
        repository.insert(item)
    }

    fun update(item: DataRecord) = viewModelScope.launch {
        repository.update(item)
    }

    fun delete(item: DataRecord) = viewModelScope.launch {
        repository.delete(item)
    }

    fun get(id: Long) = repository.get(id)

}`

In the **EmployeesList.kt** activity to find the business logic for button click.
`class EmployeesList : AppCompatActivity() {

    private var buttonAddEmp: Button? = null
    private lateinit var myViewModel: ViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_employees_list)

        val recyclerView = recyclerview_tasks
        val adapter = EmpAdapter(this)
        recyclerView.adapter = adapter
        recyclerView.layoutManager = LinearLayoutManager(this)

        buttonAddEmp = findViewById(R.id.btn_add)
        buttonAddEmp!!.setOnClickListener(View.OnClickListener {
            val intent = Intent(this, AddEmployees::class.java)
            startActivity(intent)
        })

        myViewModel = ViewModelProvider(this)[ViewModel::class.java]
        myViewModel.allItems.observe(this, Observer { items ->
            items?.let { adapter.setItems(it) }
        })

    }

}`

Create a **EmpAdapter.kt** adapter class to hold the list.
`const val TAG = "EmpAdapter"

class EmpAdapter internal constructor (context: Context) : RecyclerView.Adapter<EmpAdapter.EmpRecordViewHolder>() {

    private val inflater: LayoutInflater = LayoutInflater.from(context)
    private var itemsList = emptyList<DataRecord>().toMutableList()
    private val onClickListener: View.OnClickListener

    init {
        onClickListener = View.OnClickListener { v ->
            val item = v.tag as DataRecord
            Log.d(TAG, "Setting onClickListener for item ${item.id}")
            val intent = Intent(v.context, AddEmployees::class.java).apply {
                putExtra(DATA_RECORD_ID, item.id)
            }
            v.context.startActivity(intent)
        }
    }

    inner class EmpRecordViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val itemId: TextView = itemView.findViewById(R.id.datarecord_viewholder_id)
        val itemName: TextView = itemView.findViewById(R.id.txt_name)
        val itemAge: TextView = itemView.findViewById(R.id.txt_age)
        val itemPhone: TextView = itemView.findViewById(R.id.txt_phone)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EmpRecordViewHolder {
        val itemView = inflater.inflate(R.layout.detail_list, parent, false)
        return EmpRecordViewHolder(itemView)
    }

    override fun onBindViewHolder(holder: EmpRecordViewHolder, position: Int) {
        val current = itemsList[position]
        // It will be referenced in the View.OnClickListener above
        holder.itemView.tag = current
        with(holder) {
            // Set UI values
            itemId.text = current.id.toString()
            // itemRecord.text = current.record
            itemName.text = current.name
            itemAge.text = current.age
            itemPhone.text = current.phone
            // Set handlers
            itemView.setOnClickListener(onClickListener)
        }
    }

    internal fun setItems(items: List<DataRecord>) {
        this.itemsList = items.toMutableList()
        notifyDataSetChanged()
    }

    override fun getItemCount() = itemsList.size

    companion object {
        const val DATA_RECORD_ID : String = "datarecord_id"
    }

}`

In the **AddEmployees.kt** activity to find the business logic create, update, read and delete functions.
`class AddEmployees : AppCompatActivity() {

    private lateinit var dataViewModel: ViewModel
    private var recordId: Long = 0L
    private var isEdit: Boolean = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_add_employees)

        dataViewModel = ViewModelProvider(this)[ViewModel::class.java]
        if (intent.hasExtra(DATA_RECORD_ID)) {
            recordId = intent.getLongExtra(DATA_RECORD_ID, 0L)
            dataViewModel.get(recordId).observe(this, Observer {
                val viewId = findViewById<TextView>(R.id.datarecord_id)
                val viewName = findViewById<EditText>(R.id.edt_name)
                val viewAge = findViewById<EditText>(R.id.edt_age)
                val viewPhone = findViewById<EditText>(R.id.edt_phone)
                if (it != null) {
                    // populate with data
                    viewId.text = it.id.toString()
                    viewName.setText(it.name)
                    viewAge.setText(it.age)
                    viewPhone.setText(it.phone)
                }
            })
            isEdit = true
        }

        val save = btn_save
        save.setOnClickListener { view ->
            val id = 0L
            val mName = edt_name.text.toString()
            val mAge = edt_age.text.toString()
            val mPhone = edt_phone.text.toString()
            if (mName.isBlank() ) {
                Toast.makeText(this, "Name is blank", Toast.LENGTH_SHORT).show()
            }
            else if (mAge.isBlank()) {
                Toast.makeText(this, "Age is blank", Toast.LENGTH_SHORT).show()
            }
            else if(mPhone.isBlank()) {
                Toast.makeText(this, "Phone is blank", Toast.LENGTH_SHORT).show()
            }
            else {
                val item = DataRecord( id = id, name = mName, age = mAge, phone = mPhone)
                dataViewModel.insert(item)
                finish()
            }
        }

        val update = btn_update
        update.setOnClickListener { view ->
            val id = datarecord_id.text.toString().toLong()
            val nName = edt_name.text.toString()
            val nAge = edt_age.text.toString()
            val nPhone = edt_phone.text.toString()
            if (nName.isBlank() && nAge.isBlank() && nPhone.isBlank()) {
                Toast.makeText(this, "Empty data is not allowed", Toast.LENGTH_SHORT).show()
            } else {
                val item = DataRecord(id = id, name = nName, age = nAge, phone = nPhone)
                dataViewModel.update(item)
                finish()
            }
        }

        val delete = btn_del
        delete.setOnClickListener {
            val id = datarecord_id.text.toString().toLong()
            val nName = edt_name.text.toString()
            val nAge = edt_age.text.toString()
            val nPhone = edt_phone.text.toString()
            val item = DataRecord( id =id, name = nName, age = nAge, phone = nPhone)
            dataViewModel.delete(item)
            finish()
        }

    }

}`

In the **activity_employees_list.xml** we can create the UI screen.
`<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="vertical"
    tools:context=".RoomDatabase.EmployeesList">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="28dp"
        android:layout_gravity="center_horizontal"
        android:text="Employees List"
        android:textColor="#B602D5"
        android:textSize="24sp"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:gravity="center"
        android:padding="10dp"
        android:layout_marginTop="40dp"
        android:clickable="true" >
        <ImageView
            android:layout_width="100dp"
            android:layout_height="50dp"
            android:layout_marginRight="20dp"
            android:drawableTint="@color/design_default_color_primary"
            android:src="@drawable/add_emp"/>
        <Button
            android:id="@+id/btn_add"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:textSize="18dp"
            android:textAllCaps="false"
            android:text="Add Employees"/>
    </LinearLayout>

</LinearLayout>`

In the **activity_add_employees.xml** we can create the UI screen.
`<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="vertical"
    tools:context=".RoomDatabase.AddEmployees">

    <TextView
        android:id="@+id/datarecord_id"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="15dp"
        android:layout_gravity="left"
        android:hint="id "
        android:padding="5dp"
        android:layout_marginLeft="10dp"
        android:textSize="18sp"
        android:textColor="@color/black" />
    <EditText
        android:id="@+id/edt_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="15dp"
        android:layout_gravity="left"
        android:hint="Name "
        android:padding="5dp"
        android:layout_marginLeft="10dp"
        android:textSize="18sp"
        android:textColor="@color/black" />
    <EditText
        android:id="@+id/edt_age"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="15dp"
        android:layout_gravity="left"
        android:hint="Age "
        android:padding="5dp"
        android:layout_marginLeft="10dp"
        android:textSize="18sp"
        android:textColor="@color/black" />
    <EditText
        android:id="@+id/edt_phone"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="15dp"
        android:layout_gravity="left"
        android:hint="Phone "
        android:textSize="18sp"
        android:padding="5dp"
        android:layout_marginLeft="10dp"
        android:textColor="@color/black" />

    <Button
        android:id="@+id/btn_save"
        android:layout_width="150dp"
        android:layout_height="50dp"
        android:layout_gravity="center_horizontal"
        android:textSize="18dp"
        android:textAllCaps="false"
        android:layout_marginTop="5dp"
        android:text="Save"/>
    <Button
        android:id="@+id/btn_update"
        android:layout_width="150dp"
        android:layout_height="50dp"
        android:layout_gravity="center_horizontal"
        android:textSize="18dp"
        android:textAllCaps="false"
        android:layout_marginTop="5dp"
        android:text="Update"/>
    <Button
        android:id="@+id/btn_del"
        android:layout_width="150dp"
        android:layout_height="50dp"
        android:layout_gravity="center_horizontal"
        android:textSize="18dp"
        android:textAllCaps="false"
        android:layout_marginTop="5dp"
        android:text="Delete"/>
</LinearLayout>`

In the **detail_list.xml** we can create the UI screen for customized items.
`<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView  xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="10dp">
        <TextView
            android:id="@+id/datarecord_viewholder_id"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="5dp"
            android:layout_marginTop="10dp"
            android:textSize="18sp"
            android:text="id: "
            android:textColor="@color/purple_500"
            android:textStyle="bold" />
        <TextView
            android:id="@+id/txt_name"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="18sp"
            android:layout_marginTop="10dp"
            android:textStyle="bold"
            android:textColor="@color/purple_500"
            android:text="name: " />
        <TextView
            android:id="@+id/txt_age"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="18sp"
            android:layout_marginTop="10dp"
            android:textStyle="bold"
            android:textColor="@color/purple_500"
            android:text="age: " />
        <TextView
            android:id="@+id/txt_phone"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="18sp"
            android:layout_marginTop="10dp"
            android:textStyle="bold"
            android:textColor="@color/purple_500"
            android:text="phone: " />
    </LinearLayout>

</androidx.cardview.widget.CardView>`

**Demo**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/svdpuijg4l0zrq1wz5xw.gif)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wx9cwljygypnnjkc2hzc.gif)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e5dxyzbo1n00zx3g6gxj.jpg)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/htydytnjbblvrnwaoq3h.jpg)

**Tips and Tricks**

1. Make sure you are already registered as Huawei developer.
2. Set minSDK version to **24** or later, otherwise you will get **AndriodManifest **merge issue.
3. Make sure you have added the agconnect-services.json file to app folder.
4. Make sure you have added **SHA-256 fingerprint** without fail.
5. Make sure all the dependencies are added properly.

**Conclusion**

In this article, we have learned the integration of **Room database**. We, have learnt about the room database and its components such as **DAO**, **Entity** and **Database**. How to **create**, **read**, **update** and **delete** the content in room database and which helps the user to access the data when they are in offline.

I hope you have read this article. If you found it is helpful, please provide likes and comments.

**Reference**

[Room Database](https://developer.android.com/training/data-storage/room)