Pull to refresh

Осторожнее с SQLiteDatabase.insert-ами

Reading time 2 min
Views 8.1K
Ковыряясь с базами данных в андроиде наткнулся на весьма неприятную вещь: методы SQLiteDatabase для вставки данных работают не совсем так, как написано в документации. Задача была простая: вставить запись и получить ее ключ для использования в другой таблице. В случае, если нужная запись уже имеется, хочется узнать ключ этой старой записи. Оказалось, что получить можно что угодно, кроме этого самого старого ключа.

Чтобы разобраться в этом пришлось поставить несколько опытов на кошках, зато теперь кое-что прояснилось.

Сначала посмотрим, что же нам обещает андроид в SQLiteDatabase:

insert() — самый простой метод. Должен вернуть только что вставленный ключ или -1, если что-то пошло не так.

insertOrThrow() — описание точно соответствует предыдущему, но добавлено, что метод кидает SQLException. Судя по названию, можно ожидать, что SQLException кинется вместо того, чтобы возвращать -1, но пока уверенности нет.

insertWithOnConflict() — вот это самое интересное. Нам обещают, что метод вернет ключ свежей записи ИЛИ ключ существующей записи, если для конфликтов использовался алгоритм IGNORE, ИЛИ -1. Запомним также, что про SQLException тут ничего не говорится.

Теперь посмотрим, что же происходит на практике. Создадим простейшую таблицу и попробуем задавать для нее разные алгоритмы разрешения конфликтов (в том числе и никакой). Я не стал проверять rollback и abort — их поведение в данном случае, судя по всему, будет аналогично fail, а меня больше всего интересовал ignore.

Вставим в таблицу несколько уникальных значений, а потом попробуем добавить конфликтующее. Посмотрим, как все эти методы обрабатывают конфликты.

В случае, если алгоритм разрешения конфликтов не задан, простой insert() возвращает -1 (как и обещалось), а insertOrThrow() и insertWithOnConflict() оба кидают SQLiteConstraintException. И если для insertOrThrow() это правильно, то от insertWithOnConflict() можно было ожидать -1. То же самое происходит и при алгоритме 'fail'.

Алгоритм 'replace' все методы отработали нормально — удалили старую запись, вставили новую и вернули ее ключ.

А вот с таким нужным 'ignore' получился полный облом. Ни один метод не выдал ошибку (-1 или SQLiteConstraintException). Все дружно вернули какое-то число — предположительно ключ последней вставленной в таблицу записи. Разумеется, это совсем не то, что нам обещали.

Вобщем, документации верить нельзя. Любой метод может как вернуть самые неожиданные значения, так и бросить исключение. А вот желаемого результата достичь так и не удалось — ни один метод не смог при конфликте вернуть ключ существующей записи.
Tags:
Hubs:
+12
Comments 8
Comments Comments 8

Articles