Ответ
Нет, стандартный пакет database/sql
в Go не поддерживает концепцию вложенных транзакций напрямую. Если вы находитесь внутри объекта *sql.Tx
и вызовете у него метод Begin()
, вы получите ошибку, так как этот метод существует только у *sql.DB
.
Однако, некоторые СУБД (например, PostgreSQL, MySQL, MS SQL Server) поддерживают механизм SAVEPOINT
(точек сохранения), который позволяет эмулировать поведение вложенных транзакций.
Работать с ними нужно вручную, выполняя SQL-команды:
SAVEPOINT <имя_точки>
: создает точку сохранения.ROLLBACK TO SAVEPOINT <имя_точки>
: откатывает транзакцию до указанной точки.RELEASE SAVEPOINT <имя_точки>
: удаляет точку сохранения, оставляя изменения.
Пример эмуляции для PostgreSQL:
tx, err := db.Begin()
if err != nil {
log.Fatal(err)
}
// Гарантируем откат в случае паники или раннего выхода
defer tx.Rollback()
// Основная часть транзакции
_, err = tx.Exec(`INSERT INTO users (name) VALUES ('Alice')`)
if err != nil { ... }
// Эмуляция "вложенной" транзакции
_, err = tx.Exec("SAVEPOINT sp1")
if err != nil { ... }
_, err = tx.Exec(`UPDATE users SET balance = balance - 100 WHERE name = 'Alice'`)
if err != nil {
// "Откатываем" вложенную транзакцию
tx.Exec("ROLLBACK TO SAVEPOINT sp1")
} else {
// "Коммитим" вложенную транзакцию
tx.Exec("RELEASE SAVEPOINT sp1")
}
// Коммитим основную транзакцию
if err = tx.Commit(); err != nil {
log.Fatal(err)
}
Важно: Это не настоящие вложенные транзакции. Если вы откатите внешнюю транзакцию (tx.Rollback()
), все изменения, включая те, что были сделаны внутри SAVEPOINT
, будут отменены. Использование SAVEPOINT
усложняет код и зависит от конкретной СУБД, поэтому часто лучшим решением является пересмотр логики для избежания необходимости во вложенных транзакциях.