2014-05-01 8 views
7

Quando ho voglia di controllare e vedere se qualcosa esiste nella mia ContentProvider quello che faccio di solito è qualcosa di simile a questoUsa inserire o sostituire in ContentProvider

Cursor c = getContentResolver().query(table,projection,selection,selectionArgs,sort); 

if(c != null && c.moveToFirst()){ 

    //item exists so update 

}else{ 

    //item does not exist so insert 

} 

ma che significa devo sempre fare, eventualmente, un database non necessaria chiamare rallentare le cose soprattutto il maggior numero di controlli sul database che devo fare. Ci deve essere un modo migliore per gestire questo, quindi non devo sempre interrogare prima.

ho guardato a questa domanda

Android Contentprovider - update within an insert method

ma utilizzando insertWithOnConflict controlla solo l'id chiave primaria e nel mio caso che non funziona perché quello che sto controllando non è l'id, ma una stringa univoca da un database del server.

quindi c'è qualcosa che posso fare con il fornitore di contenuti quindi non devo sempre fare una query per verificare se l'elemento esiste già in esso?

+0

Sei consapevole del comando "UPSERT" ** ** SOSTITUIRE? https://www.sqlite.org/lang_replace.html - Inserisce un nuovo record se non esistente o ne aggiorna uno esistente. –

+0

@BobMalooga No Non sono a conoscenza di questo, ma è possibile utilizzarlo in un ContentProvider? tutto ciò che un fornitore di contenuti ha è inserire, aggiornare, sostituire o eliminare – tyczj

+0

@tyczi No ... scusa, non ero a conoscenza dei limiti di ContentProvider. Ho pensato che potevi usare rawQuery o execSQL per ottenere e impostare i dati, rispettivamente. Ora sembra che ContentProvider sfortunatamente non offra queste strutture. Quindi, ho imparato qualcosa su ContentProviders (mai usato ancora - e probabilmente salterò in futuro). E ora sai che SQLite offre gli "upserts". ;) –

risposta

19

È possibile avere un vincolo UNIQUE su colonne diverse da ID uno. Esempio:

CREATE TABLE TEST (_id INTEGER PRIMARY KEY AUTOINCREMENT, server_id INTEGER NOT NULL, name TEXT, UNIQUE(server_id))

Avere questa tabella, nel metodo di inserimento del vostro fornitore di contenuti si può fare qualcosa di simile:

@Override 
    public Uri insert(Uri uri, ContentValues contentValues) { 
     final SQLiteDatabase db = mDatabase.getWritableDatabase(); 
     final int match = mUriMathcer.match(uri); 
     switch (match) { 
      case TEST: 
       insertOrUpdateById(db, uri, "TEST", 
         contentValues, "server_id"); 
       getContext().getContentResolver().notifyChange(uri, null, false); 
       return Contract.Test.buildTestUri(contentValues.getAsString("server_id")); 
      default: 
       throw new UnsupportedOperationException("Unknown uri: " + uri); 
     } 
    } 

/** 
* In case of a conflict when inserting the values, another update query is sent. 
* 
* @param db  Database to insert to. 
* @param uri Content provider uri. 
* @param table Table to insert to. 
* @param values The values to insert to. 
* @param column Column to identify the object. 
* @throws android.database.SQLException 
*/ 
private void insertOrUpdateById(SQLiteDatabase db, Uri uri, String table, 
           ContentValues values, String column) throws SQLException { 
    try { 
     db.insertOrThrow(table, null, values); 
    } catch (SQLiteConstraintException e) { 
     int nrRows = update(uri, values, column + "=?", 
       new String[]{values.getAsString(column)}); 
     if (nrRows == 0) 
      throw e; 
    } 
} 

spero che aiuta. Saluti!

+0

Questa è un'idea geniale - esattamente quello che stavo cercando. Grazie, molto apprezzato. Ti ho upvoted. – DEzra

+0

Cosa devo fare se voglio restituire * Uri * mentre inserisco un record, Uri deve essere creato con 'ID inserito '. Hai preso 'server_id' ma voglio' _id' –

5

Edy Bolos risposta può essere semplicemente scritto come

CREATE TABLE TEST ( _id INTEGER PRIMARY KEY AUTOINCREMENT, server_id INTEGER NOT NULL, name TEXT UNIQUE ON CONFLICT REPLACE);

Aggiungi UNICO SU CONFLITTO SOSTITUIRE a creare query di tabella.

+5

Non è lo stesso, perché 'ON CONFLICT REPLACE' sostituirà l'intera riga, potenzialmente perdendo alcune colonne, mentre la mia soluzione aggiorna le colonne che hai specificato, senza perdere i dati in altre colonne. –

1

Edy Bolos risposta può ancora essere scritta come

private void insertOrUpdateById(SQLiteDatabase db, Uri uri, String table, 
           ContentValues values, String column) 
      throws Exception { 
    long id = db.insertWithOnConflict(table, column, values, 
             SQLiteDatabase.CONFLICT_REPLACE); 
    if(id < 0){ 
     throw new Exception(); 
    } 
} 
+3

Non è lo stesso, perché "ON CONFLICT REPLACE" sostituirà l'intera riga, potenzialmente perdendo alcune colonne, mentre la mia soluzione aggiorna le colonne specificate, senza perdere i dati in altre colonne. –