Go言語とSQLiteで下記のコードをトランザクションしてみる。

トランザクションとは、分割不可な一連の処理を意味する。

今回は、user_id=1のpriceから1000円を引き、user_id=2のpriceに1000円たすという処理を行うが、この時、途中でエラーが起こった場合は、どの処理もされないようにしたい。この時にトランザクションの仕組みを利用する。

package main

import (
	"database/sql"
	"log"
	"strconv"

	_ "github.com/mattn/go-sqlite3"
)

func main() {
	db, err := sql.Open("sqlite3", "sample.db")
	if err != nil {
		log.Fatal(err)
	}

	tx, err := db.Begin()
	if err != nil {
		log.Fatal(err)
	}

	/** ここからuser_idが1のpriceから1000を引いて更新する処理 **/
	rows, _ := tx.Query("SELECT price FROM calc WHERE user_id=1;")

	var price int
	for rows.Next() {
		err = rows.Scan(&price)
		if err != nil {
			log.Fatal(err)
		}
	}

	price = price - 1000
	newprice := strconv.Itoa(price)
	t := "UPDATE calc SET price = " + newprice + " WHERE user_id=1"
	_, err = tx.Exec(t)
	if err != nil {
		log.Fatal(err)
	}
	/** ここまでuser_idが1のpriceから1000を引いて更新する処理 **/


	/** ここからuser_idが2のpriceから1000を足して更新する処理 **/
	rows, _ = tx.Query("SELECT price FROM calc WHERE user_id=2;")
	defer rows.Close()

	for rows.Next() {
		err = rows.Scan(&price)
		if err != nil {
			log.Fatal(err)
		}
	}
	price = price + 1000
	newprice = strconv.Itoa(price)
	t = "UPDATE calc SET price = " + newprice + " WHERE user_id=2"
	_, err = tx.Exec(t)
	if err != nil {
		log.Fatal(err)
	}
	/** ここまでuser_idが2のpriceから1000を足して更新する処理 **/

	tx.Commit()
}

sql.Openした後、tx, err := db.Begin()でトランザクションを開始する。これ以降dbをtx表記しなければならない。

最後にtx.Commit()でトランザクションを終了する。

tx.Commitの前に一度でもエラーがあって処理がとまった場合は、エラー前に行われたSQL構文の実行結果は、全てデータベースに反映されない。