Android的Room持久性库慷慨地包含适用于对象或集合的@Insert和@Update注释 . 然而,我有一个用例(包含模型的推送通知)需要UPSERT,因为数据可能存在或可能不存在于数据库中 .
Sqlite本身没有upsert,并且在SO question中描述了变通方法 . 鉴于那里的解决方案,如何将它们应用于房间?
更具体地说,如何在Room中实现不会破坏任何外键约束的插入或更新?使用带onConflict = REPLACE的insert将导致onDelete调用该行的任何外键 . 在我的情况下,onDelete导致级联,重新插入行将导致其他表中的行与外键被删除 . 这不是预期的行为 .
7 回答
为了更优雅的方式,我建议两个选项:
使用
IGNORE
作为OnConflictStrategy
检查insert
操作的返回值(如果它等于-1则表示未插入行):使用
FAIL
作为OnConflictStrategy
处理insert
操作中的异常:也许你可以让你的BaseDao像这样 .
使用@Transaction保护upsert操作,并仅在插入失败时尝试更新 .
我找不到会插入或更新的SQLite查询而不会对我的外键造成不必要的更改,所以我选择先插入,忽略冲突(如果它们发生),然后立即更新,再次忽略冲突 .
insert和update方法受到保护,因此外部类只能查看和使用upsert方法 . 请记住,这不是真正的upsert,好像任何MyEntity POJOS都有空字段,它们将覆盖当前数据库中的内容 . 这对我来说不是一个警告,但它可能适用于您的应用程序 .
如果表有多个列,则可以使用
@Insert(onConflict = OnConflictStrategy.REPLACE)
替换一行 .
参考 - Go to tips Android Room Codelab
只是更新如何使用Kotlin保留模型的数据(可能在例如计数器中使用它):
您还可以使用@Transaction和数据库构造函数变量来处理更复杂的事务,使用database.openHelper.writableDatabase.execSQL(“SQL STATEMENT”)
我能想到的另一种方法是通过查询通过DAO获取实体,然后执行任何所需的更新 . 由于必须检索完整实体,因此在运行时方面,与此线程中的其他解决方案相比,这可能效率较低,但在允许的操作(例如,要更新的字段/变量)方面允许更大的灵活性 .
例如 :
这种说法应该是可能的: