Use Grove instead.
Main API01
WithTransaction(ctx, db, fn)WithTransactionOptions(ctx, db, opts, fn)WithNestedTransaction(ctx, fn)GetDB(ctx, defaultDB)MustGetDB(ctx, defaultDB)IsInTransaction(ctx)GetTransactionDepth(ctx)
Isolation helpers:
WithSerializableTransactionWithReadOnlyTransactionWithRepeatableReadTransaction
Transaction-Aware Repository Pattern02
err := database.WithTransaction(ctx, db, func(txCtx context.Context) error {
txDB := database.GetDB(txCtx, db)
repo := database.NewRepository[User](txDB)
if err := repo.Create(txCtx, &user); err != nil {
return err
}
return nil
})GetDB returns active bun.Tx when inside transaction, else returns default DB.
Nested Transactions03
Nested calls use SQL savepoints.
inner failure can roll back to savepoint
outer transaction can continue
Maximum nesting depth is controlled by MaxTransactionDepth (currently 10).
Panic Recovery Behavior04
Transaction wrapper catches panics and converts them into errors.
This avoids process-level crashes and ensures rollback behavior is preserved.
Container/App Convenience Wrappers05
WithTransactionFromContainer(ctx, c, name, fn)WithTransactionFromApp(ctx, app, name, fn)
These resolve named SQL DB first, then run transaction wrapper.
Good Practices06
Always use
GetDBinside transaction callbacks.Keep transactions short and deterministic.
Use
WithTransactionOptionsfor explicit isolation requirements.Use savepoints intentionally; avoid deep nesting unless required.