tutorial - android studio room




Android-Raumpersistenzbibliothek: Upsert (5)

Ein anderer Ansatz, den ich mir vorstellen kann, besteht darin, die Entität per Abfrage über DAO abzurufen und dann alle gewünschten Aktualisierungen durchzuführen. Dies kann im Vergleich zu den anderen Lösungen in diesem Thread in Bezug auf die Laufzeit weniger effizient sein, da die vollständige Entität abgerufen werden muss, ermöglicht jedoch eine viel größere Flexibilität in Bezug auf die zulässigen Vorgänge, z.

Zum Beispiel :

private void upsert(EntityA entityA) {
   EntityA existingEntityA = getEntityA("query1","query2");
   if (existingEntityA == null) {
      insert(entityA);
   } else {
      entityA.setParam(existingEntityA.getParam());
      update(entityA);
   }
}

Die Persistenzbibliothek von Android Room enthält freundlicherweise die Anmerkungen @Insert und @Update, die für Objekte oder Sammlungen funktionieren. Ich habe jedoch einen Anwendungsfall (Push-Benachrichtigungen, die ein Modell enthalten), für den ein UPSERT erforderlich wäre, da die Daten möglicherweise in der Datenbank vorhanden sind oder nicht.

Sqlite hat von Haus aus keine Probleme, und in dieser SO-Frage werden Problemumgehungen beschrieben. Wie würde man die dortigen Lösungen auf Room anwenden?

Wie kann ich ein Insert oder Update in Room implementieren, das keine Fremdschlüsseleinschränkungen verletzt? Wenn Sie insert with onConflict = REPLACE verwenden, wird onDelete für jeden Fremdschlüssel dieser Zeile aufgerufen. In meinem Fall führt onDelete zu einer Kaskade, und das erneute Einfügen einer Zeile führt dazu, dass Zeilen in anderen Tabellen mit dem Fremdschlüssel gelöscht werden. Dies ist NICHT das beabsichtigte Verhalten.


Für eine elegantere Vorgehensweise würde ich zwei Optionen vorschlagen:

Überprüfung auf Rückgabewert von insert mit IGNORE als OnConflictStrategy (wenn er gleich -1 ist, bedeutet das, dass die Zeile nicht eingefügt wurde):

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

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

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

Behandlung einer Ausnahme von der insert mit FAIL als OnConflictStrategy :

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

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

public void upsert(Entity entity) {
    try {
        insert(entity);
    } catch (SQLiteConstraintException exception) {
        update(entity);
    }
}

Nur ein Update, wie das geht, wobei Kotlin die Daten des Modells beibehält (vielleicht, um es in einem Zähler wie im Beispiel zu verwenden):

//Your Dao must be an abstract class instead of an interface (optional database constructor variable)
@Dao
abstract class ModelDao(val database: AppDatabase) {

@Insert(onConflict = OnConflictStrategy.FAIL)
abstract fun insertModel(model: Model)

//Do a custom update retaining previous data of the model 
//(I use constants for tables and column names)
 @Query("UPDATE $MODEL_TABLE SET $COUNT=$COUNT+1 WHERE $ID = :modelId")
 abstract fun updateModel(modelId: Long)

//Declare your upsert function open
open fun upsert(model: Model) {
    try {
       insertModel(model)
    }catch (exception: SQLiteConstraintException) {
        updateModel(model.id)
    }
}
}

Sie können @Transaction und die Datenbankkonstruktorvariable auch für komplexere Transaktionen mit database.openHelper.writableDatabase.execSQL ("SQL STATEMENT") verwenden.


Sollte mit dieser Art von Aussage möglich sein:

INSERT INTO table_name (a, b) VALUES (1, 2) ON CONFLICT UPDATE SET a = 1, b = 2





android-architecture-components