Room and Conflict Resolution

For @Insert and @Update methods in your @Dao, you can have onConflict properties in the annotations that stipulate what should happen if the insert or update results in a violation of a few types of constraints:

Room gives you five OnConflictStrategy enum values to choose from for your onConflict property. Each of those OnConflictStrategy values maps to an equivalent SQLite keyword, and each of those strategies results is different behavior in SQLite.

Value Meaning
OnConflictStrategy.ABORT Cancels this statement but preserves prior results in the transaction and keeps the transaction alive
OnConflictStrategy.FAIL Like ABORT, but accepts prior changes by this specific statement (e.g., if we fail on the 50th row to be updated, keep the changes to the preceding 49)
OnConflictStrategy.IGNORE Like FAIL, but continues processing this statement (e.g., if we fail on the 50th row out of 100, keep the changes to the other 99)
OnConflictStrategy.REPLACE For uniqueness violations, deletes other rows that would cause the violation before executing this statement
OnConflictStrategy.ROLLBACK Rolls back the current transaction

However, they may not wind up with different behavior in Room, due to the way that Room works with SQLite. As a result, two of these five (FAIL and ROLLBACK) have been deprecated.

In this chapter, we will examine those five options and see what SQLite does and what the resulting effects are in a Room-based app. As you will see, while there are five official options, fewer are practical.

Abort

@Insert(onConflict = OnConflictStrategy.ABORT)
@Update(onConflict = OnConflictStrategy.ABORT)

What SQLite Does

This strategy maps to INSERT OR ABORT or UPDATE OR ABORT statements. If a constraint violation would occur from this statement, the statement is skipped. SQLiteDatabase throws a SQLiteConstraintException. However, if you have started a transaction, that transaction remains open, so further statements in the transaction can be executed.

Effects in Room

An individual @Insert or @Update method that uses OnConflictStrategy.ABORT will throw a SQLiteConstraintException if there is a constraint violation. In isolation, this fits with what you might expect.

The problem comes with @Transaction.

Every method that Room generates in response to your @Dao-annotated methods has the same basic structure:

  @Override
  public void whatever(SomeEntity... entities) {
    __db.beginTransaction();
    try {
      // the real work for whatever whatever() does
      __db.setTransactionSuccessful();
    } finally {
      __db.endTransaction();
    }
  }

This includes @Transaction-annotated methods, which just wrap that template around a call to your real method:

  @Override
  public void whatever(SomeEntity... entities) {
    __db.beginTransaction();
    try {
      super.whatever(entities);
      __db.setTransactionSuccessful();
    } finally {
      __db.endTransaction();
    }
  }

If anything in your @Transaction method throws an exception, of any kind, the entire transaction gets rolled back, courtesy of the try/finally structure.

So, even though ABORT is supposed to keep the transaction open, Room rolls back the transaction, so that your @Transaction is atomic.


Prev Table of Contents Next

This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.