前言
在使用gorm持久化框架中,有时为了保持数据一致性,要开启事务进行管理,有效处理错误和回滚的策略,gorm提供了几种事务回滚策略供我们使用
gorm事务回滚
数据一致性
备注:本文使用的是MySQL数据库
1、创建一个user数据库表
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`sex` int(11) NULL DEFAULT NULL,
`userName` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 68 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
2、创建一条数据为69的数据,如下图
使用gorm编写删除69数据和和插入一条数据,并且异常
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"log"
)
type User struct {
Id int64 `gorm:"column:id"`
Name sql.NullString `gorm:"column:userName"`
Sex int `gorm:"column:sex"`
}
func main() {
dsn := "root:123456@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
fmt.Println("连接数据库错误:", err)
return
}
user := User{
Id: 69,
}
result := db.Table("user").Delete(&user)
user1 := User{
Name: sql.NullString{
"aaa",
true,
},
Sex: 1,
}
result = db.Table("user").Create(&user1)
log.Println("保存结果为:", user1)
fmt.Println(result)
var (
a = 1
b = 0
)
c := a / b
log.Println("结果为:", c)
}
结果为
这个时候,可以看到,数据库中69数据已经被删除了,创建了一条70的数据
在程序执行中,发生了异常,事务没有回滚,这个时候,我们需要使用gorm事务保持数据一致性和原子性
gorm事务操作
自动使用事务
1、gorm提供了一个闭包函数,在这个函数中,不需要手动commit提交事务或者Rollback回滚事务,Transaction开启的自动事务是不需要你手动执行Commit() 提交事务 和Rollback()回滚事务的, Commit()方法会在闭包函数执行完毕后自动执行, Rollback()方法会在闭包中有异常时自动执行,例如
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"log"
)
type User struct {
Id int64 `gorm:"column:id"`
Name sql.NullString `gorm:"column:userName"`
Sex int `gorm:"column:sex"`
}
func main() {
dsn := "root:123456@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
fmt.Println("连接数据库错误:", err)
return
}
err3 := db.Transaction(func(tx *gorm.DB) error {
user := User{
Name: sql.NullString{
"aaa",
true,
},
Sex: 1,
}
err1 := tx.Model(&User{}).Table("user").Create(&user).Error
if err1 != nil {
return err1
}
log.Println("数据为:", user)
user1 := User{
Id: 70,
}
err2 := tx.Model(&User{}).Table("user").Delete(&user1).Error
if err2 != nil {
return err2
}
return nil
})
log.Println("错误信息为:", err3)
}
我们只要在事务方法中,返回Error就行,gorm会自动回滚事务
备注: 在操作数据库中,要用tx,否则事务不生效
2、有时候要在函数中执行数据,我们也可以手动回滚数据,如创建一条70数据,
执行以下代码
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"log"
)
type User struct {
Id int64 `gorm:"column:id"`
Name sql.NullString `gorm:"column:userName"`
Sex int `gorm:"column:sex"`
}
func main() {
dsn := "root:123456@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
fmt.Println("连接数据库错误:", err)
return
}
err2 := db.Transaction(func(tx *gorm.DB) error {
defer func() {
if err := recover(); err != nil {
tx.Rollback()
}
}()
user := User{
Name: sql.NullString{
"aaa",
true,
},
Sex: 1,
}
err1 := tx.Model(&User{}).Table("user").Create(&user).Error
if err1 != nil {
return err1
}
log.Println("数据为:", user)
user1 := User{
Id: 70,
}
result1 := tx.Model(&User{}).Table("user").Delete(&user1).Error
if result1 != nil {
return result1
}
var (
a = 1
b = 0
)
c := a / b
log.Println("数据为:", c)
return nil
})
log.Println("错误信息为:", err2)
}
结果为
可以看到,事务进行回滚了
手动开启事务
gorm框架也提供手动开启事务方式
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"log"
)
type User struct {
Id int64 `gorm:"column:id"`
Name sql.NullString `gorm:"column:userName"`
Sex int `gorm:"column:sex"`
}
func main() {
dsn := "root:123456@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
fmt.Println("连接数据库错误:", err)
return
}
say(db)
log.Println("===============执行结束")
}
func say(db *gorm.DB) {
tx := db.Begin()
defer func() {
if err := recover(); err != nil {
log.Println("异常信息为:", err)
tx.Rollback()
}
}()
user := User{
Name: sql.NullString{
"aaa",
true,
},
Sex: 1,
}
result := tx.Model(&User{}).Table("user").Create(&user)
log.Println("数据为:", user)
log.Println("数据为:", result)
user1 := User{
Id: 74,
}
result1 := tx.Model(&User{}).Table("user").Delete(&user1)
log.Println("数据为:", result1)
tx.Commit()
var (
a = 1
b = 0
)
c := a / b
log.Println("数据为:", c)
log.Println("===============执行结束")
tx.Commit()
}
总结
合理使用事务,能帮住保持数据一致性