当前位置:网站首页>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处理事务,出现错误是不会回滚的
边栏推荐
- IP datagram sending and forwarding process
- 几百行代码实现一个脚本解释器
- 学习太极创客 — MQTT 第二章(五)心跳机制
- mysql----where 1=1是什么意思
- [early knowledge of activities] list of recent activities of livevideostack
- Simulation questions and answers of the latest national fire-fighting facility operators (primary fire-fighting facility operators) in 2022
- DH parameters of robotics and derivation using MATLAB symbolic operation
- 基于订单流工具,我们能看到什么?
- 电源插座是如何传输电的?困扰小伙伴这么多年的简单问题
- Severe tire damage: the first rock band in the world to broadcast live on the Internet
猜你喜欢

IP数据报的发送和转发过程

大促场景下,如何做好网关高可用防护

Learning Tai Chi Maker - mqtt Chapter II (VI) mqtt wills

Feign remote call fallback callback failed, no effect

!‘ Cat 'is not an internal or external command, nor is it a runnable program or batch file.

学习太极创客 — MQTT 第二章(六)MQTT 遗嘱

CpG solid support research: lumiprobe general CpG type II
![[noip2002 popularization group] cross the river pawn](/img/6c/31fa210e08c7fd07691a1c5320154e.png)
[noip2002 popularization group] cross the river pawn

羧酸研究:Lumiprobe 磺基花青7二羧酸

How to do a good job of gateway high availability protection in the big promotion scenario
随机推荐
JS text box loses focus to modify width text and symbols
C语言中函数是什么?编程中的函数与数学中的函数区别?理解编程语言中的函数
Dart learning - functions, classes
基于订单流工具,我们能看到什么?
项目经理考完PMP就够了?不是的!
2022 low voltage electrician examination questions and answers
Standard particle swarm optimization C language program
【微服务|OpenFeign】OpenFeign快速入门|基于Feign的服务调用
通过例子学习Rust
2022年安全员-A证考试题库及模拟考试
PMP考试成绩多久出来?这些你务必知道!
【SkyWalking】一口气学完分布式链路追踪SkyWalking
如何从零设计一款牛逼的高并发架构(建议收藏)
msa.h:没有那个文件或目录
Object detection with OpenCV
BioVendor sRAGE Elisa试剂盒化学性质和技术研究
氨基染料研究:Lumiprobe FAM 胺,6-异构体
并发之wait/notify说明
Have you finished the examination of level II cost engineer? There are also qualification regulations!
Reactive dye research: lumiprobe af594 NHS ester, 5-isomer