web-dev-qa-db-fra.com

Android Insertion ou mise à jour de SQLite

comme on peut le voir dans la documentation, la syntaxe à faire pour insérer ou mettre à jour est la suivante: INSERT OR REPLACE INTO <table> (<columns>) VALUES (<values>), ma question est-il une fonction qui fusionne le suivant?

public long insert (String table, String nullColumnHack, ContentValues values) 
public int update (String table, ContentValues values, String whereClause, String[] whereArgs)

ou doit-il être fait avec une instruction SQL préparée et rawQuery?

Quelles sont les meilleures pratiques pour effectuer une insertion ou une mise à jour sous Android?

58
Luis Neves

Je crois que vous demandez comment INSÉRER de nouvelles lignes ou METTRE À JOUR vos lignes existantes en une seule étape. Bien que cela soit possible dans un seul SQL brut, comme indiqué dans la section cette réponse , j’ai trouvé qu’il était plus facile de le faire en deux étapes dans Android utilisant SQLiteDatabase .insertWithOnConflict () en utilisant CONFLICT_IGNORE pour conflictAlgorithm.

ContentValues initialValues = new ContentValues();
initialValues.put("_id", 1); // the execution is different if _id is 2
initialValues.put("columnA", "valueNEW");

int id = (int) yourdb.insertWithOnConflict("your_table", null, initialValues, SQLiteDatabase.CONFLICT_IGNORE);
if (id == -1) {
    yourdb.update("your_table", initialValues, "_id=?", new String[] {"1"});  // number 1 is the _id here, update to variable for your code
}

Cet exemple suppose que la clé de table est définie pour la colonne "_id", que vous connaissez l'enregistrement _id et qu'il existe déjà la ligne n ° 1 (_id = 1, columnA = "valueA", columnB = "valueB"). Voici la différence utilisant insertWithOnConflict avec CONFLICT_REPLACE et CONFLICT_IGNORE

  • CONFLICT_REPLACE écrase les valeurs existantes des autres colonnes en null (c'est-à-dire que columnB deviendra NULL et le résultat sera _id = 1, columnA = "valueNEW", columnB = NULL). Vous perdez ainsi des données existantes et je ne les utilise pas dans mon code.
  • CONFLICT_IGNORE ignorera l'INSERTION SQL de votre ligne existante n ° 1 et SQL UPDATE de cette ligne à l'étape suivante en préservant le contenu de toutes les autres colonnes (le résultat sera _id = 1, columnA = "valueNEW", columnB = " valeurB ").

Lorsque vous essayez d'insérer la nouvelle ligne n ° 2 qui n'existe pas encore, le code n'exécutera l'insert SQL que dans la première instruction insertWithOnConflict (le résultat sera _id = 2, columnA = "valueNEW", columnB = NULL).

Faites attention à ce bogue qui est à l'origine du dysfonctionnement de SQLiteDatabase.CONFLICT_IGNORE sur API10 (et probablement API11). La requête renvoie 0 au lieu de -1 lorsque je teste sur Android 2.2.

Si vous ne connaissez pas la clé d'enregistrement _id ou si vous avez une condition qui ne créera pas de conflit, vous pouvez inverser la logique en UPDATE ou INSERT . Ceci gardera votre clé d'enregistrement _id lors de la mise à jour ou créera un nouvel enregistrement _id lors de l'insertion.

int u = yourdb.update("yourtable", values, "anotherID=?", new String[]{"x"});
if (u == 0) {
    yourdb.insertWithOnConflict("yourtable", null, values, SQLiteDatabase.CONFLICT_REPLACE);
}

L'exemple ci-dessus suppose que vous souhaitez simplement mettre à jour la valeur de l'horodatage dans l'enregistrement, par exemple. Si vous appelez d'abord insertWithOnConflict, INSERT créera un nouvel enregistrement _id en raison de la différence dans la condition d'horodatage.

84
theczechsensation

c'est votre méthode SQLiteDatabase.insertWithOnConflict () . comprendre ce à quoi il fait référence ce document sur sqlite

42
kdehairy

SQLiteDatabase.replace () le fait, il appelle essentiellement:

insertWithOnConflict(table, nullColumnHack, initialValues, CONFLICT_REPLACE);

Dommage que la documentation ne soit pas très claire.

9
aleb

Le nom de l'opération pour cela est "upsert" et sa solution consiste à identifier les colonnes de votre table qui rendent une ligne UNIQUE.

Exemple: _id, name, job, hours_worked

Les colonnes que nous utiliserons sont name et job.

private int getID(String name, String job){
    Cursor c = dbr.query(TABLE_NAME,new String[]{"_id"} "name =? AND job=?",new String[]{name,job},null,null,null,null);
    if (c.moveToFirst()) //if the row exist then return the id
        return c.getInt(c.getColumnIndex("_id"));
    return -1;
}

Dans votre classe de gestionnaire de base de données:

public void upsert(String name, String job){
    ContentValues values = new ContentValues();
    values.put("NAME",name);
    values.put("JOB",job);

    int id = getID(name,job);
    if(id==-1)
        db.insert(TABLE_NAME, null, values);
    else
        db.update(TABLE_NAME, values, "_id=?", new String[]{Integer.toString(id)});
}
5
z1lV3r

SQLiteDatabase.replace () est probablement ce que vous recherchez. Je ne l'ai pas essayé, mais le doc dit qu'il renvoie l'ID de ligne de la nouvelle ligne insérée , afin que cela puisse fonctionner.

3

J'avais le même problème, mais je me suis rendu compte que quand mon objet a déjà un identifiant, il devrait être mis à jour et quand il n'a pas d'identifiant, il devrait être inséré, c'est donc étape par étape ce que j'ai fait pour résoudre le problème:

1- dans votre objet getId utilisez Integer ou initialisez l'ID comme bon vous semble: voici mon code

public Integer getId() {
    return id;
}

2- Vérifiez l’id de votre méthode pour l’insertion ou la mise à jour après avoir tout mis dans ContentValues:

if(myObject.getId()!= null ){
        int count = db.update(TABLE_NAME,myContentValues,ID + " = ? ",
                new String[]{String.valueOf(myObject.getId())});
        if(count<=0){
            //inserting content values to db
            db.insert(TABLE_NAME, null, myContentValues);
        }
    } else {
        db.insert(TABLE_NAME, null, myContentValues);
    }

ce qui se passe ici, c’est que je vérifie la présence d’un identifiant si j’ai mis à jour cette ligne, mais si la méthode update renvoie -1, cela signifie qu’il n’y avait pas de rangée portant cet identifiant; j’insère donc cette rangée.

j'espère que cela t'aides.

2
pouya

Qu'en est-il de replaceOrThrow (Table de chaînes, String nullColumnHack, ContentValues ​​initialValues)

La documentation dit ... Méthode pratique pour remplacer une ligne dans la base de données. Insère une nouvelle ligne si une ligne n'existe pas déjà.

Fondamentalement, il appelle insertWithOnConflict

0
Alezis