Обережніше з SQLiteDatabase.insert-ами

Обережніше з SQLiteDatabase.insert-ами

Копирсаючись з базами даних в андроїді натрапив на вельми неприємну річ: методи 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). Всі дружно повернули якесь число - імовірно ключ останнього вставленого в таблицю запису. Зрозуміло, це зовсім не те, що нам обіцяли.

Загалом, документації вірити не можна. Будь-який метод може як повернути найбільш несподівані значення, так і кинути виняток. А ось бажаного результату досягти так і не вдалося - жоден метод не зміг при конфлікті повернути ключ існуючого запису.

COM_SPPAGEBUILDER_NO_ITEMS_FOUND