Room 持久性库在 SQLite 的基础上提供了一个抽象层,让用户能够在充分利用 SQLite 的强大功能的同时,获享更强健的数据库访问机制。
def room_version = “2.2.5”
implementation "androidx.room:room-runtime:$room_version" annotationProcessor "androidx.room:room-compiler:$room_version"
我使用的是kotlin开发,注意:对于基于 Kotlin 的应用,请确保使用 kapt,而不是 annotationProcessor。您还应添加 kotlin-kapt 插件。
Room 具有以下注释处理器选项:
- room.schemaLocation:配置并启用将数据库架构导出到给定目录中的 JSON 文件的功能。
- room.incremental:启用 Gradle 增量注释处理器。 3.room.expandProjection:配置 Room 以重写查询,使其顶部星形投影在展开后仅包含 DAO 方法返回类型中定义的列。
代码段举例:
android { ... defaultConfig { ... javaCompileOptions { annotationProcessorOptions { arguments = [ "room.schemaLocation":"$projectDir/schemas".toString(), "room.incremental":"true", "room.expandProjection":"true"] } } } }
应用使用 Room 数据库来获取与该数据库关联的数据访问对象 (DAO)。然后,应用使用每个 DAO 从数据库中获取实体,然后再将对这些实体的所有更改保存回数据库中。最后,应用使用实体来获取和设置与数据库中的表列相对应的值。
包含数据库持有者,并作为应用已保留的持久关系型数据的底层连接的主要接入点。 使用 @Database 注释的类应满足以下条件:
是扩展 RoomDatabase 的抽象类。 在注释中添加与数据库关联的实体列表。 包含具有 0 个参数且返回使用 @Dao 注释的类的抽象方法。
在运行时,您可以通过调用 Room.databaseBuilder() 或 Room.inMemoryDatabaseBuilder() 获取 Database 的实例。 databaseBuilder( ) 创建一个持久的数据库,保存在用户本地 inMemoryDatabaseBuilder() 在内存中创建一个数据库,这个数据库会随着进程的杀死而消失
@Database(entities = arrayOf(User::class), version = 1) abstract class AppDatabase : RoomDatabase() { abstract fun userDao(): UserDao }
在application初始化
val db = Room.databaseBuilder( applicationContext, AppDatabase::class.java, "database-name" ).build()
表示数据库中的表,使用@Entry注解标注 , @Entity 注释的 tableName 属性可以自定义表名,SQLite 中的表名称不区分大小写。
@Entity (tableName = "users") data class User( @PrimaryKey var id: Int, var firstName: String?, var lastName: String? )
使用主键 : 一个Entry中至少需要一个主键,我们使用**@PrimaryKey** 来注释. 自增类型的主键,则可以设置 @PrimaryKey 的 autoGenerate 属性。 复合主键,使用 @Entity 注释的 primaryKeys 属性
@Entity(primaryKeys = arrayOf("firstName", "lastName")) data class User( val firstName: String?, val lastName: String? )
同时可以自定义属性字段名 ,使用@ColumnInfo注释
@Entity(tableName = "users") data class User ( @PrimaryKey val id: Int, @ColumnInfo(name = "first_name") val firstName: String?, @ColumnInfo(name = "last_name") val lastName: String? )
忽略字段 @Ignore ,Room数据库在创建的时候会为每一个字段对应在数据库创建一列,如果不想entry中的某一个字段保留在数据表中,可以忽略
@Entity data class User( @PrimaryKey val id: Int, val firstName: String?, val lastName: String?, @Ignore val picture: Bitmap? )
如果涉及到entry继承,我们想要忽略父类中的字段 ,需要使用 @Entity 属性的 ignoredColumns 属性,这样会更加简洁
open class User { var picture: Bitmap? = null } @Entity(ignoredColumns = arrayOf("picture")) data class RemoteUser( @PrimaryKey val id: Int, val hasVpn: Boolean ) : User()
包含用于访问数据库的方法。
@Dao interface UserDao { @Query("SELECT * FROM user") fun getAll(): List<User> @Query("SELECT * FROM user WHERE uid IN (:userIds)") fun loadAllByIds(userIds: IntArray): List<User> @Query("SELECT * FROM user WHERE first_name LIKE :first AND " + "last_name LIKE :last LIMIT 1") fun findByName(first: String, last: String): User @Insert fun insertAll(vararg users: User) @Delete fun delete(user: User) }
在DAO中定义方法方便使用
insert : 当您创建 DAO 方法并使用 @Insert 对其进行注释时,Room 会生成一个实现,该实现在单个事务中将所有参数插入到数据库中。 方法的返回值是当前entry主键id,可以单个插入返回一个id,也可以批量插入,返回一个List集合Id
@Dao interface MyDao { @Insert(onConflict = OnConflictStrategy.REPLACE) fun insertUsers(vararg users: User) @Insert fun insertBothUsers(user1: User, user2: User) @Insert fun insertUsersAndFriends(user: User, friends: List<User>) }
update ,方法的返回值是表中更新的行数
@Dao interface MyDao { @Update fun updateUsers(vararg users: User) }
Delete 会从数据库中删除一组以参数形式给出的实体。这个是根据主键来查找删除的 ,方法的返回值是int,表示删除的行数
@Dao interface MyDao { @Delete fun deleteUsers(vararg users: User) }
Query 是最常用的,
@Dao interface MyDao { @Query("SELECT * FROM user") fun loadAllUsers(): Array<User> }
//条件查询 将参数传递到sql中, 使用 : 将参数绑定到sql中
@Dao interface MyDao { //条件查询 将参数传递到sql中, 使用 : @Query("SELECT * FROM user WHERE age > :minAge") fun loadAllUsersOlderThan(minAge: Int): Array<User> } @Dao interface MyDao { @Query("SELECT * FROM user WHERE age BETWEEN :minAge AND :maxAge") fun loadAllUsersBetweenAges(minAge: Int, maxAge: Int): Array<User> @Query("SELECT * FROM user WHERE first_name LIKE :search " + "OR last_name LIKE :search") fun findUserWithName(search: String): List<User> }
返回列的子集 大多数情况下,您只需获取实体的几个字段。例如,您的界面可能仅显示用户的名字和姓氏,而不是用户的每一条详细信息。通过仅提取应用界面中显示的列,您可以节省宝贵的资源,并且您的查询也能更快完成。
借助 Room,您可以从查询中返回任何基于 Java 的对象,前提是结果列集合会映射到返回的对象。例如,您可以创建以下基于 Java 的普通对象 (POJO) 来获取用户的名字和姓氏:
data class NameTuple( @ColumnInfo(name = "first_name") val firstName: String?, @ColumnInfo(name = "last_name") val lastName: String? )
现在,您可以在查询方法中使用此 POJO:
@Dao interface MyDao { @Query("SELECT first_name, last_name FROM user") fun loadFullName(): List<NameTuple> }
Room 知道该查询会返回 first_name 和 last_name 列的值,并且这些值会映射到 NameTuple 类的字段。因此,Room 可以生成正确的代码。如果查询返回太多的列,或者返回 NameTuple 类中不存在的列,则 Room 会显示一条警告。
使用RxJava进行响应式查询
dependencies { def room_version = "2.1.0" implementation 'androidx.room:room-rxjava2:$room_version' }
Room 为 RxJava2 类型的返回值提供了以下支持: @Query 方法:Room 支持 Publisher、Flowable 和 Observable 类型的返回值。 @Insert、@Update 和 @Delete 方法:Room 2.1.0 及更高版本支持 Completable、Single 和 Maybe 类型的返回值。
@Dao interface MyDao { @Query("SELECT * from user where id = :id LIMIT 1") fun loadUserById(id: Int): Flowable<User> // Emits the number of users added to the database. @Insert fun insertLargeNumberOfUsers(users: List<User>): Maybe<Int> // Makes sure that the operation finishes successfully. @Insert fun insertLargeNumberOfUsers(varargs users: User): Completable /* Emits the number of users removed from the database. Always emits at least one user. */ @Delete fun deleteAllUsers(users: List<User>): Single<Int> }
… …