Room官方文档(翻译)7.使用Room引用复杂数据

1,805 阅读4分钟

Room 提供原生类型和装箱类型的类型转换,但是不允许实体间的对象引用。本文说明了如何使用转换器以及Room为什么不支持对象引用。

使用类型转换器

有时候,你想在数据库某列存储自定义数据类型。为了支持这种自定义类型,提供一个 TypeConverter,可以在定义类型和已知类型之间转换以便Room保存。

例如,如果我们想保存Date,可以编写如下TypeConverter在数据库中存储Unix的等效时间戳:

class Converters {
    @TypeConverter
    fun fromTimestamp(value: Long?): Date? {
        return value?.let { Date(it) }
    }

    @TypeConverter
    fun dateToTimestamp(date: Date?): Long? {
        return date?.time?.toLong()
    }
}

前面的示例中定义了两个函数,一个将Date转为Long,一个将Long转为DateRoom知道如何保存 Long,所以可以利用转换器保存Date

下一步,在 数据库类上添加@TypeConverters 注解以便将转换器应用于你所定义的实体和DAO中:

@Database(entities = arrayOf(User::class), version = 1)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}

使用这些转换器,你可以在查询中像使用原生类型一样使用自定义类型:

User

@Entity
data class User(private val birthday: Date?)

UserDao

@Dao
interface UserDao {
    @Query("SELECT * FROM user WHERE birthday BETWEEN :from AND :to")
    fun findUsersBornBetweenDates(from: Date, to: Date): List<User>
}

还可以限制@TypeConverters使用范围,包括单个实体,DAODAO方法。详情请参看@TypeConverters

理解 Room 为什么不允许对象间引用

关键点:Room 不允许多个实体的引用,相反,你必须在 app 中明确请求你需要的数据。

将数据库中的关系映射到对象模型是一种比较常见的作法,并且这在服务器端运行良好,甚至在程序访问字段时才加载它们,仍然表现良好。

然而,在客户端,这种懒加载模式因经常运行在UI线程而变得不可行。在UI线程查询磁盘上的信息会引发严重的性能问题。UI线程大概有16ms来计算和绘制视图,所以即使查询只占用其中的5ms,仍然可能导致没有足够的时间来绘制视图造成掉帧。 如果有一个单独的事务在并行运行,或者磁盘正在做其他密集型运算,那么查询工作将会消耗更多的时间。如果不使用惰性加载,那么你将获取到很多用不着的数据,造成内存消耗问题。

开发者知道怎样结合app用例使对象关系映射运行得更好,通常开发者在app和UI之间共享模型,但是这种方式的扩展性不是很好,因为随着UI的变化,共享模型会产生开发者难以预料和调试的问题。

举个栗子,假设某个UI会加载书本对象的集合,每本书都有一作者对象。最初设计中你可能会使用惰性加载来书本实例检索作者。第一次检索作者字段会查询数据库,过了一段时间,你意识到你需要将作者名称显示在UI上,那么你可以使用下面的代码很方便的访问

authorNameTextView.text = book.author.name

然而,这种看似无害的更改会导致在主线程上查询作者表。

如果提前查询作者信息,如果某一天不再需要这些数据,更改这些数据的加载方式是很困难的。例如,如果你的app不需要显示作者信息,app加载的这些用不着的数据就是浪费内存空间。如果作者类引用另一个类,如书籍类,app的运行效率将进一步降低。 要使用Room同时引用多个实体,你需要写一个连接表查询而不是创建一个包含每一个实体的POJO,这种结构良好的模型,结合Room强大的查询验证功能,允许您的应用程序在加载数据时消耗更少的资源,从而提高应用程序的性能和用户体验。


Room官方文档(翻译)0.概览

Room官方文档(翻译)1.使用Room实体定义数据

Room官方文档(翻译)2.定义对象间的关系

Room官方文档(翻译)3.在数据库中创建视图

Room官方文档(翻译)4.使用Room DAOs访问数据

Room官方文档(翻译)5.迁移数据库

Room官方文档(翻译)6.测试数据库

Room官方文档(翻译)7.使用Room引用复杂数据