Изоляция транзакций в Java. Неповторяющееся чтение

Допустим есть транзакция А и транзакция В в разных потоках.

Допустим в транзакции А есть несколько select запросов подряд.

Если транзакция В во время выполнения транзакции А изменит данные считываемые select-ами транзакции А, то это опять таки бывает не желательным.

Иногда нужно, чтобы сначала данные считались, то есть второй поток был заблокирован пока выполняется первый с транзакцией А, и данные обновились только после считывания всеми select-ами.

Для того чтобы изолировать транзакции от неповторяющегося чтения, нужно в обеих вызвать TRANSACTION_REPEATABLE_READ.

Пример программы:

import java.sql.*; public class IsolationsRep { public static void main(String[] args) throws ClassNotFoundException, SQLException, InterruptedException { //здесь идет транзакция А Class.forName(“com.mysql.cj.jdbc.Driver”); Connection connection = DriverManager.getConnection( “jdbc:mysql://localhost/storage”, “root”, “0799MSD”); Statement statement = connection.createStatement(); connection.setAutoCommit(false); //изолируем транзакцию от неповторяющегося чтения connection.setTransactionIsolation( Connection.TRANSACTION_REPEATABLE_READ); //Выпим два селекта запроса ниже //между которыми перерыв 2 секунды. ResultSet resultSet = statement.executeQuery(“SELECT * FROM books”); while (resultSet.next()) { System.out.println(“name: ” + resultSet.getString(“name”)); } //Запускаем поток транзакции В //В нем транзакция В должна менять данные //второй книги пока транзакция А остановлена. new TransactionB().start(); //на две секунды останавливаем //поток транзакции А, то есть текущий. Thread.sleep(2000); //Поток транзакции В в данный момент //заблокирован поскольку транзакция А еще //не выполнилась полностью. //И когда нижний селект выполнится поток //транзакции В разблокируется и только //тогда данные в БД изменятся им. //То есть очередная транзакция А и В //не пересекаются и не мешают друг другу. ResultSet resultSet1 = statement.executeQuery(“SELECT * FROM books”); while (resultSet1.next()) { System.out.println(“name: ” + resultSet1.getString(“name”)); } } static class TransactionB extends Thread { @Override public void run() { //здесь идет транзакция В try { Connection connection = DriverManager.getConnection( “jdbc:mysql://localhost/storage”, “root”, “07998MSD”); Statement statement = connection.createStatement(); connection.setAutoCommit(false); connection.setTransactionIsolation( Connection.TRANSACTION_REPEATABLE_READ); //Пока идут 2 секунды в течении которых //поток транзакции А остановлен //транзакция В должна обновить данные //второй книги, но она этого не сделает //поскольку установлен режим //TRANSACTION_REPEATABLE_READ, который //блокирует поток транзакции В пока //полностью не выполнится транзакция А. statement.executeUpdate(“update books set” +” name = ‘another name’ where id = 2″); connection.commit(); } catch (SQLException e){} } } }

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

Проверим таблицу books после завершения работы программы через MySQL консоль:

Как видим, оба селекта считали из БД одни и те же данные. То есть пока транзакция А не завершилась транзакция В не влияла на БД и соответственно на то, что считывала транзакция А.

Изоляция транзакций. Фантомное чтение

Разберитесь с фантомным чтением в транзакциях JDBC. Как TRANSACTION_SERIALIZABLE предотвращает вставку данных во время выполнения запросов.
Time to read: 14

Безопасные запросы с PreparedStatement

Узнайте, как PreparedStatement в JDBC защищает от SQL-инъекций. Примеры уязвимого кода и безопасной реализации для работы с базой данных в Java.
Time to read: 12

Использование хранимых процедур в Java

Как работать с хранимыми процедурами SQL в Java через JDBC: создание процедур, вызов через CallableStatement и обработка входных/выходных параметров.
Time to read: 14