Транзакция в Go
Транзакция объединяет несколько шагов: либо все они попадают в файл, либо ни один. Начинают транзакцию, выполняют запросы через объект транзакции, затем фиксируют успех или откатывают назад.
При ошибке на любом шаге вызывают откат, чтобы не оставить половину изменений. Фиксацию не забывают после успеха: без нее изменения могут не сохраниться.
В одной транзакции вставим два товара:
package main
import (
"database/sql"
"fmt"
"log"
"os"
_ "modernc.org/sqlite"
)
func main() {
os.Remove("shop.db")
db, err := sql.Open("sqlite", "shop.db")
if err != nil {
log.Fatal(err)
}
defer db.Close()
err = db.Ping()
if err != nil {
log.Fatal(err)
}
_, err = db.Exec(`
CREATE TABLE goods (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
price REAL NOT NULL
)
`)
if err != nil {
log.Fatal(err)
}
tx, err := db.Begin()
if err != nil {
log.Fatal(err)
}
_, err = tx.Exec(
`INSERT INTO goods (title, price) VALUES (?, ?)`,
"pen", 2.0,
)
if err != nil {
tx.Rollback()
log.Fatal(err)
}
_, err = tx.Exec(
`INSERT INTO goods (title, price) VALUES (?, ?)`,
"book", 12.0,
)
if err != nil {
tx.Rollback()
log.Fatal(err)
}
err = tx.Commit()
if err != nil {
log.Fatal(err)
}
var count int
err = db.QueryRow(`SELECT COUNT(*) FROM goods`).Scan(&count)
if err != nil {
log.Fatal(err)
}
fmt.Println(count)
}
На экране 2. Обе вставки
зафиксированы вместе.
При сбое второго шага откат оставит таблицу пустой:
package main
import (
"database/sql"
"fmt"
"log"
"os"
_ "modernc.org/sqlite"
)
func main() {
os.Remove("shop.db")
db, err := sql.Open("sqlite", "shop.db")
if err != nil {
log.Fatal(err)
}
defer db.Close()
err = db.Ping()
if err != nil {
log.Fatal(err)
}
_, err = db.Exec(`
CREATE TABLE goods (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
price REAL NOT NULL
)
`)
if err != nil {
log.Fatal(err)
}
tx, err := db.Begin()
if err != nil {
log.Fatal(err)
}
_, err = tx.Exec(
`INSERT INTO goods (title, price) VALUES (?, ?)`,
"pen", 2.0,
)
if err != nil {
tx.Rollback()
log.Fatal(err)
}
_, err = tx.Exec(
`INSERT INTO goods (title, price) VALUES (?, ?)`,
nil, 12.0,
)
if err != nil {
tx.Rollback()
var count int
_ = db.QueryRow(`SELECT COUNT(*) FROM goods`).Scan(&count)
fmt.Println("rolled back", count)
return
}
err = tx.Commit()
if err != nil {
log.Fatal(err)
}
}
Даны два запроса вставки в orders
внутри одной транзакции. Первый прошел,
второй вернул ошибку. Скажите, что
нужно вызвать до выхода из функции.
Транзакция начата, оба запроса прошли без ошибки. Скажите, какой шаг завершит запись в файл.
Скажите, чем запрос через объект транзакции отличается от того же текста, отправленного напрямую через соединение, пока транзакция не зафиксирована.