Room使用篇二

DAO 是什么

Dao 对象构成了 Room 的主要组件,因为每个 DAO 都包含一些方法比如数据的增删改查,这些方法提供对==数据库的访问==

DAO 既可以是接口,也可以是抽象类。如果是抽象类,则该 DAO 可以选择有一个以 RoomDatabase 为唯一参数的构造函数。Room 会在编译时创建每个 DAO 实现。

注意:除非已对构建器调用 allowMainThreadQueries(),否则 Room 不支持在主线程上访问数据库,因为它可能会长时间锁定界面。异步查询(返回 LiveData 或 Flowable 实例的查询)无需遵守此规则,因为此类查询会根据需要在后台线程上异步运行查询。

数据访问方法

添加

@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>)
}

如果 @Insert 方法只接收 1 个参数,则它可以返回 long,这是插入项的新 rowId。如果参数是数组或集合,则应返回 long[] 或 List<Long>。

onConflict 属性是冲突策略

  • REPLACE 返回插入行id的DAO方法将永远不会返回-1,因为即使存在冲突,此策略也将始终插入一行,用新的数据行替换旧的数据行。
  • ROLLBACK 不适用于Android当前的SQLite绑定。使用{@link#ABORT}来回滚事务。(过时)
  • ABORT 回滚冲突的事务
  • FAIL 不按预期工作。事务被回滚。使用{@link#ABORT}。(过时)
  • IGNORE 如果存在冲突,此策略将忽略行,所以不会插入,并返回-1,保持现有数据行

更新

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

会根据传入对象的主键匹配修改
此方法返回一个 int 值,以指示数据库中更新的行数。

删除

它使用主键查找要删除的实体。

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

此方法返回一个 int 值,以指示从数据库中删除的行数

查询

@Query 是 DAO 类中使用的主要注释。它允许您对数据库执行读/写操作。每个 @Query 方法都会在编译时进行验证,因此如果查询出现问题,则会发生编译错误,而不是运行时失败。

Room 还会验证查询的返回值,以确保当返回的对象中的字段名称与查询响应中的对应列名称不匹配时,Room 可以通过以下两种方式之一提醒您:

  • 如果只有部分字段名称匹配,则会发出警告。
  • 如果没有任何字段名称匹配,则会发出错误。

简单查询

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

这是一个极其简单的查询,可加载所有用户。在编译时,Room 知道它在查询用户表中的所有列。==如果查询包含语法错误,或者数据库中没有用户表格,则 Room 会在您的应用编译时显示包含相应消息的错误。==

通过参数查询

在大多数情况下,您需要将参数传递给查询以执行过滤操作,例如仅显示某个年龄以上的用户。要完成此任务,请在 Room 注释中使用方法参数,如以下代码段所示:

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

在编译时处理此查询时,Room 会将 :minAge 绑定参数与 minAge 方法参数进行匹配。Room 通过参数名称进行匹配。如果有不匹配的情况,则应用编译时会出现错误。

您还可以在查询中传递多个参数或多次引用这些参数,如以下代码段所示:

@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 会显示一条警告。

从指定集合中查询

您的部分查询可能要求您传入数量不定的参数,参数的确切数量要到运行时才知道。例如,您可能希望从部分区域中检索所有用户的相关信息。Room 知道参数何时表示集合,并根据提供的参数数量在运行时自动将其展开。

@Dao
interface MyDao {
    @Query("SELECT first_name, last_name FROM user WHERE region IN (:regions)")
    fun loadUsersFromRegions(regions: List<String>): List<NameTuple>
}

查询返回cursor

如果应用的逻辑要求直接访问返回行,您可以从查询中返回 Cursor 对象,如以下代码段所示:

@Dao
interface MyDao {
    @Query("SELECT * FROM user WHERE age > :minAge LIMIT 5")
    fun loadRawUsersOlderThan(minAge: Int): Cursor
}

注意:强烈建议不要使用 Cursor API,因为它无法保证行是否存在或者行包含哪些值。只有当您已具有需要光标且无法轻松重构的代码时,才使用此功能。

查询多个表格

您的部分查询可能需要访问多个表格才能计算出结果。借助 Room,您可以编写任何查询,因此您也可以联接表格。此外,如果响应是可观察数据类型(如 Flowable 或 LiveData),Room 会观察查询中引用的所有表格,以确定是否存在无效表格。

以下代码段展示了如何执行表格联接以整合以下两个表格的信息:一个表格包含当前借阅图书的用户,另一个表格包含当前处于已被借阅状态的图书的数据。

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

您还可以从这些查询中返回 POJO。例如,您可以编写一条加载某位用户及其宠物名字的查询,如下所示:

@Dao
interface MyDao {
    @Query(
        "SELECT user.name AS userName, pet.name AS petName " +
        "FROM user, pet " +
        "WHERE user.id = pet.user_id"
    )
    fun loadUserAndPetNames(): LiveData<List<UserPet>>

    // You can also define this class in a separate file.
    data class UserPet(val userName: String?, val petName: String?)
}

查询返回类型

Room 支持各种查询方法的返回类型,包括与特定框架或 API 进行互操作的特殊返回类型。
使用流进行响应式查询

使用Flow返回类型

在 Room 2.2 及更高版本中,您可以使用 Kotlin 的 Flow 功能确保应用的界面保持最新状态。如需在基础数据发生变化时使界面自动更新,请编写返回 Flow 对象的查询方法:

@Query("SELECT * FROM User")
fun getAllUsers(): Flow<List<User>>

只要表中的任何数据发生变化,返回的 Flow 对象就会再次触发查询并重新发出整个结果集。

缺点:只要对表中的任何行进行更新(无论该行是否在结果集中),Flow 对象就会重新运行查询。

解决:通过将 distinctUntilChanged() 运算符应用于返回的 Flow 对象,可以确保仅在实际查询结果发生更改时通知界面:

 @Dao
 abstract class UsersDao {
     @Query("SELECT * FROM User WHERE username = :username")
     abstract fun getUser(username: String): Flow<User>

     fun getUserDistinctUntilChanged(username:String) =
            getUser(username).distinctUntilChanged()
 }

注意:如需将 Room 与 Flow 一起使用,您需要在 build.gradle 文件中包含 room-ktx 工件。如需了解详情,请参阅声明依赖项。

使用 Kotlin 协程进行异步查询

您可以将 suspend Kotlin 关键字添加到 DAO 方法中,以使用 Kotlin 协程功能使这些方法成为异步方法。这样可确保不会在主线程上执行这些方法。

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

    @Update
    suspend fun updateUsers(vararg users: User)

    @Delete
    suspend fun deleteUsers(vararg users: User)

    @Query("SELECT * FROM user")
    suspend fun loadAllUsers(): Array<User>
}

使用 LiveData 进行可观察查询

执行查询时,您通常会希望应用的界面在数据发生变化时自动更新。为此,请在查询方法说明中使用 LiveData 类型的返回值。当数据库更新时,Room 会生成更新 LiveData 所必需的所有代码。

@Dao
interface MyDao {
    @Query("SELECT first_name, last_name FROM user WHERE region IN (:regions)")
    fun loadUsersFromRegionsSync(regions: List<String>): LiveData<List<User>>
}

注意:自版本 1.0 起,Room 会根据在查询中访问的表格列表决定是否更新 LiveData 实例。

使用 RxJava 进行响应式查询

Room 为 RxJava2 类型的返回值提供了以下支持:

  • @Query 方法:Room 支持 Publisher、Flowable 和 Observable 类型的返回值。
  • @Insert、@Update 和 @Delete 方法:Room 2.1.0 及更高版本支持 Completable、Single<T> 和 Maybe<T> 类型的返回值。

如需使用此功能,请在应用的 build.gradle 文件中添加最新版本的 rxjava2 工件:

dependencies {
        def room_version = "2.1.0"
        implementation 'androidx.room:room-rxjava2:$room_version'
    }

以下代码段展示了几个如何使用这些返回类型的示例:

@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>
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,904评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,581评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,527评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,463评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,546评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,572评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,582评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,330评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,776评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,087评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,257评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,923评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,571评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,192评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,436评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,145评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352

推荐阅读更多精彩内容

  • Room持久性库在SQLite的基础上提供了一个抽象层,允许流利的数据库访问,同时利用的SQLite的全部力量。该...
    yyg阅读 5,377评论 0 5
  • WHAT 什么是数据存储? 缓存相关数据。当设备无法访问网络时,用户仍可在离线状态下浏览相应内容。设备重新连接到网...
    Method阅读 2,415评论 0 0
  • 要使用Room持久性库访问应用程序的数据,您需要使用数据访问对象或DAO。 这组Dao对象构成了Room的主要组件...
    鹿小纯0831阅读 1,199评论 0 2
  • 一、前言: Room 是一个对象关系映射(ORM)库。可以很容易将 SQLite 表数据转换为 Java 对象。R...
    因为我的心阅读 7,759评论 0 7
  • 简介 Room库在SQLite上提供了一个抽象层,允许在充分利用SQLite的功能的同时进行更健壮的数据库访问。可...
    fomin阅读 1,817评论 0 2