Room @Transaction注解与Flowable配合失效问题求助
你遇到的问题核心在于Room的@Transaction注解仅对DAO层的方法生效,放在数据源(比如你的MyDataSource)类的方法上是完全无效的——Room根本不会识别这个注解,自然不会为你的updateMyEntity方法开启事务。
这就导致你的clear()和insertAll()是作为两个独立的事务执行的:第一个事务执行完clear(),数据库数据被清空,你的Flowable观察者立刻收到空列表;第二个事务执行insertAll(),数据插入后观察者又收到新的列表,所以出现了两次回调。
结合官方文档的解释
你提到的文档内容其实是针对DAO层方法的场景:
如果查询是异步的(例如返回{@link androidx.lifecycle.LiveData LiveData}或RxJava {@code Flowable}),则事务会在查询运行时而非方法调用时正确处理。将此注解放在{@link Insert}、{@link Update}或{@link Delete}方法上无影响,因为这些方法始终在事务内运行。类似地,如果方法标注了{@link Query}但执行INSERT、UPDATE或DELETE语句,它会自动被事务包裹,此注解也无效。
这段描述的前提是注解在DAO方法上,和你把注解放在数据源层的情况完全不相关,所以对你的问题不适用。
两种有效解决方案
方案一:将事务逻辑移到DAO层,使用@Transaction
把clear()和insertAll()的合并操作放到DAO接口中,给这个合并方法加上@Transaction注解——这是Room设计的标准用法:
@Dao interface MyDao { @Insert fun insertAll(list: List<MyEntity>) @Query("SELECT * FROM MyEntity") fun observe(): Flowable<List<MyEntity>> @Query("DELETE FROM MyEntity") fun clear() // 新增带事务的合并方法 @Transaction fun clearAndInsertAll(items: List<MyEntity>) { clear() insertAll(items) } }
然后在数据源中直接调用这个DAO方法即可:
class MyDataSource(database: MyDatabase) { private val myDao = database.myDao() fun updateMyEntity(items: List<MyEntity>) { myDao.clearAndInsertAll(items) } fun observeMyEntity(): Flowable<List<MyEntity>> { return myDao.observe() } }
方案二:使用RoomDatabase.runInTransaction()手动管理事务
这也是你已经验证有效的方法,通过database.runInTransaction()包裹所有需要在同一事务中执行的操作,确保它们原子性执行:
class MyDataSource(database: MyDatabase) { private val myDao = database.myDao() private val db = database fun updateMyEntity(items: List<MyEntity>) { db.runInTransaction { myDao.clear() myDao.insertAll(items) } } fun observeMyEntity(): Flowable<List<MyEntity>> { return myDao.observe() } }
总结
Room的@Transaction注解是DAO层专属的,不要在数据源/仓库层的方法上使用。如果需要合并多个数据库操作成一个事务,要么把逻辑移到DAO层加注解,要么用runInTransaction()手动包裹操作。
内容的提问来源于stack exchange,提问作者QuarK

