【问题标题】:Android Room with deferred foreign key具有延迟外键的 Android Room
【发布时间】:2019-09-24 00:04:11
【问题描述】:

我有几个从服务器定期更新的表(BenefitBranchCoupon)和另外两个仅本地的表(FavoriteBenefitUsedCoupon)。 ER图如下所示:

每当在服务器上删除Benefit 时,我还想从FavoriteBenefit 中删除适当的实体。为此,我可以使用onDelete = ForeignKey.CASCADE,并且每当数据库中不再存在父Benefit 时,FavoriteBenefit 也会被删除。听起来不错。

每当我使用时就会出现问题 @Insert(onConflict = OnConflictStrategy.REPLACE) 更新数据库中的福利。 REPLACE 实际上执行了 DELETEINSERTDELETE 在内部触发 onDeleteFavoriteBenefit,因此,该表中的所有数据也会被删除。

CouponUsedCoupon 表也会出现类似问题。)


我正在寻找一种在事务结束之前暂时禁用外键约束的方法。也就是说,不要在交易期间验证外键,而只在交易结束时验证。 我仍然希望 Room 自动删除没有有效父级的实体。


似乎通过在@ForeignKey 定义上设置deferred = true 来标记foreign key as defferred 应该完全符合我的目标。

布尔延迟()

外键约束可以推迟到事务完成。如果您在单个事务中对数据库进行批量插入,这很有用。默认情况下,外键约束是即时的,但您可以通过将此字段设置为 true 来更改它。

但即使我设置了deferred 标志,它似乎也没有效果,因为FavoriteBenefit 仍然每次都被删除。

我对 deferred 标志的理解有误吗?

【问题讨论】:

    标签: android foreign-keys android-room deferred


    【解决方案1】:

    我不知道它是否仍然与您相关,但我遇到了类似的问题。我试图将deferred 标志放在两个地方:关系类本身和作为编译指示。在这两种情况下,由于OnConflictStrategy.REPLACE 策略(如您所提到的那样执行DELETE 操作),项目都被删除了。我发现的解决方法是使用“UPSERT-like”查询。 UPSERT 语句支持是去年在 SQLite 中添加的,所以 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)
            }
        }
    }
    

    代码背后的逻辑很简单——如果表已经包含记录(插入后通过过滤rowids 进行检查)——我们应该更新它们。

    Credits

    【讨论】:

      【解决方案2】:

      我之前遇到过这个问题,我通过创建名为 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);
          }
      

      【讨论】:

        【解决方案3】:

        原因是ON DELETE CASCADE 是一个类似触发器的动作,它会立即执行。请参阅issuecomment

        还有来自 PostgreSQL 团队的更详细的explanation

        是的,据我们所知,这是每个 SQL 规范。约束检查可以 推迟到交易结束,但“参考行动”不是 可延期。它们总是在触发语句期间发生。为了 实例 SQL99 将级联删除的结果描述为 引用行立即“标记为删除”,然后

        1. 所有标记为删除的行都被有效删除 在 SQL 语句的末尾,在检查任何 完整性约束。

        看看similar SQLite question,其中包含一些解决方法。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-03-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-11-18
          • 2012-11-03
          相关资源
          最近更新 更多