Вам бонус- начислено 1 монета за дневную активность. Сейчас у вас 1 монета

38. Транзакции в SQLite. Небольшой FAQ по SQLite.

Лекция



Привет, Вы узнаете о том , что такое транзакции в sqlite небольшой faq по sqlite , Разберем основные их виды и особенности использования. Еще будет много подробных примеров и описаний. Для того чтобы лучше понимать что такое транзакции в sqlite небольшой faq по sqlite , настоятельно рекомендую прочитать все из категории Программирование мобильных устройств Android и IOs.

В этом уроке:

- используем транзакции при работе с БД

 

 

Что такое БД-транзакция, думаю объяснять особо не надо. Она используется при работе с данными по принципу «все или ничего». Т.е., например, вам нужно вставить пачку данных. Но вставить надо так, чтобы или все вставилось или ничего не вставилось.  И если в процессе половина записей прошла, а другая нет – должна быть возможность откатить изменения.

Напишем простое приложение и исследуем возможности SQLite в этом плане.

Создадим проект:

Project name: P0381_SQLiteTransaction
Build Target: Android 2.3.3
Application name: SQLiteTransaction
Package name: ru.startandroid.develop.p0381sqlitetransaction
Create Activity: MainActivity

 

Открываем MainActivity.java и пишем:

 

package ru.startandroid.develop.p0381sqlitetransaction;

import android.app.Activity;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Bundle;
import android.util.Log;

public class MainActivity extends Activity {

  final String LOG_TAG = "myLogs";

  DBHelper dbh;
  SQLiteDatabase db;

  /** Called when the activity is first created. */
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    Log.d(LOG_TAG, "--- onCreate Activity ---");
    dbh = new DBHelper(this);
    myActions();
  }

  void myActions() {
    db = dbh.getWritableDatabase();
    delete(db, "mytable");
    insert(db, "mytable", "val1");
    read(db, "mytable");
    dbh.close();
  }

  void insert(SQLiteDatabase db, String table, String value) {
    Log.d(LOG_TAG, "Insert in table " + table + " value = " + value);
    ContentValues cv = new ContentValues();
    cv.put("val", value);
    db.insert(table, null, cv);
  }

  void read(SQLiteDatabase db, String table) {
    Log.d(LOG_TAG, "Read table " + table);
    Cursor c = db.query(table, null, null, null, null, null, null);
    if (c != null) {
      Log.d(LOG_TAG, "Records count = " + c.getCount());
      if (c.moveToFirst()) {
        do {
          Log.d(LOG_TAG, c.getString(c.getColumnIndex("val")));
        } while (c.moveToNext());
      }
      c.close();
    }
  }

  void delete(SQLiteDatabase db, String table) {
    Log.d(LOG_TAG, "Delete all from table " + table);
    db.delete(table, null, null);
  }

  // класс для работы с БД
  class DBHelper extends SQLiteOpenHelper {

    public DBHelper(Context context) {
      super(context, "myDB", null, 1);
    }

    public void onCreate(SQLiteDatabase db) {
      Log.d(LOG_TAG, "--- onCreate database ---");

      db.execSQL("create table mytable ("
          + "id integer primary key autoincrement,"
          + "val text"
          + ");");
    }

    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
  }

}

 

Разбираем код. Я создал несколько методов, где сгруппировал операции: insert – набор операций для вставки записи, read – чтение всех записей, delete – удаление всех записей. Класс DBHelper – для управления БД. Интересовать нас будет метод myActions. Сейчас в нем мы подключаемся к БД, очищаем таблицу mytable, вставляем строку с значением val1, выводим в лог все записи из таблицы и отключаемся.

Все сохраним, запустим приложение. Смотрим лог:

--- onCreate Activity ---
--- onCreate database ---
Delete all from table mytable
Insert in table mytable value = val1
Read table mytable
Records count = 1
val1

Все верно, запись вставилась и отобразилась.

Теперь попробуем использовать транзакцию. Поправим код myActions на этот:

 

  void myActions() {
    db = dbh.getWritableDatabase();
    delete(db, "mytable");
    db.beginTransaction();
    insert(db, "mytable", "val1");
    db.endTransaction();
    insert(db, "mytable", "val2");
    read(db, "mytable");
    dbh.close();
  }

 

Мы подключаемся к базе, чистим таблицу, открываем транзакцию методом beginTransaction, вставляем val1, закрываем транзакцию методом endTransaction, вставляем val2, выводим содержимое в лог и отключаемся. Об этом говорит сайт https://intellect.icu . Все сохраняем, запускаем и смотрим лог:

--- onCreate Activity ---
Delete all from table mytable
Insert in table mytable value = val1
Insert in table mytable value = val2
Read table mytable
Records count = 1
val2

По логу видно, что вставляли мы две записи, но прошла только вторая. Та, которая была в транзакции – не записалась. Это произошло потому, что мы явно не указали, что транзакция должна быть успешно закрыта. Если этого не сделать, то при закрытии транзакции все операции отменятся. Давайте исправимся. Снова перепишем myActions:

  void myActions() {
    db = dbh.getWritableDatabase();
    delete(db, "mytable");
    db.beginTransaction();
    insert(db, "mytable", "val1");
    db.setTransactionSuccessful();
    insert(db, "mytable", "val2");
    db.endTransaction();
    insert(db, "mytable", "val3");
    read(db, "mytable");
    dbh.close();
  }

Подключаемся к БД, чистим таблицу, открываем транзакцию, вставляем val1, подтверждаем успешность транзакции методом setTransactionSuccessful, вставляемval2, закрываем транзакцию, вставляем val3, выводим содержимое и отключаемся.

Сохраняем, запускаем, смотрим лог:

--- onCreate Activity ---
Delete all from table mytable
Insert in table mytable value = val1
Insert in table mytable value = val2
Insert in table mytable value = val3
Read table mytable
Records count = 3
val1
val2
val3

Вставились все три записи. Обратите внимание - несмотря на то, что val2 мы вставляли уже после подтверждения успешности транзакции, запись вставилась, вошла в эту транзакцию. Но проводить операции после подтверждения транзакции не рекомендуется хелпом. 

 

Транзакция при открытии ставит блокировку на базу. Убедимся в этом, попробуем создать новое подключение к БД во время транзакции. ПерепишемmyActions:

  void myActions() {
    try {
      db = dbh.getWritableDatabase();
      delete(db, "mytable");

      db.beginTransaction();
      insert(db, "mytable", "val1");

      Log.d(LOG_TAG, "create DBHelper");
      DBHelper dbh2 = new DBHelper(this);
      Log.d(LOG_TAG, "get db");
      SQLiteDatabase db2 = dbh2.getWritableDatabase();
      read(db2, "mytable");
      dbh2.close();

      db.setTransactionSuccessful();
      db.endTransaction();

      read(db, "mytable");
      dbh.close();

    } catch (Exception ex) {
      Log.d(LOG_TAG, ex.getClass() + " error: " + ex.getMessage());
    }
  }

 

Подключаемся к базе, чистим таблицу, открываем транзакцию, вставляем запись, создаем новое подключение к БД - db2, читаем содержимое вторым подключением, закрываем второе подключение, успешно закрываем транзакцию, читаем содержимое первым подключением, закрываем первое подключение.

Все сохраним и запустим. Смотрим лог:

--- onCreate Activity ---
Delete all from table mytable
Insert in table mytable value = val1
create DBHelper
get db
class android.database.sqlite.SQLiteException error: database is locked

Мы видим, что при попытке создать второе подключение к базе произошла ошибка SQLiteException – база заблокирована открытой транзакцией. Если вы теперь закоментите или удалите строки управления транзакцией и снова выполните код, то все пройдет успешно, т.к. никаких блокировок не будет.

 

Наверняка есть некоторые вопросы по этой теме. Попробую здесь же ответить на некоторые.

close

Метод close есть и у SQLiteDatabase и у SQLiteOpenHelper. Какая между ними разница? Каким из них пользоваться для закрытия подключения?

Тут надо понимать один момент – объект SQLiteOpenHelper всегда предоставляет только одно подключение. Попробую объяснить этот механизм. У объектаSQLiteOpenHelper есть внутренний атрибут mDatabase типа SQLiteDatabase. Когда мы вызываем метод getWritableDatabase, объект SQLiteOpenHelperпроверяет: если mDatabase не null и не закрыт, то он и идет в качестве return. Иначе  SQLiteOpenHelper выполняет подключение к БД, записывает новыйSQLiteDatabase-объект в mDatabase и возвращает нам его. Т.е. метод getWritableDatabase либо возвращает существующее подключение к БД, либо создает новое в случае отсутствия подключения. Когда же выполняется метод close для SQLiteOpenHelper, то происходит вызов close для mDatabase и выполняется кодmDatabase = null.

Рассмотрим на примере. Снова меняем метод myActions:

 

  void myActions() {
    db = dbh.getWritableDatabase();
    SQLiteDatabase db2 = dbh.getWritableDatabase();
    Log.d(LOG_TAG, "db = db2 - " + db.equals(db2));
    Log.d(LOG_TAG, "db open - " + db.isOpen() + ", db2 open - " + db2.isOpen());
    db2.close();
    Log.d(LOG_TAG, "db open - " + db.isOpen() + ", db2 open - " + db2.isOpen());
  }

 

Сначала мы получаем db. При этом dbh проверяет свой внутренний атрибут mDatabase. Т.к. это первая попытка подключения, то mDatabase пуст, поэтому внутри dbh производится подключение и в mDatabase записывается свежесозданный SQLiteDatabase, и он же и возвращается в db из методаgetWritableDatabse .

Затем мы из того же dbh получаем db2dbh снова проверяет свой внутренний mDatabse, видит, что он уже не null и не закрыт, и возвращает нам его в нашdb2. В итоге db и db2 равны и ссылаются на один и тот же объект. Проверяем это с помощью метода equals. Далее проверим, что db и db2 открыты. Потомзакроем только db2, и еще раз проверим на открытость оба объекта.

Сохраняем, запускаем, смотрим лог:

--- onCreate Activity ---
db = db2 – true
db open - true, db2 open – true
db open - false, db2 open - false

Видим, что equals вернул true. Затем видно, что db и db2 открыты. А после закрытия db2 видим, что закрыты оба объекта. Все оттого, что «оба объекта» – это всего лишь две ссылки на один объект.

 

Если в коде вместо db2.close() поставить dbh.close() - эффект будет тот же. dbh вызовет метод close для mDatabase и обнулит его - mDatabase = null. А db иdb2 будут ссылаться на закрытый SQLiteDatabase.

Я думаю, что правильнее вызывать close для SQLiteOpenHelper, а не для SQLiteDatabase. Т.к. гарантировано закрывается текущее открытое соединение иобнуляется внутренняя ссылка на объект.

Если вам надо получить второе открытое подключение к БД, то надо создавать новый экземпляр DBHelper и вызывать getWritableDatabase. Мы так делали чуть выше в примере с блокировкой транзакции.

 

read write

В чем разница между getWritableDatabase и getReadableDatabase? Судя по хелпу, в обычной ситуации оба метода возвращают одно и то же. И оба позволятчитать и менять БД. В случае же, например, проблемы отсутствия свободного места на устройстве, метод getReadableDatabase вернет БД только для чтения, а getWritableDatabase выдаст ошибку.

 

_id, как имя поля-идентификатора

В различных источниках при работе с БД в качестве наименования поля-идентификатора в таблице используют не просто id, а _id. Почему?

Ответ нашелся в доках по Cursor-адаптерам. Цитата: "The Cursor must include a column named "_id" or this class will not work.". Т.е. если вы планируете использовать Cursor-адаптеры, то необходимо, чтобы таблица содержала поле _id, иначе адаптер не будет работать. 

 

Блокировка

Метод открытия транзакции beginTransaction ставит блокировку в режиме EXCLUSIVE. Т.е. БД блокируется и на чтение и на запись для других подключений. В SDK Android версии старше 2.3.3 появился метод beginTransactionNonExclusive, который ставит блокировку в режиме IMMEDIATE. Я подозреваю, что это позволит читать данные другим подключениям.

Если есть желание подробнее погрузиться в тему, вам сюда.

 

Синтаксис

И кстати, рекомендуемая форма для использования транзакций такая:

    db.beginTransaction();
    try {
      ...
      db.setTransactionSuccessful();
    } finally {
      db.endTransaction();
    }

Это очень важно! Т.е. если вы открыли транзакцию, выполнили какие-либо действия и не закрыли транзакцию, то все операции будут считатьсянеуспешными и изменения не будут внесены в БД. Поэтому закрытие транзакции необходимо выполнять и finally нам это гарантирует.

 

На следующем уроке:

- меняем версию и обновляем структуру БД в onUpgrade

В заключение, эта статья об транзакции в sqlite небольшой faq по sqlite подчеркивает важность того что вы тут, расширяете ваше сознание, знания, навыки и умения. Надеюсь, что теперь ты понял что такое транзакции в sqlite небольшой faq по sqlite и для чего все это нужно, а если не понял, или есть замечания, то не стесняйся, пиши или спрашивай в комментариях, с удовольствием отвечу. Для того чтобы глубже понять настоятельно рекомендую изучить всю информацию из категории Программирование мобильных устройств Android и IOs

Ответы на вопросы для самопроверки пишите в комментариях, мы проверим, или же задавайте свой вопрос по данной теме.

создано: 2016-02-08
обновлено: 2024-11-12
282



Рейтиг 9 of 10. count vote: 2
Вы довольны ?:


Поделиться:

Найди готовое или заработай

С нашими удобными сервисами без комиссии*

Как это работает? | Узнать цену?

Найти исполнителя
$0 / весь год.
  • У вас есть задание, но нет времени его делать
  • Вы хотите найти профессионала для выплнения задания
  • Возможно примерение функции гаранта на сделку
  • Приорететная поддержка
  • идеально подходит для студентов, у которых нет времени для решения заданий
Готовое решение
$0 / весь год.
  • Вы можите продать(исполнителем) или купить(заказчиком) готовое решение
  • Вам предоставят готовое решение
  • Будет предоставлено в минимальные сроки т.к. задание уже готовое
  • Вы получите базовую гарантию 8 дней
  • Вы можете заработать на материалах
  • подходит как для студентов так и для преподавателей
Я исполнитель
$0 / весь год.
  • Вы профессионал своего дела
  • У вас есть опыт и желание зарабатывать
  • Вы хотите помочь в решении задач или написании работ
  • Возможно примерение функции гаранта на сделку
  • подходит для опытных студентов так и для преподавателей

Комментарии


Оставить комментарий
Если у вас есть какое-либо предложение, идея, благодарность или комментарий, не стесняйтесь писать. Мы очень ценим отзывы и рады услышать ваше мнение.
To reply

Программирование мобильных устройств Android и IOs

Термины: Программирование мобильных устройств Android и IOs