トランザクションは、トランザクションオブジェクトを生成して使います。
今回の例では、id列をprimary keyにしています。また、前回のINSERTを実行し、id=123のレコードを既に登録済みにしています。
package main
import (
"database/sql"
"fmt"
"github.com/go-sql-driver/mysql"
)
func main() {
db, err := sql.Open("mysql", "gotest:gotest@/demo")
if err != nil {
fmt.Printf("Failed to connect")
return
}
defer db.Close()
tr, err := db.Begin()
if err != nil {
fmt.Printf("Failed to begin transaction : %s", err)
return
}
statement, err := tr.Prepare("INSERT INTO user(id,email) VALUES(?,?)")
if err != nil {
fmt.Printf("Failed to prepare : %s", err)
tr.Rollback()
return
}
defer statement.Close()
if !insert("124", "demo@fkmsoft.jp", statement) {
tr.Rollback()
return
}
fmt.Printf("created! 124/demo@fkmsoft.jp\n")
if !insert("123", "demo@fkmsoft.jp", statement) {
tr.Rollback()
return
}
fmt.Printf("created! 123/demo@fkmsoft.jp\n")
tr.Commit()
}
func insert(id, email string, statement *sql.Stmt) bool {
_, err := statement.Exec(id, email)
if err != nil {
if err2, ok := err.(*mysql.MySQLError); ok {
fmt.Printf("Failed to execute Query : %s Number=%d", er\
r, err2.Number)
} else {
fmt.Printf("Failed to execute Query : %s", err, err)
}
return false
}
return true
}
ポイントだけ見ていきましょう。
tr, err := db.Begin()
if err != nil {
fmt.Printf("Failed to begin transaction : %s", err)
return
}
Begin()メソッドでトランザクションオブジェクトを生成します。End()は無いので注意。
statement, err := tr.Prepare("INSERT INTO user(id,email) VALUES(?,?)")
if err != nil {
fmt.Printf("Failed to prepare : %s", err)
tr.Rollback()
return
}
defer statement.Close()
TxのPrepare()メソッドでStmtを作ります。dbがTxに変わっただけですね。
Stmtを作った後は同じです。最後にコミット/ロールバック
成功した時はCommit()メソッドを呼びます。
tr.Commit()
失敗し、ロールバックしたい時はRollback()メソッドを呼びます。ホントはdeferで呼んだほうがいいかも。
tr.Rollback()
冒頭の条件で実行すると、1行分、成功のメッセージが出た後、エラーメッセージが出てロールバックされます。