комнатное обновление (или вставка, если не существует), строки и количество возвращенных строк

Мне нужно обновить, а если нет, добавьте строку в базу данных ROOM.

Я делаю это: 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();
}

И в DAO:

@Insert
void insert(Product products);

Но у меня есть метод

@Update
void update(Product product);

И у меня есть несколько вопросов:

  1. оба метода недействительны. Как я могу вернуть сохраненный флаг Product или boolean или вставленный счет после вставки?

  2. если я попробую выполнить update и у меня нет строки, она будет вставлена?

  3. Как я могу обновить (если нет - вставить) строку и вернуть счетчик обновления или вставленные строки?

Ответ 1

  1. Метод, аннотированный с помощью @Insert может возвращать long. Это вновь созданный идентификатор для вставленной строки. Метод, аннотированный с @Update может возвращать int. Это число обновленных строк.

  2. update попытается обновить все ваши поля, используя значение первичного ключа в предложении where. Если ваша сущность еще не сохраняется в базе данных, запрос на update не сможет найти строку и ничего не обновит.

  3. Вы можете использовать @Insert(onConflict = OnConflictStrategy.REPLACE). Это попытается вставить объект, и если существует существующая строка с одинаковым значением идентификатора, она удалит его и заменит на объект, который вы пытаетесь вставить. Имейте в виду, что если вы используете автоматически сгенерированные идентификаторы, это означает, что в полученной строке будет другой идентификатор, чем исходный, который был заменен. Если вы хотите сохранить идентификатор, вы должны придумать собственный способ сделать это.

Ответ 2

Это было какое-то время, но на этот вопрос нет принятого ответа, поэтому я постараюсь это сделать.

Как @Danail Alexiev сказал @Insert может возвращать long. @Update может вернуть int.

Но с какой целью вы используете обновление? Если вы просто хотите, чтобы весь объект был заменен новым, просто укажите OnConflictStrategy

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

Результатом будет: элементы, которые не существуют, будут добавлены, элементы, которые уже существуют, будут заменены новыми.

В случае, если вам нужно обновить только один параметр (например, количество), вы можете сделать что-то вроде этого

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

Результат будет таким: Попробуйте поискать элемент в БД, если найден обновить свойство, если не просто вставить новый элемент. Поэтому вам нужно вызвать только один метод insertOrUpdate из вашего DAO.

Ответ 3

Вы можете проверить свою базу данных, если уже есть элемент с определенным полем, для exmaple:

@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

Элемент - это ваш объект и в вашем коде:

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

                if(id == null)
                    getItemDao().insert(item)
                else
                    getItemDao().update(item)
            }
        }

Ответ 4

Вы можете проверить ниже метод insertModel(), где вы можете получить метод onComplete() и onError():

    val db: AppDatabase = Room.databaseBuilder(mCtx, AppDatabase::class.java, "db_nam.sqlite").build()

    companion object {
        @SuppressLint("StaticFieldLeak")
        private var mInstance: DatabaseClient? = null

        @Synchronized
        fun getInstance(mCtx: Context): DatabaseClient {
            if (mInstance == null) {
                mInstance = DatabaseClient(mCtx)
            }
            return mInstance as DatabaseClient
        }
    }

    // SEE HERE FOR INSERTING DATA SUCCESSFULLY AND ERROR CODE  
    private fun insertModel(rss_Model: RSS_Model) {
        Completable.fromAction {

            db.database_dao().insertRSS(rss_Model)

        }.observeOn(AndroidSchedulers.mainThread())
                .subscribeOn(Schedulers.io()).subscribe(object : CompletableObserver {
                    override fun onSubscribe(d: Disposable) {}

                    override fun onComplete() {
                        // Log.e("onComplete","GET SUCCESS HERE")
                    }

                    override fun onError(e: Throwable) {
                        // Log.e("onError","onError")
                    }
                })
    }

Ответ 5

Вы должны использовать Rx Android Single для решения этой проблемы. Пример:

@Query("SELECT * FROM subjectFormTable WHERE study_subject_id ==:subjectFormId")
fun getSubjectForm(subjectFormId: Int): Single<SubjectFormEntity>

Мы используем

val compositeDisposable = CompositeDisposable()

А также

compositeDisposable.add(
            subjectFormDao.getSubjectForm(studySubjectId)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe({
                Log.e(TAG, "successful ")
            }, {
                Log.e(TAG, "error "+it.message)
                //Insert or Update data here
            })
    )