今回は、Goでトランザクション処理を書いてみます。
import (
"database/sql"
"log"
)
func withTransaction(ctx context.Context, db *sql.DB, txFunc func(*sql.Tx) error) error {
tx, err := db.BeginTx(ctx, nil)
if err != nil {
log.Println(err.Error())
return err
}
defer func() {
if p := recover(); p != nil {
tx.Rollback()
panic(p) // re-throw panic after Rollback
} else if err != nil {
log.Println(err)
tx.Rollback()
} else {
err = tx.Commit()
}
}()
err = txFunc(tx)
return err
}
第3引数には、第1引数に*sql.Txが入り、error型が返り値の無名関数が入ります。
実際の使われ方をみた方が早いので以下を参考にしてください。
GoのクエリビルダSquirrelを使用しています。
type userRepository struct {
DBClient *sql.DB
}
func (r *userRepository) CreateUser(ctx context.Context, userID string) error {
query := sq.Insert(usersTable).
SetMap(sq.Eq{
"id": userID,
})
return withTransaction(ctx, r.DBClient, func(tx *sql.Tx) error {
if _, err := query.RunWith(tx).ExecContext(ctx); err != nil {
log.Println(err)
return err
}
return nil
}
txFuncでエラーやpanicが発生したらdefer funcの中でtx.Rollbackが呼ばれ、dbの内容の修正が取り消され、正常終了した場合はtx.Commitがされます。