当前位置: 首页 > 知识库问答 >
问题:

Android房间持久性库:Upsert

澹台阳秋
2023-03-14

Android的Room持久性库优雅地包含了适用于对象或集合的@Insert和@Update注释。但是,我有一个用例(包含模型的推送通知),它需要UPSERT,因为数据库中可能存在数据,也可能不存在数据。

Sqlite本机不具有up,在这个SO问题中描述了解决方法。鉴于那里的解决方案,如何将它们应用于Room?

更具体地说,如何在房间中实现插入或更新,而不破坏任何外键约束?使用insert with onConflict=REPLACE将导致调用该行任何外键的onDelete。在我的例子中,onDelete导致级联,而重新插入一行将导致删除其他表中具有外键的行。这不是预期的行为。

共有3个答案

蒋培
2023-03-14

我找不到一个SQLite查询,它可以插入或更新而不会对我的外键造成不必要的更改,因此我选择先插入,如果发生冲突则忽略,然后立即更新,再次忽略冲突。

insert和update方法受到保护,因此外部类只能查看和使用upsert方法。请记住,这不是真正的upsert,因为如果任何MyEntity POJO具有空字段,它们将覆盖数据库中当前的内容。这不是对我的警告,但可能是对你的申请。

@Insert(onConflict = OnConflictStrategy.IGNORE)
protected abstract void insert(List<MyEntity> entities);

@Update(onConflict = OnConflictStrategy.IGNORE)
protected abstract void update(List<MyEntity> entities);

@Transaction
public void upsert(List<MyEntity> entities) {
    insert(models);
    update(models);
}
孙阳舒
2023-03-14

对于更优雅的方式,我建议两种选择:

检查insert操作的返回值,使用IGNORE作为OnConflictStrategy(如果它等于-1,则表示未插入行):

@Insert(onConflict = OnConflictStrategy.IGNORE)
long insert(Entity entity);

@Update(onConflict = OnConflictStrategy.IGNORE)
void update(Entity entity);

@Transaction
public void upsert(Entity entity) {
    long id = insert(entity);
    if (id == -1) {
        update(entity);   
    }
}

处理异常从插入操作与FAIL作为On冲突策略

@Insert(onConflict = OnConflictStrategy.FAIL)
void insert(Entity entity);

@Update(onConflict = OnConflictStrategy.FAIL)
void update(Entity entity);

@Transaction
public void upsert(Entity entity) {
    try {
        insert(entity);
    } catch (SQLiteConstraintException exception) {
        update(entity);
    }
}
羊舌兴德
2023-03-14

也许你可以这样做你的BaseDao。

使用@Transaction保护upsert操作,并仅在插入失败时尝试更新。

@Dao
public abstract class BaseDao<T> {
    /**
    * Insert an object in the database.
    *
     * @param obj the object to be inserted.
     * @return The SQLite row id
     */
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    public abstract long insert(T obj);

    /**
     * Insert an array of objects in the database.
     *
     * @param obj the objects to be inserted.
     * @return The SQLite row ids   
     */
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    public abstract List<Long> insert(List<T> obj);

    /**
     * Update an object from the database.
     *
     * @param obj the object to be updated
     */
    @Update
    public abstract void update(T obj);

    /**
     * Update an array of objects from the database.
     *
     * @param obj the object to be updated
     */
    @Update
    public abstract void update(List<T> obj);

    /**
     * Delete an object from the database
     *
     * @param obj the object to be deleted
     */
    @Delete
    public abstract void delete(T obj);

    @Transaction
    public void upsert(T obj) {
        long id = insert(obj);
        if (id == -1) {
            update(obj);
        }
    }

    @Transaction
    public void upsert(List<T> objList) {
        List<Long> insertResult = insert(objList);
        List<T> updateList = new ArrayList<>();

        for (int i = 0; i < insertResult.size(); i++) {
            if (insertResult.get(i) == -1) {
                updateList.add(objList.get(i));
            }
        }

        if (!updateList.isEmpty()) {
            update(updateList);
        }
    }
}
 类似资料:
  • 转换器.KT FoodDatabase.kt 当我运行以下代码创建数据库时: 我还附上我的评分文件: 有人遇到过这个问题吗?

  • 最近几天,我一直在学习新的Android架构组件。跟进一些博文后,留档

  • 我一直在使用codelabs实现我的房间数据库持久化。现在,在将数据插入房间数据库后,我正在尝试获取最新的rowID。但是,我被困在存储库中,试图从异步任务返回rowID。 LogEntity.java 洛格道。JAVA 日志数据库。JAVA 日志存储库。JAVA 我放了两个星号,因为我不确定在这里要做什么来获取insert RowID,以及我的AsyncTask是否完全正确。 LogViewMo

  • 我刚刚使用Android room persistence library,通常我使用DBHeper来管理数据,但我正在尝试使用这项技术,我想知道如何在room persistence library中使用这个查询 但是,当我在“喜欢”之后使用%时,我会在红线上划下它

  • 我正在迁移一个运行在Android上的现有应用程序,并正在将其迁移到Raspberry Pi。只是想知道是否有可能直接迁移Room数据库的使用。

  • 如何使用房间持久性库删除特定表上的所有条目?我需要删除表,但我找不到任何信息如何做到这一点。 仅当数据库正在迁移或加载所有条目并删除它们时:)