Room是安卓中SQLite上的一个抽象层应用框架,可以更轻松、更好地保存数据。如果你还没用过Room的话,那就赶紧看看这篇文章吧,这篇文章将分别从以下七个角度谈谈使用Room时的注意事项。
1.预先填充数据库
如果你需要在创建数据库或打开数据库后立即向数据库添加默认数据的话,可以在调用用RoomDatabase#Callback类中addCallback方法并覆盖其中一个onCreate或onOpen。
onCreate在第一次创建数据库时将调用,onOpen在打开数据库时调用。由于只有在这些方法返回后才能访问DAO(数据访问对象),我们在这个方法中创建一个子线程,然后获得数据库的引用,最后获取DAO再插入数据。
注意:如果应用程序在首次启动时崩溃,并且数据库已经创建完成但是数据还没插入表中的话,这些预加载数据将再也不会插入表中了,因为onCreate方法不会再被调用了。
2.使用DAO的继承功能
如果应用中要创建多个DAO,可以将最基本的CRUD放在一个基类里,可以创建一个 BaseDao<T> 类,并定义通用的@Insert,@Update和@Delete方法。让每个DAO继承BaseDao并添加单个DAO特定的方法。
DAO必须是接口或抽象类,因为Room在编译时会生成它们的实现,包括来自BaseDao的方法。
3.在事务中执行查询
注释方法@Transaction可确保在该方法中执行的所有数据库操作都将在一个事务中运行,在方法体中抛出异常时,事务将失败。
在以下情况下,可能需要对带有@Query标注且包含select语句的方法附加@Transaction注释:
当查询结果相当大时。通过在一个事务中查询数据库,可以确保即使查询结果不适合单个Cursor Window,它也不会被破坏。当查询结果是带@Relation字段的POJO时,这些字段是单独的查询,因此在单个事务中运行它们将保证查询结果一致。@Delete,@Update并@Insert有多个参数的方法在一个事务中的自动运行。4.只读需要的字段
在实际开发过程中,很多程序员为了图方便,查询的时候习惯一下子把所有字段查出来。其实这种处理非常消耗内存,应该仅加载最终使用的字段子集,这种做法还有一个好处,就是可以通过降低IO成本来提高查询速度,因为Room将执行列和对象之间的映射。
比如说项目中有一个复杂的User对象,但是在某些地方,我们不需要显示所有这些信息。因此,我们可以创建一个UserMinimal只包含所需数据的对象,并且在DAO类中,我们定义查询并从users表中选择正确的列。
5.在具有外键的实体之间实施约束
尽管Room不直接支持对象之间关联关系,但它允许您在实体之间定义外键约束。
Room具有@ForeignKey注释,是@Entity注释的一部分,允许使用SQLite外键功能。它会跨表强制执行约束,以确保在修改数据库时数据关系的完整性。在实体上,定义要引用的父实体、其中的列以及当前实体中的列。
我们以一个User和一个Pet类举例,该Pet有一个所有者,这便是外键所引用的用户ID。
除此之外,还可以定义在数据库中删除或更新父实体时要采取的操作,比如NO_ACTION、RESTRICT、SET_NULL、SET_DEFAULT或者CASCADE,这些功能与SQLite使用类似,这里就不再赘述。
注意:在Room中,SET_DEFAULT实为SET_NULL,因为Room还不允许为列设置默认值。
6.通过@Relation简化一对多查询
在前面的User- Pet中,它们之间有一个明确的一对多关系:一个用户可以拥有多个宠物。假设我们想要获得一个带宠物的用户列表List<UserAndAllPets>该怎么办呢?要手动执行此操作,我们需要实现2个查询:一个用于获取所有用户的列表,另一个用于根据用户ID获取宠物列表,然后我们将遍历用户列表并查询Pets表。
为了简化这一点,我们可以用Room提供的@Relation注释来自动获取相关实体。注意一点,@Relation只能应用于一个List或Set多个对象。同时UserAndAllPets类也要修改:
并且在DAO中,虽然我们定义了一个单一的查询,Room将同时查询Users与Pets表和处理对象的映射。
7.避免观察者接收不必要的通知
我们还是以上面的User为例,每当用户更新表中User数据时,观察者都会接收到数据变化。但是,如果对于Users表中发生字段变化观察者并不感兴趣的话,这时候还以收到通知就显得没必要了。更要命的是,如果您的查询涉及多个表,那么只要其中任何一个表发生了变化,观察都同样会收到通知。
为了避免这个问题,首先我们来看下Room通知机制:
当一个DELETE、UPDATE或INSERT执行时,SQLite就会被触发。Room创建一个invalidationTracker,使用观察者跟踪观察到的表中的任何更改。LiveData和Flowable查询都依赖于InvalidationTracker.Observer#onInvalidated通知,收到此消息后,将触发重新查询。也就是说,Room只知道表已被修改,但不知道为什么修改,也不知道改变了什么。因此,在重新查询之后,查询的结果由LiveData或者Flowable发送给观察者。由于Room不在内存中保存任何数据且不能假设对象具有equals(),因此无法判断这是否是相同的数据。
如果可观察的查询是使用flowables实现的,请使用flowable#distinctuntlchanged。
如果查询返回的是一个LiveData,则可以使用MediatorLiveData。
在DAO中,确保返回不同LiveData的方法用public来修饰,而查询数据库的方法用protected。
如果要显示返回列表,可以考虑使用Paging Library库并返回LivePagedListBuilder,因为该库将有助于自动计算列表项之间的差异并更新UI。
看完了上面7个提示,相信大家对Room已经有了一个初步了解,希望通过这篇文章的学习,让大家在实际开发过程中少走弯路。