Android/SQLite :Вставить -Обновить столбцы таблицы, чтобы сохранить идентификатор

В настоящее время я использую следующую инструкцию для создания таблицы в базе данных SQLite на устройстве Android.

CREATE TABLE IF NOT EXISTS 'locations' (
  '_id' INTEGER PRIMARY KEY AUTOINCREMENT, 'name' TEXT, 
  'latitude' REAL, 'longitude' REAL, 
  UNIQUE ( 'latitude',  'longitude' ) 
ON CONFLICT REPLACE );

Предложение конфликта -в конце приводит к тому, что строки удаляются при выполнении новых вставок с теми же координатами. Документация SQLite содержит дополнительную информацию о предложении конфликта -.

Вместо этого я хотел бы сохранить прежние строки и просто обновить их столбцы. Каков наиболее эффективный способ сделать это в среде Android/SQLite?

  • Как конфликт -в операторе CREATE TABLE.
  • Как триггер INSERT.
  • Как условное предложение в методе ContentProvider#insert.
  • ... лучшее, что вы можете придумать

Я бы подумал, что более эффективно обрабатывать такие конфликты в базе данных. Кроме того, мне трудно переписать метод ContentProvider#insertдля рассмотрения сценария вставки -обновления .Вот код метода insert:

public Uri insert(Uri uri, ContentValues values) {
    final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
    long id = db.insert(DatabaseProperties.TABLE_NAME, null, values);
    return ContentUris.withAppendedId(uri, id);
}

Когда данные поступают из серверной части, все, что я делаю, это вставляю данные следующим образом.

getContentResolver.insert(CustomContract.Locations.CONTENT_URI, contentValues);

У меня проблемы с выяснением того, как применить здесь альтернативный вызов ContentProvider#update. Кроме того, это не мое любимое решение в любом случае.


Изменить:

@CommonsWare :Я попытался реализовать ваше предложение по использованию INSERT OR REPLACE. Я придумал этот уродливый кусок кода.

private static long insertOrReplace(SQLiteDatabase db, ContentValues values, String tableName) {
    final String COMMA_SPACE = ", ";
    StringBuilder columnsBuilder = new StringBuilder();
    StringBuilder placeholdersBuilder = new StringBuilder();
    List pureValues = new ArrayList(values.size());
    Iterator> iterator = values.valueSet().iterator();
    while (iterator.hasNext()) {
        Entry pair = iterator.next();
        String column = pair.getKey();
        columnsBuilder.append(column).append(COMMA_SPACE);
        placeholdersBuilder.append("?").append(COMMA_SPACE);
        Object value = pair.getValue();
        pureValues.add(value);
    }
    final String columns = columnsBuilder.substring(0, columnsBuilder.length() - COMMA_SPACE.length());
    final String placeholders = placeholderBuilder.substring(0, placeholdersBuilder.length() - COMMA_SPACE.length());
    db.execSQL("INSERT OR REPLACE INTO " + tableName + "(" + columns + ") VALUES (" + placeholders + ")", pureValues.toArray());

    // The last insert id retrieved here is not safe. Some other inserts can happen inbetween.
    Cursor cursor = db.rawQuery("SELECT * from SQLITE_SEQUENCE;", null);
    long lastId = INVALID_LAST_ID;
    if (cursor != null && cursor.getCount() > 0 && cursor.moveToFirst()) {
        lastId = cursor.getLong(cursor.getColumnIndex("seq"));
    }
    cursor.close();
    return lastId;
}

Однако, когда я проверяю базу данных SQLite, равные столбцы по-прежнему удаляются и вставляются с новыми идентификаторами . Я не понимаю, почему это происходит, и думал, что причина в моей конфликтной -статье. Но документация утверждает обратное.

The algorithm specified in the OR clause of an INSERT or UPDATE overrides any algorithm specified in a CREATE TABLE. If no algorithm is specified anywhere, the ABORT algorithm is used.

Другим недостатком этой попытки является то, что вы теряете значение идентификатора, возвращаемого оператором вставки. Чтобы компенсировать это, я наконец нашел возможность запросить last_insert_rowid. Это объясняется в сообщениях dtmilano и swiz . Однако я не уверен, что это безопасно, поскольку между ними может произойти еще одна вставка.

25
задан Community 23 May 2017 в 12:26
поделиться