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可以后续改进一下。

相关推荐
帅次10 小时前
Android CoordinatorLayout:打造高效交互界面的利器
android·gradle·android studio·rxjava·android jetpack·androidx·appcompat
IAM四十二2 天前
Jetpack Compose State 你用对了吗?
android·android jetpack·composer
Wgllss4 天前
那些大厂架构师是怎样封装网络请求的?
android·架构·android jetpack
x02421 天前
Android Room(SQLite) too many SQL variables异常
sqlite·安卓·android jetpack·1024程序员节
alexhilton24 天前
深入理解观察者模式
android·kotlin·android jetpack
Wgllss24 天前
花式高阶:插件化之Dex文件的高阶用法,极少人知道的秘密
android·性能优化·android jetpack
上官阳阳1 个月前
使用Compose创造有趣的动画:使用Compose共享元素
android·android jetpack
沐言人生1 个月前
Android10 Framework—Init进程-15.属性变化控制Service
android·android studio·android jetpack
IAM四十二1 个月前
Android Jetpack Core
android·android studio·android jetpack
王能1 个月前
Kotlin真·全平台——Kotlin Compose Multiplatform Mobile(kotlin跨平台方案、KMP、KMM)
android·ios·kotlin·web·android jetpack·kmp·kmm