定义
本地保存数据的一种方式,处理大量结构化数据。最常见的使用场景是缓存相关的数据。
优势
- 针对SQL查询的编译时验证。
- 最大程度减少重复的样板代码,方便注解
- 简化了数据迁移路径。
设置
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 注解定义列名
定义复合主键
通过多个列的组合,对实体实例进行唯一的标识,可以通过列出
@Entity
的primaryKeys
属性中的以下列定义一个复合主键
@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>>
数据库类
- 该类必须带有 @Database 注解,通过 entities 列出所有数据库相关的实体
- 该类必须是一个抽象类,用户扩展RoomDatabase
- 对于数据库关联的每个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
作者:光头强不砍树
网友评论