美文网首页APP & program
Android Jetpack之关于ROOM的使用

Android Jetpack之关于ROOM的使用

作者: 安安_660c | 来源:发表于2023-02-01 15:56 被阅读0次

定义

本地保存数据的一种方式,处理大量结构化数据。最常见的使用场景是缓存相关的数据。

优势

  1. 针对SQL查询的编译时验证。
  2. 最大程度减少重复的样板代码,方便注解
  3. 简化了数据迁移路径。

设置

dependencies {
    def room_version = "2.4.3"

    implementation "androidx.room:room-runtime:$room_version"
    annotationProcessor "androidx.room:room-compiler:$room_version"

    // kotlin 注释处理工具
    kapt "androidx.room:room-compiler:$room_version"
    // kotlin 符号处理 (KSP)
    ksp "androidx.room:room-compiler:$room_version"

    // 可选——Rxjava2 对 Room 的支持
    implementation "androidx.room:room-rxjava2:$room_version"

    // 可选——Rxjava3 对 Room 的支持
    implementation "androidx.room:room-rxjava3:$room_version"

    // 可选——对 Room 的番石榴支持,包括 Options 和 ListableFuture
    implementation "androidx.room:room-guava:$room_version"

    // 可选-测试助手
    testImplementation "androidx.room:room-testing:$room_version"

    // 可选-分页3集成
    implementation "androidx.room:room-paging:$room_version"
}

主要组件

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

定义数据实体

@Entity(tableName = "user")
data class User(
    @PrimaryKey val id: Int,
    @ColumnInfo(name = "user_name") val userName: String?,
    @ColumnInfo(name = "user_mobile") val mobile: String
)

  • 通过 @Entity 注解类。数据库中表的每一列对应类中的字段。tableName 定义表的名称,如果不写默认类名。
  • 通过 @PrimaryKey 注解定义主键,每个Room实体必须定义一个主键
  • 通过 @ColumnInfo 注解定义列名

定义复合主键

通过多个列的组合,对实体实例进行唯一的标识,可以通过列出 @EntityprimaryKeys 属性中的以下列定义一个复合主键

@Entity(primaryKeys = ["user_name", "user_mobile"])
data class User(
    @ColumnInfo(name = "user_name") val userName: String?,
    @ColumnInfo(name = "user_mobile") val mobile: String
)

忽略字段

如果某个实体中有您不想保留的字段,则可以使用 @Ignore 为这些字段添加注解,如以下代码段所示:

@Entity(tableName = "user")
data class User(
    @PrimaryKey val id: Int,
    @ColumnInfo(name = "user_name") val userName: String?,
    @ColumnInfo(name = "user_mobile") val mobile: String,
    @Ignore val age:Int
)

如果忽略的是父类字段,则使用 @Entity 属性的 ignoredColumns

open class Person(val sex: Int? = null)

@Entity(tableName = "user", ignoredColumns = ["sex"])
data class User(
    @PrimaryKey val id: Int,
    @ColumnInfo(name = "user_name") val userName: String?,
    @ColumnInfo(name = "user_mobile") val mobile: String,
    @Ignore val age: Int
) : Person()

数据访问对象(Dao)

定义名为UserDao的数据访问的Dao,UserDao 中提供与表中数据交互的方法。您可以将每个 DAO 定义为一个接口或一个抽象类

@Dao
interface UserDao {
    @Insert
    fun insertAll(vararg users: User)

    @Delete
    fun delete(user: User)

    @Query("SELECT * FROM user")
    fun getAll(): List<User>
}

便捷方法

Room 提供了方便的注解,用于定于无需编写SQL语句,即可执行简单的插入、更新、删除的方法。

插入

借助 @Insert 注释,您可以定义将其参数插入到数据库中的相应表中的方法。

@Dao
interface UserDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insertUsers(vararg users:User)

    @Insert
    fun insertBothUsers(user1:User,user2:User)

    @Insert
    fun insertUsersAndFriends(user:User,friends:List<User>)
}

  • @Insert 的参数必须是带有 @Entity 注解的数据实体。
  • 调用 @Insert 时候,Room 会将每个实体实例插入到表中。
  • 如果插入的是单个实体数据类,则返回long 类型的 rowId, 如果是多个,则返回long类型的数组或集合。

更新

借助 @Update 注释,更新数据库中特定行的方法。与 @Insert 一样接收 @Entity 注释的数据实体类。

@Dao
interface UserDao {
    @Update
    fun updateUsers(vararg users: User)
}

Room 使用主键将传递的实体实例与数据库中的行进行匹配。如果没有具有相同主键的行,Room 不会进行任何更改。

@Update 方法可以选择性地返回 int 值,该值指示成功更新的行数

删除

借助@Delete 注释,删除数据库中特定行的方法。与 上面参数要求一样接收@Entity 注释的数据实体类

@Dao
interface UserDao {
    @Delete
    fun deleteUsers(vararg users:User)
}

Room 使用主键将传递的实体实例与数据库中的行进行匹配。如果没有具有相同主键的行,Room 不会进行任何更改。

@Delete 方法可以选择性地返回 int 值,该值指示成功删除的行数

查询方法

使用 @Query 注解,可以编写SQL语句并作为DAO方法公开。使用这些方法查询应用的数据库数据,或者需要执行更复杂的插入、更新、和删除操作。

Room 会在编译时验证SQL查询,这意味着,如果查询出现问题,则会出现编译错误,而不是运行时错误。

@Dao
interface UserDao {
    @Query("SELECT * FROM user")
    fun loadAllUsers(): Array<User>
}

查询表格列的子集

查询表格数据中部分字段数据。

data class UserBaseInfo(
    @ColumnInfo(name = "user_name") val userName: String?,
    @ColumnInfo(name = "user_mobile") val mobile: String
)

@Dao
interface UserDao {
    @Query("SELECT user_name,user_mobile FROM user") //查询 user 表中,user_name 和 user_mobile
    fun loadUserBaseInfo():List<UserBaseInfo>
}

简单参数传递查询

@Dao
interface UserDao {
    @Query("SELECT * FROM user WHERE age > :minAge")
    fun loadAllUserOlderThan(minAge: Int): Array<User>

    @Query("SELECT * FROM user WHERE age BETWEEN :maxAge AND :maxAge")
    fun loadAllUserBetweenAges(minAge: Int, maxAge: Int): Array<User>

    @Query("SELECT * FROM user WHERE age > :minAge AND age <= :maxAge")
    fun loadAllUserBetweenAges2(minAge: Int, maxAge: Int): Array<User>

    @Query("SELECT * FROM user WHERE user_name LIKE :search")
    fun findUserWithName(search: String): List<User>
}

一组数据的查询

@Dao
interface UserDao {
    @Query("SELECT * FROM user WHERE age IN (:ages)")
    fun loadAllUserOlderThan(ages: List<Int>): Array<User>
}

查询多个表

您的部分查询可能需要访问多个表格才能计算出结果。您可以在 SQL 查询中使用 JOIN 子句来引用多个表。

@Entity(tableName = "book")
data class Book(@PrimaryKey val id: Int)

@Entity(tableName = "loan")
data class Loan(
    @ColumnInfo(name = "book_id") val bookId: Int,
    @ColumnInfo(name = "user_id") val userId: Int
)

@Dao
interface UserDao {
    @Query("SELECT * FROM book " +
            "INNER JOIN loan ON loan.book_id = book.id " +
            "INNER JOIN user ON user.id = loan.user_id " +
            "WHERE user.user_name LIKE :userName")
    fun findBoosBorrowedByNameSync(userName: String): List<Book>
}

您还可以定义简单对象以从多个联接表返回列的子集,定义了一个 DAO,其中包含一个返回用户姓名和借阅图书名称的方法

@Entity(tableName = "book")
data class Book(
    @PrimaryKey val id: Int,
    @ColumnInfo(name = "name") val name: String,
    @ColumnInfo(name = "user_id") val userId: Int
)

data class UserBook(val userName: String?, val bookName: String?)

@Dao
interface UserDao {
    @Query(
        "SELECT user.user_name AS userName,book.name AS bookName " +
                "FROM user,book " +
                "WHERE user.id  = book.user_id"
    )
    fun loadUserAndBookNames(): LiveData<List<UserBook>>
}

返回多重映射 ( 一 对 多 )

如:一个用户可能对应的借了多本书

@Entity(tableName = "book")
data class Book(
    @PrimaryKey val id: Int,
    @ColumnInfo(name = "name") val name: String,
    @ColumnInfo(name = "user_id") val userId: Int
)

@Dao
interface UserDao {
    @Query("SELECT * FROM user JOIN book ON user.id = book.user_id")
    fun loadUserAndBookNames(): Map<User,List<Book>>
}

通过 JOIN 添加另外一张表,再通过 ON 添加 筛选条件。

查询方法返回多重映射时,您可以编写使用 GROUP BY 子句的查询,以便利用 SQL 的功能进行高级计算和过滤。例如,您可以修改 loadUserAndBookNames() 方法,以便仅返回已借阅的三本或更多图书的用户:

@Query(
    "SELECT * FROM user" +
    "JOIN book ON user.id = book.user_id" +
    "GROUP BY user.name HAVING COUNT(book.id) >= 3"
)
fun loadUserAndBookNames(): Map<User, List<Book>>

数据库类

  1. 该类必须带有 @Database 注解,通过 entities 列出所有数据库相关的实体
  2. 该类必须是一个抽象类,用户扩展RoomDatabase
  3. 对于数据库关联的每个Dao 类,数据库类必须提供一个零参数的抽象方法,返回DAO类的实例。
@Database(
    entities = [User::class],
    version = 1
)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}

使用

val db = Room.databaseBuilder(
            applicationContext,
            AppDatabase::class.java, "database-name"
        ).build()
        
val userDao = db.userDao()
val users: List<User> = userDao.getAll()

链接:https://juejin.cn/post/7188718927545892919
作者:光头强不砍树

相关文章

网友评论

    本文标题:Android Jetpack之关于ROOM的使用

    本文链接:https://www.haomeiwen.com/subject/vapihdtx.html