На этом уроке поговорим о транзакциях в БД, и о том, как с помощью транзакций и 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 в сочетании с транзакциями позволяет существенно ускорить вставку данных в БД.
<<Предыдущий урок Следующий урок>>