房间更新(或插入,如果不存在)行和返回计数更改行

ip6*_*696 21 android android-room

我需要更新,如果不存在,则插入行到ROOM DB.

我这样做: productRepository.updateProducts(productsResponse.getProductItems());

和:

@Override
public void updateProducts(final List<ProductItem> products) {
    new Thread(() -> {
        for (ProductItem item : products) {
            Product product = createProduct(item);
            productDao.insert(product);
        }
    }).start();
}
Run Code Online (Sandbox Code Playgroud)

在DAO中:

@Insert
void insert(Product products);
Run Code Online (Sandbox Code Playgroud)

但我有方法

@Update
void update(Product product);
Run Code Online (Sandbox Code Playgroud)

我有一些问题:

  1. 两种方法都是无效的.插入后如何返回保存的Product或boolean flag或insert count?

  2. 如果我试着打电话update而且我没有排它会插入吗?

  3. 如何更新(如果不是 - 插入)行和返回计数updatet或插入的行?

Dan*_*iev 37

  1. 一个带注释的方法@Insert可以返回一个long.这是插入行的新生成的ID.一个带注释的方法@Update可以返回一个int.这是更新行的数量.

  2. update将尝试使用where子句中主键的值更新所有字段.如果您的实体尚未保留在数据库中,则update查询将无法找到行并且不会更新任何内容.

  3. 你可以用@Insert(onConflict = OnConflictStrategy.REPLACE).这将尝试插入实体,如果存在具有相同ID值的现有行,它将删除它并将其替换为您尝试插入的实体.请注意,如果您使用自动生成的ID,则意味着生成的行将具有与替换的原始ID不同的ID.如果你想保留ID,那么你必须想出一个自定义的方法来做到这一点.

  • 这不是一个好的解决方案。它不执行插入或更新。这是一个替换,因此您将丢失未为更新指定的字段。 (5认同)
  • TL;DR Sqlite 替换操作可能会违反 onDelete 外键约束,但更新不会。这不包括用例 insertOrUpdate(),当您替换时,如果您在表上找到主键,它会删除该行,以便可以假设外键违反更新可以处理 OnCascade。 (4认同)

Umy*_*Umy 10

更新。

@Upsert 添加到 Room 2.5.0-alpha03。无需重新发明轮子)

感谢@Ashwini 的好主意。

@Dao
interface SendDao {
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    suspend fun insert(model: DataModel): Long

    @Update
    suspend fun update(model: DataModel): Int

    @Transaction
    suspend fun insertOrUpdate(model: DataModel): Long {
        val id = insert(model)
        return if (id==-1L) {
            update(model)
            model.id
        } else {
            id
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

所以我们应该调用:database.sendDao().insertOrUpdate(DataModel(...))


Rai*_*ker 9

已经有一段时间了,但是这个问题没有一个可以接受的答案,所以我会考虑一下。

至于@Danail Alexiev@Insert可以返回long@Update可以传回int

但是,您出于什么目的使用更新?如果您只想用新对象替换整个对象,则只需指定OnConflictStrategy

@Insert(onConflict = OnConflictStrategy.REPLACE)
void insert(Product products);
Run Code Online (Sandbox Code Playgroud)

结果将是:将添加不存在的项目,将已存在的项目替换为新项目。

如果您只需要更新一个参数(例如数量),则可以执行以下操作

  @Insert(onConflict = OnConflictStrategy.REPLACE)
  void insert(Product products);

  @Query("SELECT * from item WHERE id= :id")
  void getItemById(int id): List<Product>

  @Query("UPDATE item SET quantity = quantity + 1 WHERE id = :id")
  void updateQuantity(int id)

  void insertOrUpdate(Product item) {
                Product itemFromDB = getItemById(item.getId())
                if (itemFromDB == null)
                    insert(item)
                else
                    updateQuantity(item.getId())
            }
        }
Run Code Online (Sandbox Code Playgroud)

结果将是:尝试在数据库中查找项目,如果发现更新属性,则不只是插入一个新项目。因此,您只需insertOrUpdate要从DAO 调用一种方法。

  • 为了进一步优化这一点,我们可以通过直接使用 ConflictStrategy 作为 Ignore 插入并检查其返回类型来节省在数据库上执行的事务数量。如果由于记录存在而插入失败,它将直接更新记录,否则插入成功。这样就取消了在数据库中额外查找记录的过程。 (6认同)
  • 使用这种方法时要小心,因为 ConflictStrategy.REPLACE 的工作方式类似于 DELETE+INSERT。这意味着其他表中与外键相关的所有实体都将被删除‍♂️ (3认同)

Daw*_*idJ 8

如果已经有具有特定字段的项目,您可以检查您的数据库,例如:

@Query("SELECT id FROM items WHERE id = :id LIMIT 1")
fun getItemId(id: String): String?

@Insert
fun insert(item: Item): Long

@Update(onConflict = OnConflictStrategy.REPLACE)
fun update(item: Item): Int
Run Code Online (Sandbox Code Playgroud)

Item 是您的对象,在您的代码中:

 fun insertOrUpdate(item: Item) {
            database.runInTransaction {
                val id = getItemDao().getItemId(item.id)

                if(id == null)
                    getItemDao().insert(item)
                else
                    getItemDao().update(item)
            }
        }
Run Code Online (Sandbox Code Playgroud)


Dan*_*anu 7

你可以使用@Upsert

def room_version = "2.5.0-beta01" // or later
implementation "androidx.room:room-runtime:$room_version"
implementation "androidx.room:room-ktx:$room_version"


@Upsert(entity = Show::class)
fun updateOrInsert(shows: List<ShowUpdate>)

@Entity
data class Show(
@PrimaryKey var id: Int = 0,
val title: String? = null,
val imageUrl: String? = null,
val timestamp: Long = System.currentTimeMillis(),
val isFavorite: Boolean? = false
)
Run Code Online (Sandbox Code Playgroud)

这仅更新 Show 的某些字段。不更新字段isFavorite

@Entity
data class ShowUpdate(
@PrimaryKey var id: Int = 0,
val title: String? = null,
val imageUrl: String? = null,
val timestamp: Long = System.currentTimeMillis()
)
Run Code Online (Sandbox Code Playgroud)