← Назад к вопросам

Может ли быть много транзакций в рамках сессии?

3.0 Senior🔥 61 комментариев
#Многопоточность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Множество транзакций в рамках одной сессии

Ответ: Да, абсолютно возможно и очень распространено в реальных приложениях. Одна сессия БД может содержать множество последовательных или логически разделённых транзакций.

Понимание сессии и транзакции

Сессия — это долгоживущее соединение с БД, которое существует на протяжении работы приложения или запроса.

Транзакция — это единица работы с БД (набор операций), которая либо полностью выполняется, либо полностью откатывается.

Отношение: одна сессия может содержать множество транзакций.

Жизненный цикл

Сессия начала
  ↓
Транзакция 1 начало → операции → COMMIT/ROLLBACK
  ↓
Транзакция 2 начало → операции → COMMIT/ROLLBACK
  ↓
Транзакция 3 начало → операции → COMMIT/ROLLBACK
  ↓
Сессия закрыта

Пример в Java с JDBC

public class MultipleTransactionsExample {
    public static void main(String[] args) throws SQLException {
        // Одна сессия (одно соединение)
        Connection session = DriverManager.getConnection(
            "jdbc:postgresql://localhost/mydb", "user", "password"
        );
        
        try {
            // Отключаем автокоммит
            session.setAutoCommit(false);
            
            // ТРАНЗАКЦИЯ 1: Перевод денег со счёта А на счёт Б
            transferMoney(session, "account_A", "account_B", 100);
            session.commit(); // Первая транзакция завершена
            System.out.println("Транзакция 1: SUCCESS");
            
            // ТРАНЗАКЦИЯ 2: Добавление комиссии
            addCommission(session, "commission_account", 5);
            session.commit(); // Вторая транзакция завершена
            System.out.println("Транзакция 2: SUCCESS");
            
            // ТРАНЗАКЦИЯ 3: Логирование операции
            logTransaction(session, "TRANSFER", "From A to B");
            session.commit(); // Третья транзакция завершена
            System.out.println("Транзакция 3: SUCCESS");
            
        } catch (SQLException e) {
            session.rollback();
            System.err.println("Ошибка: " + e.getMessage());
        } finally {
            session.close(); // Сессия закрыта
        }
    }
    
    private static void transferMoney(Connection conn, String from, String to, int amount)
            throws SQLException {
        String sql = "UPDATE accounts SET balance = balance - ? WHERE account = ?";
        PreparedStatement stmt = conn.prepareStatement(sql);
        stmt.setInt(1, amount);
        stmt.setString(2, from);
        stmt.executeUpdate();
        
        sql = "UPDATE accounts SET balance = balance + ? WHERE account = ?";
        stmt = conn.prepareStatement(sql);
        stmt.setInt(1, amount);
        stmt.setString(2, to);
        stmt.executeUpdate();
    }
}

Важные моменты

1. Autocommit режим

По умолчанию в Java setAutoCommit(true), что означает: каждая SQL команда — это отдельная транзакция.

Connection conn = DriverManager.getConnection(url);
// По умолчанию:
// conn.setAutoCommit(true);

conn.executeUpdate("UPDATE ..."); // транзакция 1, автокоммит
conn.executeUpdate("INSERT ...");  // транзакция 2, автокоммит
conn.executeUpdate("DELETE ...");  // транзакция 3, автокоммит

2. Явное управление транзакциями

conn.setAutoCommit(false); // Отключаем автокоммит

// Все операции между соммитами — одна транзакция
conn.executeUpdate("UPDATE ...");
conn.executeUpdate("INSERT ...");
conn.executeUpdate("DELETE ...");
conn.commit(); // Все три операции коммитятся вместе

3. Savepoints — точки спасения внутри транзакции

conn.setAutoCommit(false);

try {
    conn.executeUpdate("INSERT INTO accounts ...");
    
    // Создаём контрольную точку
    Savepoint sp1 = conn.setSavepoint("before_transfer");
    
    conn.executeUpdate("UPDATE accounts SET balance = ...");
    
    if (someCondition) {
        // Откатываемся только до sp1, не до начала транзакции
        conn.rollback(sp1);
    }
    
    conn.commit(); // Коммитим всё (кроме откаченной части)
} catch (SQLException e) {
    conn.rollback(); // Откатываем всю транзакцию
}

Пример с Spring Data

@Service
public class PaymentService {
    @Autowired
    private AccountRepository accountRepository;
    
    // Каждый метод с @Transactional — это отдельная транзакция в рамках одной сессии
    
    @Transactional
    public void transferMoney(String from, String to, BigDecimal amount) {
        Account accountFrom = accountRepository.findById(from).orElseThrow();
        Account accountTo = accountRepository.findById(to).orElseThrow();
        
        accountFrom.setBalance(accountFrom.getBalance().subtract(amount));
        accountTo.setBalance(accountTo.getBalance().add(amount));
        
        accountRepository.save(accountFrom);
        accountRepository.save(accountTo);
        // Транзакция 1 завершена (COMMIT)
    }
    
    @Transactional
    public void addCommission(String account, BigDecimal commission) {
        Account acc = accountRepository.findById(account).orElseThrow();
        acc.setBalance(acc.getBalance().subtract(commission));
        accountRepository.save(acc);
        // Транзакция 2 завершена (COMMIT)
    }
}

// В контроллере:
@RestController
public class PaymentController {
    @Autowired
    private PaymentService paymentService;
    
    @PostMapping("/pay")
    public ResponseEntity<Void> pay() {
        paymentService.transferMoney("A", "B", new BigDecimal("100"));
        // Транзакция 1
        
        paymentService.addCommission("commission", new BigDecimal("5"));
        // Транзакция 2
        
        return ResponseEntity.ok().build();
    }
}

Типовые паттерны

Паттерн 1: Разные сессии для разных операций

// Сессия 1 — запись
Connection session1 = DriverManager.getConnection(url);
session1.setAutoCommit(false);
writeData(session1);
session1.commit();
session1.close();

// Сессия 2 — чтение
Connection session2 = DriverManager.getConnection(url);
readData(session2);
session2.close();

Паттерн 2: Одна сессия, множество транзакций (типичный)

Connection session = DriverManager.getConnection(url);
try {
    session.setAutoCommit(false);
    
    for (Order order : orders) {
        processOrder(session, order);
        session.commit(); // Каждый заказ — отдельная транзакция
    }
} finally {
    session.close();
}

Изоляция и видимость данных

Когда в одной сессии множество транзакций:

Connection session = DriverManager.getConnection(url);
session.setAutoCommit(false);

// Транзакция 1
session.executeUpdate("INSERT INTO users VALUES ('John')");
Session.commit();

// Транзакция 2
ResultSet rs = session.executeQuery("SELECT * FROM users WHERE name = 'John'");
// Видит 'John', потому что предыдущая транзакция коммитилась

Выводы

  1. Одна сессия может содержать множество транзакций — это нормально и типично
  2. Каждая @Transactional — отдельная транзакция, но в рамках одной HTTP сессии
  3. Используй setAutoCommit(false) для явного управления границами транзакций
  4. Savepoints помогают отчасти откатывать транзакцию
  5. Изоляция между транзакциями гарантируется на уровне СУБД
Может ли быть много транзакций в рамках сессии? | PrepBro