事务管理
概述
事务用于保证一组数据库操作的原子性、一致性、隔离性和持久性。Juice 提供两套事务使用方式:
手动事务(
engine.Tx()/engine.ContextTx(...))函数式事务(
juice.Transaction(...)/juice.NestedTransaction(...))
事务接口
Juice 的事务相关核心接口如下:
type Manager interface {
Object(v any) SQLRowsExecutor
}
type TxManager interface {
Manager
Begin() error
Commit() error
Rollback() error
}
手动事务
适合你需要显式控制 Begin/Commit/Rollback 的场景:
tx := engine.Tx()
if err := tx.Begin(); err != nil {
return err
}
defer tx.Rollback()
if _, err := tx.Object(Repo{}.CreateUser).ExecContext(ctx, user); err != nil {
return err
}
if _, err := tx.Object(Repo{}.CreateOrder).ExecContext(ctx, order); err != nil {
return err
}
return tx.Commit()
Note
建议始终使用 defer tx.Rollback() 作为兜底,成功路径再执行 tx.Commit()。
函数式事务
函数式事务会在回调中自动注入事务 manager,适合 service 层封装:
baseCtx := juice.ContextWithManager(context.Background(), engine)
err := juice.Transaction(baseCtx, func(ctx context.Context) error {
repo := NewUserRepository()
_, err := repo.CreateUser(ctx, user)
return err
},
tx.WithIsolationLevel(sql.LevelReadCommitted),
tx.WithReadOnly(false),
)
if err != nil {
return err
}
Transaction 的行为是:回调返回 nil 时提交,返回非 nil 时回滚。
嵌套调用语义
NestedTransaction 的语义是“已在事务中则复用当前事务,否则新开事务”:
err := juice.Transaction(baseCtx, func(ctx context.Context) error {
if err := serviceA(ctx); err != nil {
return err
}
return juice.NestedTransaction(ctx, func(ctx context.Context) error {
return serviceB(ctx)
})
})
Attention
NestedTransaction 不是数据库 savepoint 语义;它不会自动创建独立的内层提交点。
隔离级别与只读选项
你可以在事务开启时指定隔离级别和只读属性:
err := juice.Transaction(baseCtx, handler,
tx.WithIsolationLevel(sql.LevelSerializable),
tx.WithReadOnly(true),
)
这些选项与 database/sql 的 sql.TxOptions 对齐。