JetPack Room的基本使用

JetPack Room的基本使用

今天和大家一起来学Jetpack库中Room数据库的使用,之前也使用过Litepal、greenDao的第三方ORM(对象关系映射)数据库 。以后准备在项目中使用JetPack全家桶,Room是google官方推出的ORM数据库,势必要学习一波。

技术栈

本项目中用到的技术栈

  • kotlin
  • 协程
  • viewbinding
项目前期准备
java 复制代码
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
// room需要
apply plugin: 'kotlin-kapt'
android {
   	...
    // viewBinding开启,注意必须是android studio3.6以上
    viewBinding {
        enabled = true
    }
}

dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation 'androidx.core:core-ktx:1.1.0'
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
    // room所需依赖    
    implementation "androidx.room:room-runtime:2.2.2"
    kapt "androidx.room:room-compiler:2.2.2"
    implementation "androidx.room:room-ktx:2.2.2"
    // 协程
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'
    // 列表主要是用于显示数据库的数据
    implementation "androidx.recyclerview:recyclerview:1.1.0"
}

这里主要是项目中用到的各种依赖

Room介绍
markdown 复制代码
Room 包含 3 个主要组件:

- 数据库:包含数据库持有者,并作为应用已保留的持久关系型数据的底层连接的主要接入点。
  使用 @Database 注释的类应满足以下条件:
  - 是扩展 RoomDatabase 的抽象类。
  - 在注释中添加与数据库关联的实体列表。
  - 包含具有 0 个参数且返回使用 @Dao 注释的类的抽象方法。
  在运行时,您可以通过调用 Room.databaseBuilder() 或 Room.inMemoryDatabaseBuilder() 获取 Database 的实例。
  
- Entity:表示数据库中的表。
- DAO:包含用于访问数据库的方法。

应用使用 Room 数据库来获取与该数据库关联的数据访问对象 (DAO)。然后,应用使用每个 DAO 从数据库中获取实体,然后再将对这些实体的所有更改保存回数据库中。 最后,应用使用实体来获取和设置与数据库中的表列相对应的值。
1.创建实体类
kotlin 复制代码
package com.example.dataroom

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity
data class User(var name:String,var age:Int) {
    @PrimaryKey(autoGenerate = true)
    var id:Long = 0
}

这里我们创建一个User的实体类,并添加@Entity的注解,添加主键并且设置为自增。

2.创建UserDao接口
kotlin 复制代码
package com.example.dataroom

import androidx.room.*

@Dao
interface UserDao {
    @Insert
    fun insertUser(user: List<User>)

    @Query("select * from User")
    fun queryAllUser(): List<User>

    @Query("select * from User where age > :age")
    fun sectionUser(age: Int): List<User>

    @Update
    fun updateUser(user: List<User>)

    @Delete
    fun deleteUser(user: User)

    @Query("delete from User")
    fun deleteAllUser()
}

创建UserDao接口,并且添加@Dao注册,可以看到这里面就是执行对表增、删、改、查。有@Insert、 @Query、@Update、@Delete注解。如果你sql语句比较熟练,将会事半功倍。

3.编写database
kotlin 复制代码
package com.example.dataroom

import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase

@Database(version = 1, entities = [User::class])
abstract class AppDataBase : RoomDatabase() {
    abstract fun userDao(): UserDao

    companion object {
        private var instance: AppDataBase? = null

        @Synchronized
        fun getDataBase(context: Context): AppDataBase {
            instance?.let {
                return it
            }
            return Room.databaseBuilder(context, AppDataBase::class.java, "dataroom")
                .build().apply {
                    instance = this
                }
        }
    }
}

这里我们创建了一个AppDataBase的抽象类,并添加@DataBase注解其中version指定当前数据库的版本,entites指定实体类,多个实体类用逗号隔开。我们又定义了一个获取UserDao的抽象方法,通过companion object定义内部的静态方法,用于获取AppDataBase的实例,这里使用了单例模式,保证频繁获取AppDataBase实例的时候,AppDataBase只创建一次。

4.编写页面
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">
    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolBar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        app:layout_constraintTop_toTopOf="parent"
        android:background="@color/colorPrimary"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
        app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
        />
    <Button
        android:id="@+id/insertUserBtn"
        android:layout_width="0dp"
        app:layout_constraintHorizontal_weight="1"
        android:layout_height="wrap_content"
        android:text="插入用户"
        app:layout_constraintTop_toBottomOf="@id/toolBar"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@id/deleteUserBtn"
        />
    <Button
        android:id="@+id/deleteUserBtn"
        android:layout_width="0dp"
        app:layout_constraintHorizontal_weight="1"
        android:layout_height="wrap_content"
        android:text="删除用户"
        app:layout_constraintTop_toTopOf="@id/insertUserBtn"
        app:layout_constraintLeft_toRightOf="@id/insertUserBtn"
        app:layout_constraintRight_toLeftOf="@id/updateUserBtn"
        />
    <Button
        android:id="@+id/updateUserBtn"
        android:layout_width="0dp"
        app:layout_constraintHorizontal_weight="1"
        android:layout_height="wrap_content"
        android:text="修改用户"
        app:layout_constraintTop_toTopOf="@id/insertUserBtn"
        app:layout_constraintLeft_toRightOf="@id/deleteUserBtn"
        app:layout_constraintRight_toLeftOf="@id/queryUserBtn"
        />
    <Button
        android:id="@+id/queryUserBtn"
        android:layout_width="0dp"
        app:layout_constraintHorizontal_weight="1"
        android:layout_height="wrap_content"
        android:text="查询用户"
        app:layout_constraintTop_toTopOf="@id/insertUserBtn"
        app:layout_constraintLeft_toRightOf="@id/updateUserBtn"
        app:layout_constraintRight_toRightOf="parent"
        />
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintTop_toBottomOf="@id/insertUserBtn"
        />
</androidx.constraintlayout.widget.ConstraintLayout>
5.编写逻辑
kotlin 复制代码
package com.example.dataroom

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.dataroom.databinding.ActivityMainBinding
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.*

class MainActivity : AppCompatActivity() {
  private val tag = "MainActivity"
  private lateinit var activityMainBinding:ActivityMainBinding
  private  val coroutineScope by lazy {
        CoroutineScope(Dispatchers.Main)
  }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        activityMainBinding= ActivityMainBinding.inflate(LayoutInflater.from(this))
        setContentView(activityMainBinding.root)
        activityMainBinding.toolBar.title = "JetPack Room数据库"
        activityMainBinding.toolBar.setNavigationIcon(R.drawable.ic_baseline_arrow_back_24)
        setSupportActionBar(toolBar)
        activityMainBinding.toolBar.setNavigationOnClickListener {
            finish()
        }
        activityMainBinding.recyclerView.layoutManager = LinearLayoutManager(this)
        initData()
    }

    private fun initData() {
        // 批量插入用户
        activityMainBinding.insertUserBtn.setOnClickListener {
            val users = ArrayList<User>()
            for (i in 0..10){
                users.add( User("张三${i}",i))
            }
            coroutineScope.launch {
                val deffer = async(Dispatchers.IO) {
                    AppDataBase.getDataBase(this@MainActivity).userDao().insertUser(users)
                }
            }
        }

        // 批量删除用户
        activityMainBinding.deleteUserBtn.setOnClickListener {
            coroutineScope.launch {
                async(Dispatchers.IO) {
                    AppDataBase.getDataBase(this@MainActivity).userDao().deleteAllUser()
                }
            }
        }

        // 批量修改数据
        activityMainBinding.updateUserBtn.setOnClickListener {
            coroutineScope.launch {
                async(Dispatchers.IO) {
                   var users = AppDataBase.getDataBase(this@MainActivity).userDao().queryAllUser()
                   for (index in users.indices){
                      var user = users[index]
                       user.name = "李四$index"
                       user.age = index+24
                   }
                  AppDataBase.getDataBase(this@MainActivity).userDao().updateUser(users)
                }
            }
        }

        // 批量查询用户
        activityMainBinding.queryUserBtn.setOnClickListener {
            coroutineScope.launch {
               val deffer = async(Dispatchers.IO) {
                    AppDataBase.getDataBase(this@MainActivity).userDao().queryAllUser()
                }
               val users = deffer.await()
               activityMainBinding.recyclerView.adapter = UserAdapter(users)
            }
        }
    }
}

这里没什么好讲的就是简单的增、删、改、查。每一个布局文件都会生成对应的Biding类,通过ActivityMainBinding.inflate(LayoutInflater.from(this))设置进来,再赋值给setContentView即可。

6.列表页适配器编写
kotlin 复制代码
package com.example.dataroom

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.dataroom.databinding.UserItemBinding

class UserAdapter(var users:List<User>): RecyclerView.Adapter<UserAdapter.UserViewHolder>() {

    inner class UserViewHolder(userItemBinding: UserItemBinding):RecyclerView.ViewHolder(userItemBinding.root) {
        val userNameTv = userItemBinding.userNameTv
        val userAgeTv = userItemBinding.userAgeTv
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
      return UserViewHolder(UserItemBinding.inflate(LayoutInflater.from(parent.context),parent,false))
    }

    override fun getItemCount(): Int = users.size

    override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
       var user = users[position]
        holder.userNameTv.text = user.name
        holder.userAgeTv.text = user.age.toString()
    }
}
7.数据库的升级

1.比如我又增加了一个Book类,并且在User类中增加了一个hobby爱好的字段

kotlin 复制代码
package com.example.dataroom

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity
data class Book(val bookName:String,val author:String, val price:Double){
    @PrimaryKey(autoGenerate = true)
    var id:Long=0
}	
kotlin 复制代码
package com.example.dataroom

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity
data class User(var name:String,var age:Int,var hobby:String) {
    @PrimaryKey(autoGenerate = true)
    var id:Long = 0
}

2.修改AppDataBase类

kotlin 复制代码
package com.example.dataroom

import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase

// 修改版本, 添加新增的实体类的Class
@Database(version = 2, entities = [User::class,Book::class])
abstract class AppDataBase : RoomDatabase() {
    abstract fun userDao(): UserDao

    companion object {
        private var instance: AppDataBase? = null

        //编写增加实体类和修改类的两条sql语句
        private val MIGRATION_1_2 = object : Migration(1, 2) {
            override fun migrate(database: SupportSQLiteDatabase) {
                database.execSQL("alter table User add column hobby text not null default 'unknown'")
                database.execSQL("create table Book(id integer primary key autoincrement not null,bookName text not null,author text not null,price real not null)")
            }
        }

        @Synchronized
        fun getDataBase(context: Context): AppDataBase {
            instance?.let {
                return it
            }
            return Room.databaseBuilder(context, AppDataBase::class.java, "dataroom")
            	//执行更新
                .addMigrations(MIGRATION_1_2)
                .build().apply {
                    instance = this
                }
        }
    }
}

需要注意的是,如果是删除原来类中多余的字段,需要新建一个临时表将数据迁移过去,再将原来的表删除,再将这个临时表重命名为原来表的名字,这个方法可以自行百度。

8.总结

总的来说Room和greenDao很像,不过room更多的是原生的sql语句操作,所以需要对sql语句有一定的功底。还有就是感觉数据库升级功能不是很友好,频繁的升级我要不断的编写升级语句,而且这里 .addMigrations(MIGRATION_1_2,MIGRATION_2_3)一定是追加的操作,希望google可以后续改进一下。

相关推荐
shenshizhong1 天前
Compose + Mvi 架构的玩android 项目,请尝鲜
android·架构·android jetpack
alexhilton5 天前
学会在Jetpack Compose中加载Lottie动画资源
android·kotlin·android jetpack
ljt27249606618 天前
Compose笔记(六十一)--SelectionContainer
android·笔记·android jetpack
QING6188 天前
Jetpack Compose 中的 ViewModel 作用域管理 —— 新手指南
android·kotlin·android jetpack
惟恋惜9 天前
Jetpack Compose 的状态使用之“界面状态”
android·android jetpack
喜熊的Btm9 天前
探索 Kotlin 的不可变集合库
kotlin·android jetpack
惟恋惜9 天前
Jetpack Compose 界面元素状态(UI Element State)详解
android·ui·android jetpack
惟恋惜9 天前
Jetpack Compose 多页面架构实战:从 Splash 到底部导航,每个 Tab 拥有独立 ViewModel
android·ui·架构·android jetpack
alexhilton11 天前
Jetpack Compose 2025年12月版本新增功能
android·kotlin·android jetpack