Jetpack架构组件库:Room

Room

Room是一款轻量级orm数据库,本质上是一个基于SQLite之上的抽象层。它通过注解的方式提供相关功能,编译时自动生成实现Impl,相比纯 SQLite 的API使用方式更加简单。另外一个相比于SQLite API的优势是:它会在编译时检查 SQL 语句的合法性,而不是等到运行时应用崩溃才发现。

Room 的使用

添加依赖项

dependencies {
   
    def room_version = "2.5.0"

    implementation "androidx.room:room-runtime:$room_version"
    implementation "androidx.room:room-ktx:$room_version"
    
    // annotationProcessor "androidx.room:room-compiler:$room_version"
    
    // To use Kotlin annotation processing tool (kapt)
    // kapt "androidx.room:room-compiler:$room_version"
    
    // To use Kotlin Symbol Processing (KSP)
    ksp "androidx.room:room-compiler:$room_version"
}

注解处理器这里建议使用 KSP,编译速度更快。

定义Entity实体

@Entity
data class User(@PrimaryKey val id: Int, val name: String, val age: Int) 

默认表名与类名相同,如需显示指定表名,使用 @Entity(tableName = "user_table")

@Entity(tableName = "user_table")
data class User(@PrimaryKey val id: Int, val name: String, val age: Int) 

如需显示指定表中的列名,使用 @ColumnInfo(name = "xxx")

@Entity
data class User(
    @PrimaryKey 
    val id: Int,
    @ColumnInfo(name = "userName") 
    val name: String,
    val age: Int,
) 

定义主键

每个 Room 实体都必须定义一个主键,用于唯一标识相应数据库表中的每一行。执行此操作的最直接方式是使用 @PrimaryKey 为单个列添加注解:

@Entity
data class User(
    @PrimaryKey // 主键
    val id: Int, 
    val name: String,
    val age: Int
) 

如需设置主键自动生成,使用 @PrimaryKey(autoGenerate = true)

@Entity
data class User(
    @PrimaryKey(autoGenerate = true) // 设置主键自动生成
    val id: Int, 
    val name: String,
    val age: Int,
)

如需定义复合主键进行唯一标识,使用 @Entity(primaryKeys = ["name1", "name2"])

@Entity(primaryKeys = ["firstName", "lastName"])
data class User(
    val firstName: String?,
    val lastName: String?
)

忽略字段

默认情况下,Room 会为实体中定义的每个字段创建一个列。 如果某个实体中有不想保留的字段,则可以使用 @Ignore 为这些字段添加注解:

@Entity
data class User(
    @PrimaryKey val id: Int,
    val firstName: String?,
    val lastName: String?,
    @Ignore val picture: Bitmap?
)

如果实体继承了父实体的字段,则使用 @Entity 属性的 ignoredColumns 属性通常会更容易:

open class User {
   
    var picture: Bitmap? = null
}

@Entity(ignoredColumns = ["picture"])
data class RemoteUser(
    @PrimaryKey val id: Int,
    val hasVpn: Boolean
) : User()

创建嵌套对象

例如,User 类可以包含一个 Address 类型的字段,它表示名为 street、city、state 和 postCode 的字段的组合。若要在表中单独存储组合列,请在 User 类中添加 Address 字段,并添加 @Embedded 注解,如以下代码段所示:

data class Address(
    val street: String?,
    val state: String?,
    val city: String?,
    @ColumnInfo(name = "post_code") val postCode: Int
)

@Entity
data class User(
    @PrimaryKey val id: Int,
    val firstName: String?,
    @Embedded val address: Address?
)

然后,表示 User 对象的表将包含具有以下名称的列:id、firstName、street、state、city 和 post_code。

注意:嵌套字段还可以包含其他嵌套字段。如果某个实体具有相同类型的多个嵌套字段,可以通过设置 prefix 属性确保每个列的唯一性。然后,Room 会将提供的值添加到嵌套对象中每个列名称的开头。

支持全文搜索

如果应用需要通过全文搜索 (FTS) 快速访问数据库信息,请使用虚拟表(使用 FTS3 或 FTS4 SQLite 扩展模块)为实体提供支持。如需使用 Room 2.1.0 及更高版本中提供的这项功能,请将 @Fts3@Fts4 注解添加到给定实体,如下代码所示:

// 只有当你的应用程序对磁盘空间有严格的要求,或者你需要与旧SQLite版本兼容时才使用“@Fts3”
@Fts4
@Entity(tableName = "users")
data class User(
    /* 为FTS表支持的实体指定主键是可选的,但是如果指定了,则必须使用Int类型和rowid列名 */
    @PrimaryKey @ColumnInfo(name = "rowid") val id: Int,
    @ColumnInfo(name = "first_name") val firstName: String?
)

注意:启用 FTS 的表始终使用 INTEGER 类型的主键且列名称为“rowid”。如果是由 FTS 表支持的实体定义主键,则必须使用相应的类型和列名称。

如果表支持以多种语言显示的内容,请使用 languageId 选项指定用于存储每一行语言信息的列:

@Fts4(languageId = "lid")
@Entity(tableName = "users")
data class User(
    // ...
    @ColumnInfo(name = "lid") val languageId: Int
)

为特定列添加索引

如果您的应用必须支持不允许使用由 FTS3 或 FTS4 表支持的实体的 SDK 版本,您仍可以将数据库中的某些列编入索引,以加快查询速度。如需为实体添加索引,请在 @Entity 注解中添加 indices 属性,列出要在索引或复合索引中包含的列的名称。

@Entity(indices = [Index(value = ["last_name", "address"])])
data class User(
    @PrimaryKey val id: Int,
    val firstName: String?,
    val address: String?,
    @ColumnInfo(name = "last_name") val lastName: String?,
    @Ignore val picture: Bitmap?
)

添加基于 AutoValue 的对象

Room 2.1.0 及更高版本中,您可以将基于 Java不可变值类(使用 @AutoValue 进行注解)用作应用数据库中的实体。此支持在实体的两个实例被视为相等(如果这两个实例的列包含相同的值)时尤为有用。

将带有 @AutoValue 注解的类用作实体时,您可以使用 @PrimaryKey、@ColumnInfo、@Embedded@Relation 为该类的抽象方法添加注解。但是,您必须在每次使用这些注解时添加 @CopyAnnotations 注解,以便 Room 可以正确解释这些方法的自动生成实现。

以下代码段展示了一个使用 @AutoValue 进行注解的类(Room 将其标识为实体)的示例:

// User.java
@AutoValue
@Entity
public abstract class User {
   
    // Supported annotations must include `@CopyAnnotations`.
    @CopyAnnotations
    @PrimaryKey
    public abstract long getId();

    public abstract String getFirstName();
    public abstract String getLastName();

    // Room uses this factory method to create User objects.
    public static User create(long id, String firstName, String lastName) {
   
        return new AutoValue_User(id, firstName, lastName);
    }
}

注意:此功能旨在用于基于 Java 的实体。如需在基于 Kotlin 的实体中实现相同的功能,最好改用数据类

创建 DAO

什么是 DAO?

在 DAO(Database Access Object 数据访问对象)中,您可以指定 SQL 查询并将其与方法调用相关联。编译器会检查 SQL 并根据常见查询的方便的注解(如 @Insert)生成查询。Room 会使用 DAO 为代码创建整洁的 API。

  • DAO 必须是一个接口或抽象类
  • 默认情况下,所有查询都必须在单独的线程上执行。
  • Room 支持 Kotlin 协程,您可使用 suspend 修饰符对查询进行注解,然后从协程或其他挂起函数对其进行调用。
@Dao 
interface UserDao {
   

    @Insert 
    suspend fun insert(vararg user: User) // 注意是挂起函数

    @Update
    suspend fun update(vararg user: User)

    @Delete
    suspend fun delete(vararg user: User)  

    @Query("DELETE FROM user") // 表名会自动转大写
    suspend fun deleteAll()  

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

插入

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

@Dao  
interface UserDao {
   
    @Insert 
    suspend fun insert(vararg user: User) 
    
    @Insert
    fun insertBothUsers(user1: User, user2: User)
    
    @Insert
    fun insertUsersAndFriends(user: User, friends: List<User>)
}

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

更新

@Insert 方法类似,@Update 方法接受数据实体实例作为参数。

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

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

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

删除

@Insert 方法类似,@Delete 方法接受数据实体实例作为参数。

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

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

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

查询

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

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

简单查询

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

@Query("SELECT * FROM user")
fun loadAllUsers(): Array<User>
查询指定的列

在大多数情况下,您只需要返回要查询的表中的列的子集。为节省资源并简化查询的执行,您应只查询所需的字段。

借助 Room,您可以从任何查询返回简单对象,前提是您可以将一组结果列映射到返回的对象。例如,您可以定义以下对象来保存用户的名字和姓氏:

data class NameTuple(
    @ColumnInfo(name = "first_name") val firstName: String?,
    @ColumnInfo
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: Jetpack是一套基于Android开发语言Kotlin的组件,目的是帮助Android开发者快速构建健壮的、高效的应用程序。Jetpack架构组件从入门到精通是一本介绍Jetpack组件的电子书,其内容向读者提供了Jetpack组件的最基础的知识体系,以及对组件的更高阶的实践知识和技能分享。 在读者学习这本电子书时,首先会学习到基本的Jetpack概念,并了解到如何使用各种工具来实现底层代码、布局和资源文件的编写。同时,电子书重点讲解了Jetpack组件中最常用的组件,比如ViewModel、LiveData、Room、WorkManager等,它们各自的功能和用法,让读者熟知Jetpack组件的基本运作原理。读者还会学习如何将这些组件综合使用,以构造真正的应用程序。在内容的后半部分,电子书重点讲解了Jetpack架构层面的设计,以及如何更有效的使用和管理组件。 总之,Jetpack架构组件从入门到精通这本电子书非常适合想要学习Jetpack组件Android初学者和有一定开发经验的开发者,能够帮助他们快速掌握Jetpack组件,以及提高软件的可扩展性和有效性。 ### 回答2: Jetpack架构组件是一组Android软件组件,通过这些组件,开发者可以更加轻松地开发应用程序,并提供高质量的用户体验。Jetpack架构组件包括众多不同的组件,这些组件都具有不同的用途和功能。对于开发者来说,学习Jetpack架构组件是非常重要的。 《jetpack架构组件从入门到精通 pdf》是一份针对Jetpack架构组件的学习资料。它是可以帮助开发者更好地理解和掌握Jetpack架构组件的重要手册。通过这份PDF文档,开发者可以学习到Jetpack架构组件的基本概念、用途和功能,并了解如何将这些组件应用到他们的应用程序开发中。 这份PDF文档涵盖了Jetpack架构组件的多个方面,包括ViewModel、LiveData、Data Binding、Room、Navigation、WorkManager等等。每个章节都包含了详细的介绍和具体的用例,从而帮助开发者更好地理解每个组件的用途和功能。 无论是初学者还是经验丰富的开发者,都可以从这份PDF文档中获益。它可以帮助初学者更轻松地入门Jetpack架构组件,并提供了有用的工具和技巧。对于经验丰富的开发者来说,这份PDF文档可以帮助他们更深入地了解Jetpack架构组件,并提供了一些高级技术和策略,以优化应用程序性能和用户体验。 总之,Jetpack架构组件从入门到精通的PDF文档是一份非常有价值的资源,它可以帮助开发者更好地理解和掌握Jetpack架构组件,从而更加轻松地开发高质量的Android应用程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

川峰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值