GORM中使用事务

209 阅读2分钟

公司用的还是 v1.x版本,业务场景是先根据条件删除一条记录,同时写入一条新的。 要保证两者要么同时执行成功,要么都不执行。本地demo记录一下:

image.png

create table shuang.student
(
    id          int auto_increment
        primary key,
    name        varchar(128) not null,
    age         int          not null,
    id_card     varchar(128) not null,
    last_update date         not null
)
    charset = utf8mb4;


package main

import (
	"fmt"
	"github.com/davecgh/go-spew/spew"
	_ "github.com/go-sql-driver/mysql"
	"github.com/jinzhu/gorm"
	"os"
	"time"
)

type Student struct {
	ID         int64     `gorm:"column:id" db:"id" json:"id" form:"id"`
	Name       string    `gorm:"column:name" db:"name" json:"name" form:"name"`
	Age        int64     `gorm:"column:age" db:"age" json:"age" form:"age"`
	IdCard     string    `gorm:"column:id_card" db:"id_card" json:"id_card" form:"id_card"`
	LastUpdate time.Time `gorm:"column:last_update" db:"last_update" json:"last_update" form:"last_update"`
}

func (Student) TableName() string {
	return "student"
}

func main() {
	dsn := "root:12345678@tcp(127.0.0.1:3306)/shuang?charset=utf8mb4&parseTime=True&loc=Local"

	db, err := gorm.Open("mysql", dsn)

	if err != nil {
		fmt.Fprintln(os.Stderr, "connect db addr :", dsn, "err :", err)
		panic(fmt.Sprintf("连接数据库失败: %s", err))
	}

	db.LogMode(true) // 是否打印每条sql

	db.DB().SetMaxIdleConns(10)
	db.DB().SetMaxOpenConns(200)
	db.DB().SetConnMaxLifetime(10 * time.Second)

	if err != nil {
		panic("failed to connect database")
	}

	// 用于构造数据
	//stu0 := &Student{Name: "爽哥测试333", Age: 200, IdCard: "qwert", LastUpdate: time.Now()}
	//// 插入新数据
	//errC := db.Create(stu0).Error
	//if errC != nil {
	//	fmt.Printf("插入新纪录错误 err:%#v\n", errC)
	//}

	var stu Student
	db.Table("student").Where("id=?", 36).First(&stu)

	spew.Dump("取出的记录为:", stu)

	errT := db.Transaction(func(tx *gorm.DB) error {
		// 先删
		fmt.Println("要删的id是:", stu.ID)

		// errD := tx.Where("name=? ", stu.Name).Delete(&stu).Error
		// 最好传入一个空的&Student{}
		// Limit(1)限制最多只删除一行,需要加在Delete()的前面
		errD := tx.Where("name=? ", stu.Name).Limit(1).Delete(&Student{}).Error

		if errD != nil {
			fmt.Printf("删除纪录错误 err:%#v\n", errD)
			return errD
		}

		// 再故意写入失败(ID冲突),则上面的删除操作需要回滚,原来的记录不能被删掉
		stu2 := &Student{Name: stu.Name, Age: stu.Age, IdCard: stu.IdCard, LastUpdate: time.Now(), ID: 37}

		errC := tx.Create(stu2).Error

		if errC != nil {
			fmt.Printf("写入新纪录错误 err:%#v\n", errC)
			return errC
		}

		return nil

	})

	fmt.Println("事务执行的errT为:", errT)

	// 事务生效的标准就是36这条记录不能真的被删掉。执行后和执行前记录完全一样

	// 将上面stu2从的ID:37 去掉,再执行代码,则36这条会被删掉,同时生成一行新的记录

}


image.png


GORM-事务

如何让gorm输出执行的sql