🛢️
【Go】Xormの使い方 その③(トランザクション)
はじめに
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の使い方 その①と②も良かったら見てください。
参考
Discussion