124

Android の Room 永続化ライブラリには、オブジェクトまたはコレクションに対して機能する @Insert および @Update アノテーションが含まれています。ただし、データがデータベースに存在する場合と存在しない場合があるため、UPSERT が必要なユース ケース (モデルを含むプッシュ通知) があります。

Sqlite にはネイティブの upsert がなく、回避策はこのSO questionに記載されています。そこにあるソリューションを考えると、それらを Room にどのように適用しますか?

より具体的には、外部キーの制約に違反しない挿入または更新を Room に実装するにはどうすればよいですか? onConflict=REPLACE で挿入を使用すると、その行への外部キーの onDelete が呼び出されます。私の場合、 onDelete はカスケードを引き起こし、行を再挿入すると、外部キーを持つ他のテーブルの行が削除されます。これは意図した動作ではありません。

4

12 に答える 12

111

おそらく、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);
        }
    }
}
于 2018-06-07T08:34:26.080 に答える
90

それを行うためのよりエレガントな方法については、2 つのオプションをお勧めします。

as aを使用したinsert操作からの戻り値を確認します(-1 に等しい場合は、行が挿入されなかったことを意味します):IGNOREOnConflictStrategy

@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);   
    }
}

としてのinsert操作からの例外の処理:FAILOnConflictStrategy

@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);
    }
}
于 2018-02-06T11:17:27.540 に答える