我有几个从服务器定期更新的表(Benefit
,Branch
,Coupon
)和另外两个仅是本地的表(FavoriteBenefit
,{{ 1}})。 ER图如下所示:
无论何时在服务器上删除UsedCoupon
时,我都还想从Benefit
中删除适当的实体。为此,我可以使用FavoriteBenefit
,并且在数据库中不再存在父项onDelete = ForeignKey.CASCADE
时,Benefit
也将被删除。听起来不错。
我每次使用都会出现问题
FavoriteBenefit
以更新数据库中的收益。 @Insert(onConflict = OnConflictStrategy.REPLACE)
实际上执行REPLACE
和DELETE
,但是INSERT
内部触发DELETE
中的onDelete
,因此该表中的所有数据都被删除也是
(FavoriteBenefit
和Coupon
表也会发生类似的问题。)
我正在寻找一种方法来临时禁用外键约束,直到交易结束。也就是说,请勿在交易过程中验证外键,而只能在交易结束时验证外键。 我仍然希望Room自动删除没有有效父代的实体。
似乎通过在UsedCoupon
定义上设置deferred = true
来标记foreign key as defferred应该正是我想要达到的目标。
布尔延迟()
可以将外键约束推迟到事务完成之前。如果您要在单个事务中向数据库中批量插入,这将很有用。默认情况下,外键约束是即时的,但是您可以通过将此字段设置为true来更改它。
但是即使我设置了@ForeignKey
标志,它似乎也没有任何作用,因为deferred
每次仍在被删除。
我误解了FavoriteBenefit
标志吗?
答案 0 :(得分:3)
原因是ON DELETE CASCADE
是类似于触发器的动作,它会立即执行。请参见issue和comment。
PostgreSQL团队还提供了更详细的explanation:
是的,据我们所知,这是根据SQL规范。约束检查可以 推迟到交易结束,但“推荐行为”不是 延期的它们总是在触发语句期间发生。对于 实例SQL99将级联删除的结果描述为 引用行立即被“标记为删除”,然后
- 所有标记为删除的行均被有效删除 在SQL语句的末尾,在检查任何 完整性约束。
看看similar SQLite question,其中包含一些解决方法。
答案 1 :(得分:0)
我不知道它是否仍然与您有关,但我也遇到类似的问题。我尝试将deferred
标记放在两个位置:关系类本身和作为杂注。在两种情况下,由于OnConflictStrategy.REPLACE
策略(您执行过DELETE
操作)都删除了一个项目。我发现的解决方法是使用类似“ UPSERT
”的查询。去年在SQLite中添加了UPSERT语句支持,因此Room还不支持该语句,但是您可以编写如下内容:
@Dao
abstract class BaseDao<T> {
/**
* Insert an item in the database.
*
* @param item the item to be inserted.
* @return The SQLite row id
*/
@Insert(onConflict = OnConflictStrategy.IGNORE)
abstract fun insert(item: T): Long
/**
* Insert an array of items in the database.
*
* @param items the items to be inserted.
* @return The SQLite row ids
*/
@Insert(onConflict = OnConflictStrategy.IGNORE)
abstract fun insert(items: List<T>): List<Long>
/**
* Update an item from the database.
*
* @param item the item to be updated
*/
@Update
abstract fun update(item: T)
/**
* Update an array of items from the database.
*
* @param item the item to be updated
*/
@Update
abstract fun update(item: List<T>)
@Transaction
fun upsert(item: T) {
val id = insert(item)
if (id == -1L) {
update(item)
}
}
@Transaction
fun upsert(items: List<T>) {
val insertResult = insert(items)
val updateList = mutableListOf<T>()
for (i in insertResult.indices) {
if (insertResult[i] == -1L) {
updateList.add(items[i])
}
}
if (updateList.isNotEmpty()) {
update(updateList)
}
}
}
代码背后的逻辑很简单-如果表已经包含记录(在插入之后通过过滤rowid
进行检查),那么我们应该对其进行更新。
答案 2 :(得分:0)
我之前遇到过这个问题,因此我通过创建名为delsert
的新语法来解决了这个问题。
这是一个示例:
@Query("DELETE FROM patient")
public abstract void delete();
@Query("DELETE FROM patient WHERE p_id IN (SELECT p_id FROM patient WHERE p_id NOT IN (:ids))")
public abstract void deleteUnused(List<Long> ids);
@Transaction
public void delsert(List<Patient> patientList) {
if (CommonUtils.isListEmpty(patientList)) {
this.delete();
return;
}
List<Long> idsPatient = new ArrayList<>();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
patientList.forEach(order -> idsPatient.add(order.getId()));
else
for (Patient id : patientList) idsPatient.add(id.getId());
deleteUnused(idsPatient);
insert(patientList);
}