Jetpack Room first try

Jetpack Room is an official Android framework for manipulating the App’s SQLite database. The overall use is relatively simple.

Write a simple Hello World for managing users.

The environment

Android Studio 4.2 Kotlin 1.4.31 Kotlin Coroutines 1.5.0 Jetpack Room 2.3.0

  • Depend on the add
def room_version = "2.3.0"

/ / core library
implementation "androidx.room:room-runtime:$room_version"
// Use kapt(Kotlin Annotation Processing tool) to process annotations
kapt "androidx.room:room-compiler:$room_version"
// Use KSP (Kotlin Symbolic Processing) to process annotations (faster)
ksp("androidx.room:room-compiler:$room_version")

// Room coroutine support
implementation("androidx.room:room-ktx:$room_version")

// Room RxJava2 support
implementation "androidx.room:room-rxjava2:$room_version"

// Room RxJava3 support
implementation "androidx.room:room-rxjava3:$room_version"

// Room Guava support
implementation "androidx.room:room-guava:$room_version"

/ / test Room
testImplementation("androidx.room:room-testing:$room_version")

/ / coroutines
def coroutine_version = "1.5.0"
// Coroutine core library
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutine_version"
// Provide an Android-specific scheduler
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutine_version"
Copy the code

Why import the coroutine library? Since Room stipulates that database operations cannot be performed on the main thread, coroutines are introduced to handle this problem. In addition, Room supports coroutines and can be used in combination with them.

Three components of Room

Room consists of three components:

  • DataBase

The database holding class, which inherits from the RoomDatabase class, holds zero or more abstract methods to get the DAO

  • Entity

A data entity class that defines the structure of a database table.

  • DAO

Data manipulation interface, which defines methods for manipulating data.

Hello World

So let’s see how to write Hello World.

First of all, you have to define an entity, let’s call it User.

// Use the @Entity annotation to identify an Entity component
@Entity
data class User(
    // Primary key, autoGenerate=true
    @PrimaryKey(autoGenerate = true)
    val id: Long = 0./ / column information
    @ColumnInfo(name = "name")
    val name: String="".@ColumnInfo(name = "gender")
    val gender: String="".@ColumnInfo(name = "age")
    val age: String=""
)
Copy the code

If you have an entity, you must have a method to manipulate the entity, so define a UserDao

// Identifies a DAO component
@Dao
interface UserDao {
    Vararg indicates that multiple users can be inserted at once
    / / onConflict logo insert encounter conflict resolution strategy, optional REPLACE, the ROLLBACK and ABORD, FAIL, IGNORE, simple and clear, the words can understand
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insertUser(vararg users: User)
    
    // Delete the user's annotations
    @Delete
    fun deleteUser(user: User)

    // Delete all user annotations. Use the Query annotation to execute custom SQL statements
    @Query("DELETE FROM user")
    fun deleteAll(a)
    
    // Update the user's annotations. You can also set the conflict policy
    @Update(onConflict = OnConflictStrategy.REPLACE)
    fun updateUser(user: User)
    
    // Query all users
    @Query("SELECT * FROM user")
    fun queryAllUser(a): List<User>

    // Query the user by name. :name corresponds to the name parameter in the method
    @Query("SELECT * FROM USER WHERE name=:name")
    fun queryByName(name: String): User?
}
Copy the code

With DAO, how to use DAO methods to operate the database, also need to define a UserDataBase abstract class

// @database identifies a DataBase component. Entities can be passed as entities owned by the DataBase. Version is used to update the DataBase version
@Database(entities =[User::class,],version = 1)
abstract class UserDataBase :RoomDatabase() {// Define an abstract method to retrieve the UserDao defined above
    abstract fun userDao(a):UserDao
}
Copy the code

Now that you have all three components together, it’s time to actually get started.

First, you need to construct a UserDataBase object using Room

private val dataBase: UserDataBase by lazy {
    // Pass in the context, the UserDataBase, and the name of the database. DatabaseBuilder can also set other things, such as importing the database from a file, timeout, and so on.
    Room.databaseBuilder(this, UserDataBase::class.java, "user.db").build()
}
Copy the code

Since Hell World is only used in MainActivity, use the singleton lazy mode to create a Hell World in MainActivity. Normally, use the singleton lazy mode to create a Hell World, because the frequent creation of a DataBase is expensive.

With the dataBase object, you can directly access the UserDao to manipulate the data, starting with the simplest additions, deleters, modifications and queries. First write a way to create users:

private fun createUser(a): User {
    val name = binding.etName.text.toString()
    val gender = binding.etGender.text.toString()
    val age = binding.etAge.text.toString()
    return User(name = name, gender = gender, age = age)
}
Copy the code

Simply read the data in the input box and construct a user, using a ViewBinding to retrieve the component.

  • increase
binding.btnInsert.setOnClickListener {
    launch {
        // The database cannot be operated on the main thread, so the IO thread is used here
        withContext(Dispatchers.IO) {
            dataBase.userDao().insertUser(createUser())
        }
        // Update the UI after insertion
        updateView()
        Toast.makeText(this@MainActivity."Insert success", Toast.LENGTH_SHORT).show()
    }
}
Copy the code
  • delete
binding.btnDelete.setOnClickListener {
    launch {
        withContext(Dispatchers.IO) {
            dataBase.userDao().deleteUser(createUser().copy(id=getUid()))
        }
        updateView()
        Toast.makeText(this@MainActivity."Delete success", Toast.LENGTH_SHORT).show()
    }
}
Copy the code
  • Delete all
binding.btnDelete.setOnLongClickListener {
    launch {
        withContext(Dispatchers.IO) {
            dataBase.userDao().deleteAll()
        }
        updateView()
        Toast.makeText(this@MainActivity."Delete all success", Toast.LENGTH_SHORT).show()
    }
    true
}
Copy the code
  • change
// Get the UID in the input box
private fun getUid(a): Long {
    return try {
        binding.etId.text.toString().toLong()
    } catch (e: Exception) {
        0L
    }
}

binding.btnUpdate.setOnClickListener {
    launch {
        // Create a new User using the copy method and set its ID
        withContext(Dispatchers.IO) {
            dataBase.userDao().updateUser(createUser().copy(id=getUid()))
        }
        updateView()
        Toast.makeText(this@MainActivity."Update success", Toast.LENGTH_SHORT).show()
    }
}
Copy the code
  • check
binding.btnQuery.setOnClickListener {
    launch {
        // Query the user by name. WithContext returns the last line, so you can get the user information here
        valuser = withContext(Dispatchers.IO) { dataBase.userDao().queryByName(binding.etName.text.toString()) } user? .let { binding.tvInfo.text ="${it.id}-${it.name} ${it.gender} ${it.age}"
        }
        Toast.makeText(this@MainActivity."Query success", Toast.LENGTH_SHORT).show()
    }
}
Copy the code

So, if it’s super simple, some of Jetpack’s frameworks are really good for lazy people. Finally put the overall source code:

  • MainACtivity
package com.ricky.room

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.room.Room
import com.ricky.room.databinding.ActivityMainBinding
import kotlinx.coroutines.*
import java.lang.Exception

class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
    private val binding: ActivityMainBinding by lazy { ActivityMainBinding.inflate(layoutInflater) }
    private val dataBase: UserDataBase by lazy {
        Room.databaseBuilder(this, UserDataBase::class.java, "user.db").build()
    }

    override fun onCreate(savedInstanceState: Bundle?). {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)

        binding.btnInsert.setOnClickListener {
            launch {
                withContext(Dispatchers.IO) {
                    dataBase.userDao().insertUser(createUser())
                }
                updateView()
                Toast.makeText(this@MainActivity."Insert success". Toast.LENGTH_SHORT).show() } } binding.btnDelete.setOnClickListener { launch { withContext(Dispatchers.IO) { dataBase.userDao().deleteUser(createUser().copy(id=getUid())) } updateView() Toast.makeText(this@MainActivity."Delete success". Toast.LENGTH_SHORT).show() } } binding.btnDelete.setOnLongClickListener { launch { withContext(Dispatchers.IO) { dataBase.userDao().deleteAll() } updateView() Toast.makeText(this@MainActivity."Delete all success", Toast.LENGTH_SHORT).show()
            }
            true
        }
        binding.btnUpdate.setOnClickListener {
            launch {
                withContext(Dispatchers.IO) {
                    dataBase.userDao().updateUser(createUser().copy(id=getUid()))
                }
                updateView()
                Toast.makeText(this@MainActivity."Update success", Toast.LENGTH_SHORT).show()
            }
        }
        binding.btnQuery.setOnClickListener {
            launch {
                valuser = withContext(Dispatchers.IO) { dataBase.userDao().queryByName(binding.etName.text.toString()) } user? .let { binding.tvInfo.text ="${it.id}-${it.name} ${it.gender} ${it.age}"
                }
                Toast.makeText(this@MainActivity."Query success", Toast.LENGTH_SHORT).show()
            }
        }
        updateView()
    }

    private fun getUid(a): Long {
        return try {
            binding.etId.text.toString().toLong()
        } catch (e: Exception) {
            0L}}private fun createUser(a): User {
        val name = binding.etName.text.toString()
        val gender = binding.etGender.text.toString()
        val age = binding.etAge.text.toString()
        return User(name = name, gender = gender, age = age)
    }

    private fun updateView(a) {
        launch {
            val text = withContext(Dispatchers.IO) {
                val users = dataBase.userDao().queryAllUser()
                users.joinToString("\n") { "${it.id}-${it.name} ${it.gender} ${it.age}" }
            }
            binding.tvInfo.text = text
        }
    }
}
Copy the code
  • activity_main.xml
<? 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">

    <TextView
        android:id="@+id/tv_info"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toTopOf="@+id/guideline2"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_begin="435dp" />

    <EditText
        android:id="@+id/et_id"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginHorizontal="10dp"
        android:hint="ID"
        app:layout_constraintEnd_toStartOf="@id/et_name"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/guideline2" />

    <EditText
        android:id="@+id/et_name"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginHorizontal="10dp"
        android:hint="Name"
        app:layout_constraintEnd_toStartOf="@id/et_gender"
        app:layout_constraintStart_toEndOf="@id/et_id"
        app:layout_constraintTop_toBottomOf="@id/guideline2" />

    <EditText
        android:id="@+id/et_gender"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginHorizontal="10dp"
        android:hint="Gender"
        app:layout_constraintEnd_toStartOf="@id/et_age"
        app:layout_constraintStart_toEndOf="@id/et_name"
        app:layout_constraintTop_toBottomOf="@id/guideline2" />

    <EditText
        android:id="@+id/et_age"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginHorizontal="10dp"
        android:hint="Age"
        android:inputType="number"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@id/et_gender"
        app:layout_constraintTop_toBottomOf="@id/guideline2" />

    <Button
        android:id="@+id/btn_insert"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="insert"
        app:layout_constraintBottom_toTopOf="@+id/btn_update"
        app:layout_constraintEnd_toStartOf="@id/btn_delete"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@+id/guideline2" />

    <Button
        android:id="@+id/btn_delete"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="delete"
        app:layout_constraintBottom_toTopOf="@id/btn_query"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@id/btn_insert"
        app:layout_constraintTop_toBottomOf="@id/guideline2" />

    <Button
        android:id="@+id/btn_update"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="14dp"
        android:text="update"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/btn_query"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/btn_insert" />

    <Button
        android:id="@+id/btn_query"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="query"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@id/btn_update"
        app:layout_constraintTop_toBottomOf="@id/btn_delete" />
</androidx.constraintlayout.widget.ConstraintLayout>
Copy the code