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

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

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