🛢️

【Go】Xormの使い方 その③(トランザクション)

2022/02/20に公開

はじめに

Xormのトランザクションの記事が少ないかと思いましたので、少しまとめてみました。

環境

go 1.17
github.com/go-sql-driver/mysql v1.6.0
xorm.io/xorm v1.2.5

前提

idのオートインクリメントや、外部キーの設定などは、説明が増えるので今回は対応していません。

Transaction(トランザクション)

Xormでトランザクションを対応する場合は、下記のメソッドを使用します。
また、先にsessionを作成する必要があります。

engine.NewSession()

Begin()

トランザクションを開始する時に使用します。

session.Begin()

Commit()

トランザクションを終了する際に使用します。

session.Commit()

Rollback()

トランザクション内で何かしら処理が失敗した際に、ロールバックさせる為に使用します。

session.Rollback()

動かしてみる

実際にソースコード書いて動かしてみます。

  • DockerでMySQLを使います。
  • Goのコードはmain.goに全部書きしてます。
  • go.mod使用してます。

条件

今回は3つのテーブルを使用します。

  • usersテーブル
  • itemsテーブル
  • user_has_itemsテーブル
    • userが持っているitemのテーブル(中間テーブル)

userがコインを払ってitemを取得するという事をトランザクションで行いたいと思います。
下記のテーブルが既に作成されていて、下記のデータが既に登録されている状態で進めます。
今回はuser1の太郎がitem1の普通の剣を購入するという処理を実施します。

テーブル構成

  • usersテーブル

    • id
    • name
    • coin
    • age
  • itemsテーブル

    • id
    • Name
    • Price
  • user_has_itemsテーブル

    • id
    • user_id
    • item_id

DBデータ

  • usersテーブル
    • id: 1
    • name: 太郎
    • coin: 100
    • age: 20

  • itemsテーブル
    • id: 1
    • name: 普通の剣
    • price: 20

ソースコード

docker-compose.yml

docker-compose.yml

version: "3"
services:
  db:
    image: mysql:5.7
    environment:
    - MYSQL_DATABASE=test_db
    - MYSQL_ROOT_PASSWORD=password
    command: >
      --character-set-server=utf8mb4
      --collation-server=utf8mb4_general_ci
    ports:
      - 3306:3306

main.go

type Users struct {
	ID   int `xorm:"id"`
	Name string
	Coin int
	Age  int
}

type Items struct {
	ID    int `xorm:"id"`
	Name  string
	Price int
}

type UserHasItems struct {
	ID     int `xorm:"id"`
	UserID int `xorm:"user_id"`
	ItemID int `xorm:"item_id"`
}

func main() {
	//enginを作成します。今回使用するのはDockerで立てたMySQLです。
	engine, err := xorm.NewEngine("mysql", "root:password@tcp([127.0.0.1]:3306)/test_db?charset=utf8mb4&parseTime=true")
	if err != nil {
		log.Fatal(err)
	}
	
	//buyItemというメソッドを呼び出します
	buyItem(*engine)
}

func buyItem(engine xorm.Engine) {

	// sessionを作成します
	session := engine.NewSession()
	// 最後にsessionを閉じます
	defer session.Close()

	// トランザクションを開始します
	err := session.Begin()

	// ユーザーからコインを減らします
	// コインだけ更新
	user := Users{
		Coin: 80,
	}
	_, err := engine.Where("id =?", 1).Update(&user)
	if err != nil {
		//もし、エラーがあった場合に処理を戻します
		session.Rollback()
		return
	}

	// ユーザーにitemを持たせます
	userItem := UserHasItems{
		ID:     1,
		UserID: 1,
		ItemID: 1,
	}
	_, err = engine.Table("user_has_items").Insert(userItem)
	if err != nil {
		//もし、エラーがあった場合に処理を戻します
		session.Rollback()
		return
	}	
	
	// トランザクションを終了します
	session.Commit()
}	

結果

上記のコードをgo run main.goをすると、上手く動くはずです。

user_has_itemsテーブルにデータが追加され、userテーブルの太郎のコインが80に減っています。

さいごに

  • buyItemは実務の処理とはかけ離れているメソッドになってしまいました。
  • 本当は、ItemsテーブルからpriceをGetしてとか、やろうと思ったのですが、めんどくさくなってやめました。(Itemsテーブルの意味がなくなった。)
  • なんとなくトランザクションの使い方は分かれば良しとします。
  • 良かったら、Xormの使い方 その①と②も良かったら見てください。

https://zenn.dev/a_ichi1/articles/5d9fbf3fbfea38
https://zenn.dev/a_ichi1/articles/0460e6a0161708

参考

https://pkg.go.dev/github.com/go-xorm/xorm
https://gobook.io/read/gitea.com/xorm/manual-en-US/chapter-10/index.html

Discussion