当前位置:网站首页>gorm事务体验
gorm事务体验
2022-06-28 05:06:00 【.番茄炒蛋】
简介
表
CREATE TABLE `accounts` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(10) NOT NULL,
`money` decimal(30,2) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

没有事务
package main
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"log"
"os"
"time"
)
type Account struct {
ID uint `gorm:"primarykey"`
Name string `gorm:"type:varchar(10);not null"`
Money float64 `gorm:"type:decimal(30,2);not null"`
}
func main() {
// 连接对应的数据库
dsn := fmt.Sprintf("%s:%[email protected](%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
"root", "root", "127.0.0.1", 3306, "test")
newLogger := logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer(日志输出的目标,前缀和日志包含的内容——译者注)
logger.Config{
SlowThreshold: time.Second, // 慢 SQL 阈值
LogLevel: logger.Info, // 日志级别
IgnoreRecordNotFoundError: true, // 忽略ErrRecordNotFound(记录未找到)错误
Colorful: true, // 使用用彩色打印
},
)
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: newLogger,
})
if err != nil {
panic(err)
}
a := 1 - 1
// 模拟张三给李四转100
account := Account{
ID: 1, Name: "张三", Money: 900}
account1 := Account{
ID: 2, Name: "李四", Money: 1100}
db.Save(&account)
if true {
// 手动制造一个错误
_ = 1 / a
}
db.Save(&account1)
}

在代码中手动制造了一个异常,正常情况下应该转账失败,两个人的余额都还是1000,但是由于没有使用事务,张三已经发生了扣款,但是李四却没有收到钱,这在真实的场景中是绝对不能接受的.
事务
package main
import (
"errors"
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"log"
"os"
"time"
)
type Account struct {
ID uint `gorm:"primarykey"`
Name string `gorm:"type:varchar(10);not null"`
Money float64 `gorm:"type:decimal(30,2);not null"`
}
func main() {
// 连接对应的数据库
dsn := fmt.Sprintf("%s:%[email protected](%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
"root", "root", "127.0.0.1", 3306, "test")
newLogger := logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer(日志输出的目标,前缀和日志包含的内容——译者注)
logger.Config{
SlowThreshold: time.Second, // 慢 SQL 阈值
LogLevel: logger.Info, // 日志级别
IgnoreRecordNotFoundError: true, // 忽略ErrRecordNotFound(记录未找到)错误
Colorful: true, // 使用用彩色打印
},
)
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: newLogger,
})
if err != nil {
panic(err)
}
db.Transaction(func(tx *gorm.DB) error {
// 模拟张三给李四转100
account := Account{
ID: 1, Name: "张三", Money: 800}
account1 := Account{
ID: 2, Name: "李四", Money: 1100}
tx.Save(&account)
if true {
return errors.New("手动制造一个错误")
}
tx.Save(&account1)
return nil
})
}

使用事务以后就是正常的,出现错误会回滚,返回任何错误都会回滚事务
嵌套事务
package main
import (
"errors"
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"log"
"os"
"time"
)
type Account struct {
ID uint `gorm:"primarykey"`
Name string `gorm:"type:varchar(10);not null"`
Money float64 `gorm:"type:decimal(30,2);not null"`
}
func main() {
// 连接对应的数据库
dsn := fmt.Sprintf("%s:%[email protected](%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
"root", "root", "127.0.0.1", 3306, "test")
newLogger := logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer(日志输出的目标,前缀和日志包含的内容——译者注)
logger.Config{
SlowThreshold: time.Second, // 慢 SQL 阈值
LogLevel: logger.Info, // 日志级别
IgnoreRecordNotFoundError: true, // 忽略ErrRecordNotFound(记录未找到)错误
Colorful: true, // 使用用彩色打印
},
)
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: newLogger,
})
if err != nil {
panic(err)
}
db.Transaction(func(tx *gorm.DB) error {
// 模拟张三给李四转100
account := Account{
ID: 1, Name: "张三", Money: 800}
account1 := Account{
ID: 2, Name: "李四", Money: 1100}
tx.Save(&account)
tx.Transaction(func(tx *gorm.DB) error {
// 嵌套事务中模仿王五给赵六转100
account2 := Account{
ID: 3, Name: "王五", Money: 900}
account3 := Account{
ID: 4, Name: "赵六", Money: 1100}
tx.Save(account2)
if true {
return errors.New("手动制造一个错误")
}
tx.Save(account3)
return nil
})
tx.Save(&account1)
return nil
})
}

这次错误是发生再嵌套事务中,张三给李四转账是正常完成的,但是王五给赵六转账是出现回滚的
手动事务
package main
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"log"
"os"
"time"
)
type Account struct {
ID uint `gorm:"primarykey"`
Name string `gorm:"type:varchar(10);not null"`
Money float64 `gorm:"type:decimal(30,2);not null"`
}
func main() {
// 连接对应的数据库
dsn := fmt.Sprintf("%s:%[email protected](%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
"root", "root", "127.0.0.1", 3306, "test")
newLogger := logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer(日志输出的目标,前缀和日志包含的内容——译者注)
logger.Config{
SlowThreshold: time.Second, // 慢 SQL 阈值
LogLevel: logger.Info, // 日志级别
IgnoreRecordNotFoundError: true, // 忽略ErrRecordNotFound(记录未找到)错误
Colorful: true, // 使用用彩色打印
},
)
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: newLogger,
})
if err != nil {
panic(err)
}
// 模拟张三给李四转100
// 开启事务
tx := db.Begin()
account := Account{
ID: 1, Name: "张三", Money: 700}
account1 := Account{
ID: 2, Name: "李四", Money: 1200}
if result := tx.Save(&account); result.Error != nil {
// 出现错误回滚
tx.Rollback()
}
if result := tx.Save(&account1); result.Error != nil {
// 出现错误回滚
tx.Rollback()
}
// 提交事务
tx.Commit()
}

中间没有出现任何错误,提交事务转账成功.手动事务对error进行判断,如果error不为nil就直接使用tx.Rollback()回滚事务就好了.
SavePoint、RollbackTo
GORM 提供了 SavePoint、Rollbackto 方法,来提供保存点以及回滚至保存点功能
package main
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"log"
"os"
"time"
)
type Account struct {
ID uint `gorm:"primarykey"`
Name string `gorm:"type:varchar(10);not null"`
Money float64 `gorm:"type:decimal(30,2);not null"`
}
func main() {
// 连接对应的数据库
dsn := fmt.Sprintf("%s:%[email protected](%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
"root", "root", "127.0.0.1", 3306, "test")
newLogger := logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer(日志输出的目标,前缀和日志包含的内容——译者注)
logger.Config{
SlowThreshold: time.Second, // 慢 SQL 阈值
LogLevel: logger.Info, // 日志级别
IgnoreRecordNotFoundError: true, // 忽略ErrRecordNotFound(记录未找到)错误
Colorful: true, // 使用用彩色打印
},
)
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: newLogger,
})
if err != nil {
panic(err)
}
// 模拟张三给李四转100
// 模拟王五给赵六转100
account := Account{
ID: 1, Name: "张三", Money: 600}
account1 := Account{
ID: 2, Name: "李四", Money: 1300}
account2 := Account{
ID: 3, Name: "王五", Money: 900}
account3 := Account{
ID: 4, Name: "赵六", Money: 1100}
tx := db.Begin()
tx.Save(&account)
tx.Save(&account1)
tx.SavePoint("test")
tx.Save(&account2)
tx.Save(&account3)
if true {
// 在这里回滚,只完成张三给李四转账
tx.RollbackTo("test")
}
tx.Commit()
}

使用tx.RollbackTo(“test”)回滚到tx.SavePoint(“test”)之前,只完成张三给李四转账;未完成王五给赵六的转账
注意



再官方文档中,作者秉承着重要的事情说三遍的原则,事务一旦开始就应该使用 tx 处理数据,这一点需要特别注意,如果使用事务之前的db处理事务,出现错误是不会回滚的
边栏推荐
- DH parameters of robotics and derivation using MATLAB symbolic operation
- Organize the online cake mall project
- 为什么大厂不让使用undefined
- 【JVM系列】JVM调优
- Study on modified triphosphate: lumiprobe amino-11-ddutp
- BioVendor sRAGE Elisa试剂盒化学性质和技术研究
- Store inventory management system source code
- Assembly common instructions
- RxSwift --(1)创建一个项目
- JS 文本框失去焦点修改全半角文字和符号
猜你喜欢

mysql----where 1=1是什么意思

CpG solid support research: lumiprobe general CpG type II

Amino dye research: lumiprobe fam amine, 6-isomer

How high is the gold content of grade II cost engineer certificate? Just look at this

短视频本地生活版块成为热门,如何把握新的风口机遇?

Blocking, non blocking, IO multiplexing select\poll\epoll

分享一个因子挖掘的利器:遗传规划

Congratulations to myself, official account has more than ten thousand fans

The number of small stores in Suning has dropped sharply by 428 in one year. Zhangkangyang, the son of Zhang Jindong, is the actual controller

LeetCode 88:合并两个有序数组
随机推荐
二级造价工程师考试还没完?还有资格审核规定!
Biovendor sRAGE protein solution
Opencv实现目标检测
Interview: what are the similarities and differences between abstract classes and interfaces?
Study on modified triphosphate: lumiprobe amino-11-ddutp
Unity out ref params
Learning Tai Chi Maker - mqtt Chapter II (VI) mqtt wills
Have you finished the examination of level II cost engineer? There are also qualification regulations!
羧酸研究:Lumiprobe 磺基花青7二羧酸
Notepad++ -- common plug-ins
2022年安全员-A证考试题库及模拟考试
How high is the gold content of grade II cost engineer certificate? Just look at this
几百行代码实现一个脚本解释器
Audio and video technology development weekly
IP datagram sending and forwarding process
Operation of simulated examination platform of G3 boiler water treatment recurrent training question bank in 2022
Severe tire damage: the first rock band in the world to broadcast live on the Internet
氨基染料研究:Lumiprobe FAM 胺,6-异构体
CpG solid support research: lumiprobe general CpG type II
DH parameters of robotics and derivation using MATLAB symbolic operation