Transacciones
En esta página
db.transaction(|tx| { ... }) envuelve un bloque de operaciones en una transacción
ACID. El handle tx es intercambiable con db — se llama a tx.execute,
tx.query, etc. exactamente igual que fuera de la transacción.
Al final del bloque, Zolo realiza un commit automático. Si ocurre un pánico o un error no controlado dentro del callback, la transacción hace rollback y la base de datos vuelve al estado anterior.
Transferencia bancaria atómica: débito y crédito en dos cuentas dentro de una única transacción.
// Feature: Database — transactions with `db.transaction(fn)`
// Syntax: the callback receives the same db; auto-commit on return,
// auto-rollback on panic or error.
// When to use: multi-step operations that must be atomic (bank
// transfer, batch insert).
use std::database::Database
let db = Database.open("sqlite://:memory:").unwrap()
defer db.close()
db.execute("CREATE TABLE accounts (id INTEGER PRIMARY KEY, balance REAL)").unwrap()
db.execute("INSERT INTO accounts VALUES (1, 100.0)").unwrap()
db.execute("INSERT INTO accounts VALUES (2, 50.0)").unwrap()
// Transfer: debit 1, credit 2 — atomic.
fn transfer(tx, from_id: int, to_id: int, amount: float) {
tx.execute("UPDATE accounts SET balance = balance - ? WHERE id = ?", [amount, from_id]).unwrap()
tx.execute("UPDATE accounts SET balance = balance + ? WHERE id = ?", [amount, to_id]).unwrap()
}
db.transaction(|tx| {
transfer(tx, 1, 2, 30.0)
})
let rows = db.query("SELECT id, balance FROM accounts ORDER BY id").unwrap()
for row in rows {
print("account {row.id}: {row.balance}")
}
// expected:
// account 1: 70
// account 2: 80
Requiere la CLI o el host de Zolo; ábrelo en el playground o ejecútalo localmente.
La misma transferencia usando sql"..." con interpolación — los valores capturados
son vinculados, no concatenados.
// Feature: `db.transaction(|tx| { ... })` mixed with `sql"..."`
// Syntax: the callback receives a tx handle interchangeable with `db`
// for `sql"..."` execution. Auto-commits on return; auto-rollback on
// panic or error.
// When to use: multi-step writes that must be atomic — bank transfers,
// inventory updates, rename + foreign-key fix-ups. Compare with the
// older form in `05-transactions.zolo` (positional binding).
use std::database::Database
let db = Database.open("sqlite://:memory:").unwrap()
defer db.close()
db.execute(/* sql */ "CREATE TABLE accounts (id INTEGER PRIMARY KEY, balance INTEGER)").unwrap()
db.execute(/* sql */ "INSERT INTO accounts (id, balance) VALUES (1, 100)").unwrap()
db.execute(/* sql */ "INSERT INTO accounts (id, balance) VALUES (2, 50)").unwrap()
let from_id = 1
let to_id = 2
let amount = 30
// Both UPDATEs use `sql"..."` interpolation — captured values are bound.
db.transaction(|tx| {
sql"UPDATE accounts SET balance = balance - {amount} WHERE id = {from_id}".execute(tx).unwrap()
sql"UPDATE accounts SET balance = balance + {amount} WHERE id = {to_id}".execute(tx).unwrap()
})
let rows = sql"SELECT id, balance FROM accounts ORDER BY id".query(db).unwrap()
for row in rows {
print("{row.id}: {row.balance}")
}
// expected:
// 1: 70
// 2: 80
Requiere la CLI o el host de Zolo; ábrelo en el playground o ejecútalo localmente.
Desafío
Simula un fallo en medio de la transacción (p. ej. error("saldo insuficiente")) y
verifica que los saldos permanecen sin cambios tras el rollback.
Consulta también