На этом уроке поговорим о транзакциях в БД, и о том, как с помощью транзакций и SQLiteStatment ускорить работу с базой данных SQLite в android.
Что такое транзакции в БД SqLite? В нескольких словах, это работа с данными по принципу — «все или ничего». Например, если при сохранении в базу большого объема данных произошел сбой, и операция не закончилась успешно, сохранение или изменение данных в БД будет отменено, и все останется как было.
Т.е. либо транзакция выполняется целиком и переводит базу данных из одного целостного состояния в другое целостное состояние, либо, если по каким-либо причинам, одно из действий транзакции невыполнимо, или произошло какое-либо нарушение работы системы, база данных возвращается в исходное состояние, которое было до начала транзакции (происходит откат транзакции).
По умолчанию каждая инструкция SQL выполняется в собственной транзакции — это типичное поведение для базы данных SQL и SQLite не является исключением.
Но вы можете определить собственные границы транзакции, которые будут включать больше, чем одна инструкция, и это повышает производительность, поскольку одна большая транзакция выполняется гораздо быстрее, чем множество маленьких.
SQLiteDatabase.beginTransaction(); try { //Вставляем данные SQLiteDatabase.setTransactionSuccessful(); } finally { SQLiteDatabase.endTransaction(); }
Семантика транзакций БД SqLite проста. Вы начинаете новую транзакцию путем вызова метода SQLiteDatabase.beginTransaction(). После того, как вы вставите все записи успешно, вызовите SQLiteDatabase. setTransactionSuccessful(), а затем завершаете транзакцию с SQLiteException.endTransaction(). Если что-то пойдет не так в одной из вставок, выпадет исключение SQLException, и будет выполнен откат всех предыдущих вставок, поскольку не произойдет вызов метода SQLiteDatabase.setTransactionSuccessful().
Использование транзакций не только делает работу с БД более безопасной, но и существенно ускоряет ее. Напишем небольшое приложение, которое поможет убедиться в этом.
Приложение будет создавать 1000 записей программно, выполнять вставку записей в таблицу БД, и отображать длительность операции на экране.
Создаем новый проект с использованием шаблона Empty Activity.
В макете главного экрана такая структура:
< LinearLayout xmlns: android ="http://schemas.android.com/apk/res/android" xmlns: tools ="http://schemas.android.com/tools" android :layout_width= "match_parent" android :layout_height= "match_parent" android :orientation= "vertical" tools :context= "info.fandroid.sqlitestatement.MainActivity" > <TextView android:layout_width= "wrap_content" android:layout_height= "wrap_content" android:text= "Time: " android:textSize= "24sp" android:id= "@+id/tvTime" /> <Button android:layout_width= "wrap_content" android:layout_height= "wrap_content" android:text= "Insert" android:id= "@+id/btnInsert" /> </LinearLayout >
Текстовое поле, в которое мы будем выводить время длительности втавки данных в БД.
И кнопка, по нажатию которой будет происходить вставка данных.
package ... import android.content.ContentValues; import android.database.sqlite.SQLiteDatabase; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.TextView; public class MainActivity extends AppCompatActivity implements View.OnClickListener{ private static final String DB_NAME = "MyDB" ; private static final String TABLE_NAME = "MyTable" ; private SQLiteDatabase database; TextView tvTime ; Button btnInsert ; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout. activity_main); initDB(); tvTime = (TextView) findViewById(R.id. tvTime ); btnInsert = (Button) findViewById(R.id. btnInsert ); btnInsert.setOnClickListener( this); } private void initDB(){ database = this.openOrCreateDatabase( DB_NAME , MODE_PRIVATE , null ); database.execSQL( "CREATE TABLE IF NOT EXISTS " + TABLE_NAME + "(FirstNumber INT, SecondNumber INT, Result INT);" ); database.delete( TABLE_NAME, null , null ); } @Override public void onClick(View view) { database.delete( TABLE_NAME, null , null ); long startTime = System. currentTimeMillis(); insertRecords(); long diff = System. currentTimeMillis() - startTime; tvTime.setText( "Time: " + Long. toString(diff) + " ms"); } private void insertRecords(){ for ( int i = 0; i < 1000 ; i++){ ContentValues cv = new ContentValues(); cv.put( "FirstNumber", i); cv.put( "SecondNumber", i); cv.put( "Result", i*i); database.insert( TABLE_NAME , null , cv); } } @Override protected void onDestroy() { database.close(); super.onDestroy(); } }
В файле MainActivity.java реализуем интерфейс слушателя нажатия кнопки.
Далее добавим константы имени БД и заголовка таблицы, переменную класса SQLiteDatabase.
Объявим кнопку и текстовое поле.
Найдем экранные компоненты по id, присвоим слушатель кнопке.
Теперь напишем метод initDB в котором будем создавать БД. Метод openOrCreateDatabase создает БД или открывает, если она уже создана.
Затем выполняем запрос на создание таблицы с темя столбцами, где два столбца будут хранить целые числа, а третий столбец — результат их произведения.
Также добавим метод очистки всех полей таблицы.
Будем вызывать метод initDB в onCreate.
Теперь заполним метод onClick — сначала вызываем метод очистки таблицы, затем создаем переменную startTime, которая будет хранить приблизительный момент нажатия кнопки.
Здесь вызываем метод добавления записей в БД. Все будет происходить в основном потоке, и процесс вставки записей будет занимать какое-то время.
После чего мы снова создаем переменную, в которую передаем разницу между текущим временем и моментом нажатия кнопки.
И затем выводим результат в текстовое поле, преобразовав время в строковый формат.
Теперь рассмотрим код метода вставки данных в БД. Ничего нового здесь по сравнению с прошлыми уроками нет.
В цикле создаем экземпляр класса ContentValues, добавляем данные в столбцы методом put — первое значение, второе значение, и их произведение. Метод insert записывает данные в таблицу.
Теперь запустим приложение в эмуляторе.
Нажимаем кнопку, наше приложение зависает на несколько секунд, а затем сообщает, что операция вставки занимает 5071 миллисекунду.
Добавим методы транзакции в метод insertRecords().
private void insertRecords(){ database.beginTransaction(); try { for ( int i = 0; i < 1000 ; i++){ ContentValues cv = new ContentValues(); cv.put( "FirstNumber", i); cv.put( "SecondNumber", i); cv.put( "Result", i*i); database.insert( TABLE_NAME , null , cv); } database.setTransactionSuccessful(); } finally { database.endTransaction(); } }
Метод beginTransaction() начинает транзакцию, метод setTransactionSuccessful() вызовется по ее окончании. Его и весь блок цикла обернем в конструкцию try> finally, для этого выделяем код и нажимаем клавиши Ctrl+Alt+T. Выбираем нужное в списке. Завершаем транзакцию методом endTransaction().
Запустим приложение и нажмем кнопку вставки.
C применением транзакций длительность операции вставки сократилась до 115 ms — это в 44 раза быстрее!
Но это еще не все — результат можно улучшить, если использовать класс SQLiteStatement.
Перепишем метод insertRecords:
private void insertRecords(){ String sql = "INSERT INTO " + TABLE_NAME + " VALUES(?,?,?);"; SQLiteStatement statement = database.compileStatement(sql); database .beginTransaction(); try { for ( int i = 0; i < 1000 ; i++){ statement.clearBindings(); statement.bindLong( 1, i); statement.bindLong( 2, i); statement.bindLong( 3, i*i); statement.execute(); } database.setTransactionSuccessful(); } finally { database.endTransaction(); } }
Создаем строковую переменную, которая будет хранить sql инструкцию. Далее создаем экземпляр SQLiteStatement, которому передаем скомпилированный SQL запрос.
Меняем код в теле цикла на такой: сначала метод clearBindings() очищает все текущие привязки, методы bindLong привязывают значения данных к столбцам, а метод execute выполняет вставку данных.
Запускаем приложение, жмем кнопку вставки.
Результат — 42 ms, то есть мы ускорили операцию вставки данных еще почти в 3 раза.
Как видите, использование класса SQLiteStatment в сочетании с транзакциями позволяет существенно ускорить вставку данных в БД.
<<Предыдущий урок Следующий урок>>