使うシーン
- 一括で複数のInsert Updateで全部成功させないといけないシーン
- リレーションを貼っているテーブルに対してデータの更新や追加などを一括して追加変更しないといけないテーブルが存在するシーン
Go言語でトランザクション制御
- トランザクション制御のラッパー関数の作成
- トランザクションラッパー関数を開始し、エラーだったらrollbackを実行エラーじゃなければcommit
Begin()、Commit()、Rollback()が同じ関数内に定義(これは冗長)
- SqlHandlerポインタレシーバからDB接続を定義し同じ関数内で実行
func (h *SqlHandler) WithTransaction() (err error) {
tx, err := h.db.Begin()
if err != nil {
return
}
defer func() {
if err != nil {
tx.Rollback()
return
}
err = tx.Commit()
}()
if _, err = tx.Exec(...); err != nil {
return
}
if _, err = tx.Exec(...); err != nil {
return
}
// ...
return
}
func (t *sql.Tx)Exec(...){
// Insert or Update SQL
// ...
}
トランザクションラッパー関数
- 実行したい関数を引数 txFuncとして受け取り、トランザクションを開始してからtxFuncを実行
- txFuncの中でerrorもしくはpanicが起きたらdeferでrecoverを使って広い、rollbackを実行
- 問題がなければcommitという流れです。
- トランザクション制御を行う際に、トランザクション境界線がリクエストの開始〜終了となりますが、ラッパー関数方式の場合トランザクション境界を柔軟に変更可能
func (t *sqlRepository) ExecWithTx(txFunc func(*sql.Tx) error) (err error) {
tx, err := t.db.Begin()
if err != nil {
return
}
defer func() {
if p := recover(); p != nil {
tx.Rollback()
} else if err != nil {
tx.Rollback()
} else {
err = tx.Commit()
}
}()
err = txFunc(tx)
return err
}
トランザクションラッパー関数使い方(サンプル)
- 実行したいSQL関数を引数txFunc内に記述
func (r *Repository) DoSomething() error {
return r.DB.ExecWithTx(func(tx *sql.Tx) error {
if _, err := tx.Exec(...); err != nil { // ここにSQL系関数定義
return err
}
if _, err := tx.Exec(...); err != nil { // ここにSQL系関数定義
return err
}
return nil
})
}