Transactions
Callback transactions, manual transactions, nested savepoints, retry, and row locks.
Callback transaction
err := db.Transaction(ctx, func(tx *oro.DB) error {
_, err := tx.Use[Product]().Create(ctx, &Product{Code: "P001"})
return err
})
Returning an error rolls back the transaction. Returning nil commits it.
Nested transactions
Nested transactions use savepoints when the driver supports them.
err := db.Transaction(ctx, func(tx *oro.DB) error {
if _, err := tx.Table("orders").Create(ctx, oro.Map{"status": "draft"}); err != nil {
return err
}
return tx.Transaction(ctx, func(inner *oro.DB) error {
_, err := inner.Table("order_logs").Create(ctx, oro.Map{"message": "created"})
return err
})
})
Manual transaction
tx, err := db.Begin(ctx)
if err != nil { return err }
committed := false
defer func() {
if !committed {
_ = tx.Rollback(ctx)
}
}()
if _, err := tx.Use[Product]().Create(ctx, product); err != nil {
return err
}
if err := tx.Commit(ctx); err != nil {
return err
}
committed = true
Manual transactions are useful when transaction boundaries must cross helper functions. Prefer callback transactions for normal application code.
Locks
err := db.Transaction(ctx, func(tx *oro.DB) error {
product, err := tx.Use[Product]().Where("ID", id).LockForUpdate().First(ctx)
if err != nil || product == nil {
return err
}
_, err = tx.Use[Product]().Where("ID", product.ID).Update(ctx, oro.Map{
"Stock": oro.Decrement(1),
})
return err
})
Locks require a transaction. Supported helpers include LockForUpdate, LockForShare, NoWait, and SkipLocked.
Retry and timeout
Configure defaults globally:
db, err := oro.Open(oro.Config{
Timeout: oro.TimeoutConfig{
Query: 5 * time.Second,
Transaction: 10 * time.Second,
},
Retry: oro.RetryConfig{
TxDeadlockAttempts: 3,
},
})
Override per query when needed:
rows, err := db.Use[Product]().Timeout(2 * time.Second).Get(ctx)