当前位置:网站首页>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处理事务,出现错误是不会回滚的
边栏推荐
- lotus v1.16.0 calibnet
- Unity delegate
- sqlmap工具使用手册
- 2022 safety officer-b certificate examination question bank and answers
- 乔布斯在斯坦福大学的演讲稿——Follow your heart
- 2022年G3锅炉水处理复训题库模拟考试平台操作
- Extjs library management system source code intelligent library management system source code
- Metartc5.0 API programming guide (I)
- 2022年最新辽宁建筑八大员(标准员)考试试题及答案
- Unity delegate
猜你喜欢
How high is the gold content of grade II cost engineer certificate? Just look at this
别卷!如何高质量地复现一篇论文?
IP datagram sending and forwarding process
Dart学习——函数、类
wordpress zibll子比主题6.4.1开心版 免授权
羧酸研究:Lumiprobe 磺基花青7二羧酸
Distributed transaction - Final consistency scheme based on message compensation (local message table, message queue)
CI & CD must be known!
Meta universe standard forum established
Carboxylic acid study: lumiprobe sulfoacyanine 7 dicarboxylic acid
随机推荐
分享一个因子挖掘的利器:遗传规划
Light collector, Yunnan Baiyao!
活性染料研究:Lumiprobe AF594 NHS 酯,5-异构体
mysql----where 1=1是什么意思
【JVM系列】JVM调优
gsap的简单用法
How high is the gold content of grade II cost engineer certificate? Just look at this
Notepad++ -- common plug-ins
公司为什么选择云数据库?它的魅力到底是什么!
Learn Taiji Maker - mqtt Chapter 2 (IV) esp8266 reserved message application
开关电源电压型与电流型控制
二级造价工程师证书含金量到底有多高?看这些就知道了
短视频本地生活版块成为热门,如何把握新的风口机遇?
使用class toplevel的messagebox时,窗口弹出问题。
quartus 复制IP核
It is the latest weapon to cross the blockade. It is one of the fastest ladders.
IP datagram sending and forwarding process
Lumiprobe细胞成像分析:PKH26 细胞膜标记试剂盒
Audio and video technology development weekly
Binary sort tree: BST