Transactions¶
To declare a transaction, use the Transaction
method which can be called recursively to define nested transactions.
Rel accepts a function with context.Context
argument that is used to determine the transaction scope. Context makes it easier to call any function that involves db operation inside a transaction, because the scope of transaction is automatically passed by context.
If any error occured within transaction, the transaction will be rolled back, and returns the error. If the error is a runtime error or panic
with string argument, it'll panic after rollback.
err := repo.Transaction(ctx, func(ctx context.Context) error {
repo.Update(ctx, &book, rel.Dec("stock"))
// Any database calls inside other function will be using the same transaction as long as it share the same context.
Process(ctx, transaction)
// Nested transaction
repo.Transaction(ctx, func(ctx context.Context) error {
repo.UpdateAny(ctx, rel.From("authors").Where(where.Eq("id", book.AuthorID)), rel.Inc("popularity"))
repo.UpdateAny(ctx, rel.From("publishers").Where(where.Eq("name", book.Publisher)), rel.Inc("popularity"))
return nil
})
return repo.Update(ctx, &transaction, rel.Set("status", "paid"))
})
repo.ExpectTransaction(func(repo *reltest.Repository) {
repo.ExpectUpdate(rel.Dec("stock")).ForType("main.Book")
// mock process
repo.ExpectTransaction(func(r *reltest.Repository) {
repo.ExpectUpdateAny(rel.From("authors").Where(where.Eq("id", 0)), rel.Inc("popularity"))
repo.ExpectUpdateAny(rel.From("publishers").Where(where.Eq("name", "")), rel.Inc("popularity"))
})
repo.ExpectUpdate(rel.Set("status", "paid")).ForType("main.Transaction")
})
Last update: 2024-12-19