Изоляция транзакций. Неповторяющееся чтение
Last updated: 9 мая 2025 г.Допустим есть транзакция А
и транзакция В
в разных потоках.
Допустим в транзакции А
есть несколько select
запросов подряд.
Если транзакция В
во время выполнения транзакции А
изменит данные считываемые select-ами транзакции А
, то это опять таки бывает не желательным.
Иногда нужно, чтобы сначала данные считались, то есть второй поток был заблокирован пока выполняется первый с транзакцией А
, и данные обновились только после считывания всеми select-ами.
Для того чтобы изолировать транзакции от неповторяющегося чтения, нужно в обеих вызвать TRANSACTION_REPEATABLE_READ
.
Пример программы:
1import java.sql.*;
2
3public class IsolationsRep {
4 public static void main(String[] args)
5 throws ClassNotFoundException,
6 SQLException, InterruptedException {
7 //здесь идет транзакция А
8 Class.forName("com.mysql.cj.jdbc.Driver");
9 Connection connection =
10 DriverManager.getConnection(
11 "jdbc:mysql://localhost/storage",
12 "root", "0799MSD");
13 Statement statement = connection.createStatement();
14 connection.setAutoCommit(false);
15 //изолируем транзакцию от неповторяющегося чтения
16 connection.setTransactionIsolation(
17 Connection.TRANSACTION_REPEATABLE_READ);
18 //Выпим два селекта запроса ниже
19 //между которыми перерыв 2 секунды.
20 ResultSet resultSet =
21 statement.executeQuery("SELECT * FROM books");
22 while (resultSet.next()) {
23 System.out.println("name: "
24 + resultSet.getString("name"));
25 }
26 //Запускаем поток транзакции В
27 //В нем транзакция В должна менять данные
28 //второй книги пока транзакция А остановлена.
29 new TransactionB().start();
30 //на две секунды останавливаем
31 //поток транзакции А, то есть текущий.
32 Thread.sleep(2000);
33 //Поток транзакции В в данный момент
34 //заблокирован поскольку транзакция А еще
35 //не выполнилась полностью.
36 //И когда нижний селект выполнится поток
37 //транзакции В разблокируется и только
38 //тогда данные в БД изменятся им.
39 //То есть очередная транзакция А и В
40 //не пересекаются и не мешают друг другу.
41 ResultSet resultSet1 =
42 statement.executeQuery("SELECT * FROM books");
43 while (resultSet1.next()) {
44 System.out.println("name: "
45 + resultSet1.getString("name"));
46 }
47 }
48
49 static class TransactionB extends Thread {
50 @Override
51 public void run() {
52 //здесь идет транзакция В
53 try {
54 Connection connection =
55 DriverManager.getConnection(
56 "jdbc:mysql://localhost/storage",
57 "root", "07998MSD");
58 Statement statement =
59 connection.createStatement();
60 connection.setAutoCommit(false);
61 connection.setTransactionIsolation(
62 Connection.TRANSACTION_REPEATABLE_READ);
63 //Пока идут 2 секунды в течении которых
64 //поток транзакции А остановлен
65 //транзакция В должна обновить данные
66 //второй книги, но она этого не сделает
67 //поскольку установлен режим
68 //TRANSACTION_REPEATABLE_READ, который
69 //блокирует поток транзакции В пока
70 //полностью не выполнится транзакция А.
71 statement.executeUpdate("update books set"
72 +" name = ‘another name’ where id = 2″);
73 connection.commit();
74 }
75 catch (SQLException e){}
76 }
77 }
78}
Скомпилируем, запустим программу:

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

Как видим, оба селекта считали из БД одни и те же данные. То есть пока транзакция А не завершилась транзакция В не влияла на БД и соответственно на то, что считывала транзакция А.
Следующие уроки
Изоляция транзакций. Фантомное чтение
14
мин.
Безопасные запросы с PreparedStatement
12
мин.
Использование хранимых процедур в Java
14
мин.