JetPack开发笔记:Room数据库的详解与基础使用

Posted Tai_Monster

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JetPack开发笔记:Room数据库的详解与基础使用相关的知识,希望对你有一定的参考价值。

JetPack开发笔记:Room数据库的详解与基础使用

简介:

android开发中我们常常会用到持久化数据保存手段,即将一些数据保存到本地。
其中比较常用的有两种:

  • 1.通过SharedPreferences这种轻量化手段,利用键值对存储一些关系并不复杂的数据。
  • 2.通过Android内置的SQLite数据库进行关系较为复杂的数据的存储。

Android提供了API帮助我们操作SQLite数据库,但是这种方法较为复杂。而利用JetPack中
的Room数据库,我们可以较为方便地操作SQLite数据库。

Room 持久性库在 SQLite 上提供了一个抽象层,以便在充分利用 SQLite 的强大功能的同时,能够流畅地访问数据库。具体来说,Room 具有以下优势:

  • 1.针对 SQL 查询的编译时验证。
  • 2.可最大限度减少重复和容易出错的样板代码的方便注解。
  • 3.简化了数据库迁移路径。

出于这些方面的考虑,现在Google官方已经建议我们使用Room,而不是直接使用SQLite
API

添加依赖:

我们需要在应用的build.gradle文件下添加几行依赖:

implementation 'androidx.room:room-common:2.3.0'
implementation 'androidx.room:room-runtime:2.4.3'
annotationProcessor "androidx.room:room-compiler:2.4.3"  

Room数据库的三个主要组件:

Room的三个主要组件分别对应SQLite数据库的数据库本身,表以及对数据库的操作:

    1. 数据库类,用于保存数据库并作为应用持久性数据底层连接的主要访问点。
      对应数据库本身。
    1. 数据实体,用于表示应用的数据库中的表。
      对应数据库中的表。
    1. 数据访问对象 (DAO),提供您的应用可用于查询、更新、插入和删除数据库中的数据的方法。
      对应具体的操作方法

在具体使用Room时,我们也需要具体实现这三个类,以帮助Room数据库创建,读写数据。

开始使用Room数据库

1.定义实体类

上面说到,数据实体对应的是数据库中的表,那么数据实体中的字段就对应的是表中每一列的数据,例如:

@Entity
public class User 
    @PrimaryKey
    public int uid;

    @ColumnInfo(name = "firstName")
    public String firstName;

    @ColumnInfo(name = "lastName")
    public String lastName;

    @ColumnInfo(name = "score")
    public int score;
        
        

此示例就实现了一个简单的数据实体类,对应的就是一张具有四列,n行的数据表:

在定义数据实体类时,我们需要加上@Entity注解,表示该类为一个数据实体类。默认情况下,Room将类名用作数据库表的名称,如果我们希望自定义表名,可以 @Entity注解的tableName 属性。
比如,我们修改上述代码为:

 @Entity(tableName = "MyTable")
 public class User 
    @PrimaryKey
    public int uid;

    @ColumnInfo(name = "firstName")
    public String first_Name;

    @ColumnInfo(name = "lastName")
    public String last_Name;

    @ColumnInfo(name = "score")
    public int score;
        
        

此时,表的名称就为"MyTable"了。

类似的,默认情况下Room也会将每一个字段的名称用作数据库表中列的名称。我们上述代码中默认的列名就分别为:first_Name,last_Name,score。但我们也可以将@ColumnInfo注解添加到该字段并设置name 属性,比如我们设置了每一个变量的@ColumnInfo注解的name属性,这样数据库表中的每一列名称就是我们设置的name属性了。

另外,每一张数据表也总会有一个主键,对应到数据实体中我们需要给对应到主键的字段加上@PrimaryKey注解,如果我们需要Room为实体实例分配自动ID,可以将@PrimaryKey的autoGenerate属性设为true。

定义复合主键:

如果您需要通过多个列的组合对实体实例进行唯一标识,则可以通过列出 @Entity 的 primaryKeys 属性中的以下列定义一个复合主键:

    @Entity(primaryKeys = "firstName", "lastName")
        public class User 
        public String firstName;
        public String lastName;
        

忽略字段:

如果我们有时想忽略掉实体类中的一些字段,让其不被存入Room数据库表中,我们可以使用@Ignore注解为这些字段添加注解,官方示例:

    @Entity
    public class User 
        @PrimaryKey
        public int id;

        public String firstName;
        public String lastName;

        @Ignore
        Bitmap picture;
               

创建数据访问对象(DAO):

官方简介:当您使用 Room 持久性库存储应用的数据时,您可以通过定义数据访问对象 (DAO) 与存储的数据进行交互。每个 DAO 都包含一些方法,这些方法提供对应用数据库的抽象访问权限。在编译时,Room 会自动为您定义的 DAO 生成实现。

我们可以用接口或者抽象类来定义DAO,一般我们使用的是接口(interface),切我们需要在类前加上@DAO注解。 一个具体的DAO中定义了一个或多个方法来实现对数据库表的增删改查的操作。

插入操作

借助 @Insert注释,我们可以定义将其参数插入到数据库中的相应表中的方法。我们无需实现具体的操作,而只需要定义方法的形式即可。实例:

@Dao
public interface UserDao 
    @Insert
    public void insertUsers(User... users);

    @Insert
    public void insertBothUsers(User user1, User user2);

    @Insert
    public void insertUsersAndFriends(User user, List<User> friends);

@Insert方法的每个参数必须是带有**@Entity注解**的Room数据实体类的实例或数据实体类实例的集合。调用 @Insert 方法时,Room 会将每个传递的实体实例插入到相应的数据库表中。

如果 @Insert 方法接收单个参数,则会返回 long 值,这是插入项的新 rowId。如果参数是数组或集合,则该方法应改为返回由 long 值组成的数组或集合,并且每个值都作为其中一个插入项的 rowId。

更新操作

借助@Update注释,我们可以实现更新数据库表中特定的行的方法。@Update 方法接受数据实体实例作为参数。 官方示例:

@Dao
public interface UserDao 
    @Update
    public void updateUsers(User... users);

需要注意的是,Room是根据主键来判断哪一行数据需要进行更新的,当传入的实体的主键与数据库表中的某一行的主键相匹配时,Room才会进行修改,否则将不会进行修改。@Update 方法可以选择性地返回 int 值,该值指示成功更新的行数。

删除操作

借助@Delete注释
删除操作与更新操作也相类似,也是根据传入实体的主键来匹配需要删除的数据行。
官方示例:

@Dao
public interface UserDao 
    @Delete
    public void deleteUsers(User... users);

查询操作

借助@Query注释
以下代码定义了一个方法,该方法使用简单的 SELECT 查询返回数据库中的所有 User 对象,即查询User表中的所有数据:

    @Query("SELECT * FROM user")//SQLite中不区分大小写
    public User[] loadAllUsers();   

简单的按参数查询:具体的查询条件有以下几种:

    1. 大于 或 小于 或 等于,对应的是 > , < , =

       @Query("SELECT * FROM user WHERE age > :minAge")
       public User[] loadAllUsersOlderThan(int minAge);
      
    1. 相等 可以使用LIKE 关键字

       @Query("SELECT * FROM user WHERE first_name LIKE :search " +
           "OR last_name LIKE :search")
       public List<User> findUserWithName(String search);
      
    1. 条件在一个区间之内 可以使用BETWEEN 关键字

       @Query("SELECT * FROM user WHERE age BETWEEN :minAge AND :maxAge")
       public User[] loadAllUsersBetweenAges(int minAge, int maxAge);
      
    1. 条件是表中是否含有所给的集合之中的数据,即是否有交集

       @Query("SELECT * FROM user WHERE region IN (:regions)")
       public List<User> loadUsersFromRegions(List<String> regions);  
      

    返回子集:

    有时我们查询数据但是并不需要表中每一行中的所有数据,通过Room我们就可以只返回一行数据中的几个数据。 我们需要通过具体的映射来将需要的数据映射到一个具体的类中:

        public class NameTuple 
        @ColumnInfo(name = "first_name")
        public String firstName;
    
        @ColumnInfo(name = "last_name")
        @NonNull
        public String lastName;
                    
    

    利用ColumnInfo的name属性,我们将数据表中的first_name 列映射到
    NameTuple的firstName 字段, last_name 映射到 lastName 字段 ,然后,我们可以从查询方法中返回该子集:

        public class NameTuple 
        @ColumnInfo(name = "first_name")
        public String firstName;
    
        @ColumnInfo(name = "last_name")
        @NonNull
        public String lastName;
               
    

创建数据库类:

该类会具有一些规范:

AppDatabase 定义数据库配置,并作为应用对持久性数据的主要访问点。数据库类必须满足以下条件:
1.该类必须带有 @Database 注解,该注解包含列出所有与数据库关联的数据实体的 entities 数组。
2.该类必须是一个抽象类,用于扩展 RoomDatabase。
3.对于与数据库关联的每个 DAO 类,数据库类必须定义一个具有零参数的抽象方法,并返回 DAO 类的实例。

示例代码:

    @Database(entities = User.class, version = 1)
    public abstract class AppDatabase extends RoomDatabase 
        public abstract UserDao userDao();
    

该数据库中只含有一张User表。     

具体使用:

以上三个准备工作完成了后,我们就可以在具体的代码中使用Room数据库了。

    首先我们需要创建数据库示例:       
                     
    AppDatabase db = Room.databaseBuilder(getApplicationContext(),
    AppDatabase.class, "database-name").build();

其中,第一个参数为整个应用程序的顶层上下文,第二个参数为数据库类,第三个参数为数据库的名称,最后调用build方法完成创建。

有了具体的数据库后我们就可以使用DAO来对其进行具体的操作了,还记得我们在
数据库类中有一个返回相应的DAO的方法,我们可以用其来获取DAO:

        UserDao userDao = db.userDao();

最后,我们就可以调用之前定义在DAO中的方法来具体地操作数据库了:

       List<User> users = userDao.getAll();         

Jetpack Room

前言:

🏀在我们日常开发中,经常要和数据打交道,所以存储数据是很重要的事。Android从最开始使用SQLite作为数据库存储数据,再到许多的开源的数据库,例如QRMLite,DBFlow,郭霖大佬开发的Litepal等等,都是为了方便SQLite的使用而出现的,因为SQLite的使用繁琐且容易出错。Google当然也意识到了SQLite的一些问题,于是在Jetpack组件中推出了Room,本质上Room也是在SQLite上提供了一层封装。因为它官方组件的身份,和良好的开发体验,现在逐渐成为了最主流的数据库ORM框架。

🌟Room官方文档:https://developer.android.google.cn/jetpack/androidx/releases/room

🌟SQL语法教程:https://www.runoob.com/sqlite/sqlite-tutorial.html

🚀本文代码地址:https://github.com/taxze6/Jetpack_learn/tree/main/Jetpack_basic_learn/room

为什么要使用Room?Room具有什么优势?

Room在SQLite上提供了一个抽象层,以便在充分利用SQLite的强大功能的同时,能够享有更强健的数据库访问机制。

Room的具体优势:

  • 有可以最大限度减少重复和容易出错的样板代码的注解
  • 简化了数据库迁移路径
  • 针对编译期SQL的语法检查
  • API设计友好,更容易上手,理解
  • SQL语句的使用更加贴近,能够降低学习成本
  • RxJavaLiveDataKotlin协程等都支持

Room具有三个主要模块

  • Entity: Entity用来表示数据库中的一个表。需要使用@Entity(tableName = "XXX")注解,其中的参数为表名。
  • Dao: 数据库访问对象,用于访问和管理数据(增删改查)。在使用时需要@DAO注解
  • Database: 它作为数据库持有者,用@Database注解和Room Database扩展的类

如何使用Room呢?

①添加依赖
最近更新时间(文章发布时的最新版本)稳定版Alpha 版
2022 年 6 月 1 日2.4.22.5.0-alpha02
plugins 
    ...
    id 'kotlin-kapt'
def room_version = "2.4.2"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
kapt 'androidx.room:room-compiler:$room_version'
②创建Entity实体类,用来表示数据库中的一张表(table)
@Entity(tableName = "user")
data class UserEntity(
    //主键定义需要用到@PrimaryKey(autoGenerate = true)注解,autoGenerate参数决定是否自增长
    @PrimaryKey(autoGenerate = true) val id:Int = 0, //默认值为0
    @ColumnInfo(name = "name", typeAffinity = ColumnInfo.TEXT) val name:String?,
    @ColumnInfo(name = "age", typeAffinity = ColumnInfo.INTEGER) val age:Int?
)

其中,每个表的字段都要加上@ColumnInfo(name = "xxx", typeAffinity = ColumnInfo.xxx)name属性表示这张表中的字段名,typeAffinity表示改字段的数据类型。

其他常用注解:

  • @Ignore :Entity中的所有属性都会被持久化到数据库,除非使用@Ignore

    @Ignore val name: String?
    
  • @ForeignKey:外键约束,不同于目前存在的大多数ORM库,Room不支持Entitiy对象间的直接引用。Google也做出了解释,具体原因请查看:https://developer.android.com/training/data-storage/room/referencing-data,不过Room允许通过外键来表示Entity之间的关系。ForeignKey我们文章后面再谈,先讲简单的使用。

  • @Embedded :实体类中引用其他实体类,在某些情况下,对于一张表的数据,我们用多个POJO类来表示,所以在这种情况下,我们可以使用Embedded注解嵌套对象。

③创建数据访问对象(Dao)处理增删改查
@Dao
interface UserDao 
    //添加用户
    @Insert
    fun addUser(vararg userEntity: UserEntity)//删除用户
    @Delete
    fun deleteUser(vararg userEntity: UserEntity)//更新用户
    @Update
    fun updateUser(vararg userEntity: UserEntity)//查找用户
    //返回user表中所有的数据
    @Query("select * from user")
    fun queryUser(): List<UserEntity>

Dao负责提供访问DBAPI,我们每一张表都需要一个Dao。在这里使用@Dao注解定义Dao类。

  • @Insert, @Delete需要传一个entity()进去
  • Class<?> entity() default Object.class;
    
  • @Query则是需要传递SQL语句
  • public @interface Query 
        //要运行的SQL语句
        String value();
    
    

☀注意:Room会在编译期基于Dao自动生成具体的实现类,UserDao_Impl(实现增删改查的方法)。

🔥Dao所有的方法调研都在当前线程进行,需要避免在UI线程中直接访问!

④创建Room database
@Database(entities = [UserEntity::class], version = 1)
abstract class UserDatabase : RoomDatabase() 
    abstract fun userDao(): UserDao

通过Room.databaseBuilder()或者 Room.inMemoryDatabaseBuilder()获取Database实例

val db = Room.databaseBuilder(
    applicationContext,
    UserDatabase::class.java, "userDb"
    ).build()

☀注意:创建Database的成本较高,所以我们最好使用单例的Database,避免反复创建实例所带来的开销。

单例模式创建Database:

@Database(entities = [UserEntity::class], version = 1)
abstract class UserDatabase : RoomDatabase() 
    abstract fun getUserDao(): UserDao
​
    companion object 
        @Volatile
        private var INSTANCE: UserDatabase? = null@JvmStatic
        fun getInstance(context: Context): UserDatabase 
            val tmpInstance = INSTANCE
            if (tmpInstance != null) 
                return tmpInstance
            
            //锁
            synchronized(this) 
                val instance =
                    Room.databaseBuilder(context, UserDatabase::class.java, "userDb").build()
                INSTANCE = instance
                return instance
            
        
    
⑤在Activity中使用,进行一些可视化操作

activity_main:

<LinearLayout
    ...
    tools:context=".MainActivity"
    android:orientation="vertical">
    <Button
        android:id="@+id/btn_add"
        ...
        android:text="增加一条数据"/>
    <Button
        android:id="@+id/btn_delete"
        ...
        android:text="删除一条数据"/>
    <Button
        android:id="@+id/btn_update"
        ...
        android:text="更新一条数据"/>
    <Button
        android:id="@+id/btn_query_all"
        ...
        android:text="查新所有数据"/>
</LinearLayout>

MainActivity:

private const val TAG = "My_MainActivity"
class MainActivity : AppCompatActivity() 
    private val userDao by lazy 
        UserDatabase.getInstance(this).getUserDao()
    
    private lateinit var btnAdd: Button
    private lateinit var btnDelete: Button
    private lateinit var btnUpdate: Button
    private lateinit var btnQueryAll: Button
    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        init()
        //添加数据
        btnAdd.setOnClickListener 
            //数据库的增删改查必须在子线程,当然也可以在协程中操作
            Thread 
                val entity = UserEntity(name = "Taxze", age = 18)
                userDao.addUser(entity)
            .start()
        
        //查询数据
        btnQueryAll.setOnClickListener 
            Thread 
                val userList = userDao.queryUser()
                userList.forEach 
                    Log.d(TAG, "查询到的数据为:$it")
                
            .start()
        
        //修改数据
        btnUpdate.setOnClickListener 
            Thread 
                userDao.updateUser(UserEntity(2, "Taxzeeeeee", 18))
            .start()
        
        //删除数据
        btnDelete.setOnClickListener 
            Thread 
                userDao.deleteUser(UserEntity(2, null, null))
            .start()
        
    
    //初始化
    private fun init() 
        btnAdd = findViewById(R.id.btn_add)
        btnDelete = findViewById(R.id.btn_delete)
        btnUpdate = findViewById(R.id.btn_update)
        btnQueryAll = findViewById(R.id.btn_query_all)
    

结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e35hYmSg-1657849942540)(https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/50e10c57e1fa41a6960b3c5d87fd9713~tplv-k3u1fbpfcp-watermark.image?)]
到这里我们已经讲完了Room的最基本的使用,如果只是一些非常简单的业务,你看到这里已经可以去写代码了,但是还有一些进阶的操作需讲解一下,继续往下看吧!

数据库的升级

Room在2021 年 4 月 21 日发布的版本 2.4.0-alpha01中开始支持自动迁移,不过很多朋友反应还是有很多问题,建议手动迁移,当然如果你使用的是更低的版本只能手动迁移啦。

具体信息请参考:https://developer.android.google.cn/training/data-storage/room/migrating-db-versions#manual

具体如何升级数据库呢?下面我们一步一步来实现吧!

①修改数据库版本

UserDatabase文件中修改version,将其变为2(原来是1)

在此时,我们需要想一想,我们要对数据库做什么升级操作呢?

我们这里为了演示就给数据库增加一张成绩表:

@Database(entities = [UserEntity::class,ScoreEntity::class], version = 2)

添加表:

@Entity(tableName = "score")
data class ScoreEntity(
    @PrimaryKey(autoGenerate = true) var id: Int = 0,
    @ColumnInfo(name = "userScore")
    var userScore: Int
)
②创建对应的Dao,ScoreDao
@Dao
interface ScoreDao 
    @Insert
    fun insertUserScore(vararg scoreEntity: ScoreEntity)@Query("select * from score")
    fun queryUserScoreData():List<ScoreEntity>

③在Database中添加迁移
@Database(entities = [UserEntity::class,ScoreEntity::class], version = 2)
abstract class UserDatabase : RoomDatabase() 
    abstract fun getUserDao(): UserDao
    
    //添加一个Dao
    abstract fun getScoreDao():ScoreDao
​
    companion object 
        //变量名最好为xxx版本迁移到xxx版本
        private val MIGRATION_1_2 = object : Migration(1, 2) 
            override fun migrate(database: SupportSQLiteDatabase) 
                database.execSQL(
                    """
                    create table userScore(
                    id integer primary key autoincrement not null,
                    userScore integer not null)
                """.trimIndent()
                )
            
        @Volatile
        private var INSTANCE: UserDatabase? = null@JvmStatic
        fun getInstance(context: Context): UserDatabase 
            ...
            synchronized(this) 
                val instance =
                    Room.databaseBuilder(
                        context.applicationContext,
                        UserDatabase::class.java,
                        "userDb"
                    )
                        .addMigrations(MIGRATION_1_2)
                        .build()
                INSTANCE = instance
                return instance
            
        
    

④使用更新后的数据

在xml布局中添加两个Button:

<Button
    android:id="@+id/btn_add_user_score"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="增加user的score数据"/><Button
    android:id="@+id/btn_query_user_score"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="查询user的score数据"/>

在MainActivity中加入:

private val userScoreDao by lazy 
    UserDatabase.getInstance(this).getScoreDao()
...private lateinit var btnAddUserScore: Button
private lateinit var btnQueryUserScore: Button
​
...
btnAddUserScore = findViewById(R.id.btn_add_user_score)
btnQueryUserScore = findViewById(R.id.btn_query_user_score)...
btnAddUserScore.setOnClickListener 
            Thread
                userScoreDao.insertUserScore(ScoreEntity(userScore = 100))
            .start()
        
​
btnQueryUserScore.setOnClickListener 
            Thread
                userScoreDao.queryUserScoreData().forEach
                    Log.d(TAG,"userScore表的数据为:$it")
                
            .start()
        

这样对数据库的一次手动迁移就完成啦!💪

如果你想继续升级,就重复之前的步骤,然后将2→3

private val MIGRATION_2_3 = object : Migration(2, 3) 
    override fun migrate(database: SupportSQLiteDatabase) 
        database.execSQL(
            """
            .... 再一次新的操作
        """.trimIndent()
        )
    
...
.addMigrations(MIGRATION_1_2,MIGRATION_2_3)

使用Room更多的骚操作!

①想知道更多的Room数据库迁移的操作吗,那你可以看看这篇文章:

https://www.modb.pro/db/139101

②更优雅的修改数据

在上面的修改数据操作中,我们是需要填入每个字段的值的,但是,大部分情况,我们是不会全部知道的,比如我们不知道Userage,那么我们的age字段就填个Null吗?

val entity = UserEntity(name = "Taxze", age = null)

这显然是不合适的!

当我们只想修改用户名的时,却又不知道age的值的时候,我们需要怎么修改呢?

⑴创建UpdateNameBean

class UpdateNameBean(var id:Int,var name:String)

⑵在Dao中加入新的方法

@Update(entity = UserEntity::class)
fun updataUser2(vararg updataNameBean:UpdateNameBean)

⑶然后在使用时只需要传入id,和name即可

userDao.updateUser2(updataNameBean(2, "Taxzeeeeee"))

当然你也可以给用户类创建多个构造方法,并给这些构造方法添加@lgnore

③详解@Insert 插入
@Dao
interface UserDao 
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insertUsers(vararg userEntity: UserEntity)

其中onConflict用于设置当事务中遇到冲突时的策略。

有如下一些参数可以选择:

  • OnConflictStrategy.REPLACE : 替换旧值,继续当前事务
  • OnConflictStrategy.NONE : 忽略冲突,继续当前事务
  • OnConflictStrategy.ABORT : 回滚
④@Query 指定参数查询

每次都查表的全部信息这也不是事啊,我们要用到where条件来指定参数查询。

@Dao
interface UserDao 
    @Query("SELECT * FROM user WHERE age BETWEEN :minAge AND :maxAge")
    fun loadAllUsersBetweenAges(minAge: Int, maxAge: Int): Array<UserEntity>

大家可以自己学习一下SQL语法~

⑤多表查询

很多业务情况下,我们是需要同时在多张表中进行查询的。

@Dao
interface UserDao 
    @Query(
        "SELECT * FROM user " +
                "INNER JOIN score ON score.id = user.id "  +
                "WHERE user.name LIKE :userName"
    )
    fun findUsersScoreId(userName: String): List<UserEntity>

⑥@Embedded内嵌对象

我们可以使用@Embedded注解,将一个Entity作为属性内嵌到另外一个Entity,然后我们就可以像访问Column一样去访问内嵌的Entity啦。

data class Score(
    val id:Int?,
    val score:String?,
)
@Entity(tableName = "user")
data class UserEntity(
    @PrimaryKey(autoGenerate = true) val id:Int = 0,
    .....
    @Embedded val score: Score?
)
⑦使用@Relation 注解和 foreignkeys注解来描述Entity之间更复杂的关系

可以实现一对多,多对多的关系

⑧预填充数据库

可以查看官方文档:https://developer.android.google.cn/training/data-storage/room/prepopulate

⑨类型转换器 TypeConverter

Room配合LiveData和ViewModel

下面我们通过一个Room+LiveData+ViewModel的例子来完成这篇文章的学习吧

话不多说,先上效果图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QGq7L7fj-1657849942542)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/106dc07d0e1c43b0a3b54e04d4e80678~tplv-k3u1fbpfcp-watermark.image?)]

①创建UserEntity
@Entity(tableName = "user")
data class UserEntity(
    @PrimaryKey(autoGenerate = true) val id: Int = 0,
    @ColumnInfo(name = "name", typeAffinity = ColumnInfo.TEXT) val name: String?,
    @ColumnInfo(name = "age", typeAffinity = ColumnInfo.INTEGER) val age: Int?,
)
②创建对应的Dao
@Dao
interface UserDao 
    //添加用户
    @Insert
    fun addUser(vararg userEntity: UserEntity)
​
    //查找用户
    //返回user表中所有的数据,使用LiveData
    @Query("select * from user")
    fun getUserData(): LiveData<List<UserEntity>>

③创建对应的Database

代码在最开始的例子中已经给出了。

④创建ViewModel
class UserViewModel(userDao: UserDao):ViewModel()
    var userLivedata = userDao.getUserData()

⑤创建UserViewModelFactory

我们在UserViewModel类中传递了UserDao参数,所以我们需要有这么个类实现ViewModelProvider.Factory接口,以便于将UserDao在实例化时传入。

class UserViewModelFactory(private val userDao: UserDao) : ViewModelProvider.Factory 
    override fun <T : ViewModel?> create(modelClass: Class<T>): T 
        return UserViewModel(userDao) as T
    

⑥编辑xml

activity_main:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">
​
    <EditText
        android:id="@+id/user_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:hint="请输入UserName" />
​
    <EditText
        android:id="@+id/user_age"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:hint="请输入UserAge" />
​
    <Button
        android:id="@+id/btn_add"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="添加一条user数据" />
​
    <ListView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </ListView>
</LinearLayout>

创建一个simple_list_item.xml,用于展示每一条用户数据

以上是关于JetPack开发笔记:Room数据库的详解与基础使用的主要内容,如果未能解决你的问题,请参考以下文章

JetPack架构---ORM数据库Room的使用

Jetpack Room

Jetpack Room

Android jetpack room 记录数据库升级日志

Android Jetpack: Room | 中文教学视频

Jetpack 总结系列- Google-Room数据库使用