公司用的还是 v1.x版本,业务场景是先根据条件删除一条记录,同时写入一条新的。 要保证两者要么同时执行成功,要么都不执行。本地demo记录一下:
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这条会被删掉,同时生成一行新的记录 }