今天是【7天从零实现TORM框架】的第六天,主要任务是:
- 介绍事务的ACID属性
- TORM支持事务
若对Go中反射的使用不了解的话,我写了三篇关于反射的文章,给小伙伴提供参考,足以应对本项目中所使用的反射知识点。
- go反射第一弹:https://mp.weixin.qq.com/s/F8yZyqC5UwoewsX0THqy1w
- go反射第二弹:https://mp.weixin.qq.com/s/lgZykTL8ls6aG0OMNSbZMw
- go反射第三弹:https://mp.weixin.qq.com/s/vFt06c9herwTrx1LTxNaKg
源代码:在【迈莫coding】中回复关键字「 torm 」获取github地址链接
后续会为【七天从零实现TORM框架】录制视频,文章+视频+代码
ACID属性
事务特性指的就是ACID,如图所示:
- 原子性 Atomicity :一个事务(transaction)中的所有操作,或者全部完成,或者全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被恢复(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。即,事务不可分割、不可约简。
- 一致性 Consistency :在事务开始和完成时,数据必须保持一致。这意味着所有相关的数据规则都必须应用于事务的修改,以保持数据的完整性;事务结束时,所有的内部数据结构(如B树索引或双向链表)也都必须是正确的。
- 隔离性 Isolation :数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
- 持久性 Durability :事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
具体事务介绍,请看我写的另一篇文章【mysql事务隔离机制及其原理】
https://mp.weixin.qq.com/s/CchC86sDw07iDgHcs-KXMw。
TORM支持事务
在TORM框架中实现事务,无非就是开启事务、提交事务、回滚事务这三大类,因此我们只需封装这三种方法即可。代码存储在根目录下 transaction.go 文件中。
// transaction.go
package session
import log "github.com/sirupsen/logrus"
// 开启事务
func (s *Session) Begin() (err error) {
log.Info("transaction begin")
if s.tx, err = s.db.Begin(); err != nil {
log.Error(err)
return
}
return
}
// 提交事务
func (s *Session) Commit() (err error) {
log.Info("transaction commit")
if err = s.tx.Commit(); err != nil {
log.Error(err)
}
return
}
// 回滚事务
func (s *Session) Rollback() (err error) {
log.Info("transaction rollback")
if err = s.tx.Rollback(); err != nil {
log.Error(err)
}
return
}
代码说明
- 第7~14行:事务开启
- 第17~23行:事务提交
- 第25~31行:事务回滚
事务操作API
接下来,实现给用户事务操作的API,代码存放在 client.go 文件中。
type TxFunc func(ctx context.Context, client *Client) (interface{}, error)
// 支持事务
func (c *Client) Transaction(f TxFunc) (result interface{}, err error) {
if err := c.session.Begin(); err != nil {
return nil, err
}
defer func() {
if p := recover(); p != nil {
_ = c.session.Rollback()
panic(p)
} else if err != nil {
_ = c.session.Rollback()
} else {
err = c.session.Commit()
}
}()
return f(context.Background(), c)
}
Transaction 的实现参考了 stackoverflow
用户只需要将所有的操作放到一个回调函数中,作为入参传递给 client.Transaction() ,发生任何错误,自动回滚,如果没有错误发生,则提交。
测试
func TestClient_Transaction(t *testing.T) {
user := &Users{
Name: "迈莫",
Age: 1,
}
client, _ := Newclient()
statement := NewStatement()
statement = statement.SetTableName("user").
AndEqual("age", 21).Select("user_name,age")
res, err := client.Transaction(func(ctx context.Context, client2 *Client) (interface{}, error) {
err := client2.FindOne(ctx, statement, user)
return user, err
})
if err != nil {
log.Error(err)
return
}
log.Info(res)
}
代码说明
=== RUN TestClient_Transaction
time="2021-01-16T22:19:39+08:00" level=info msg="Connect database success"
time="2021-01-16T22:19:39+08:00" level=info msg="transaction begin"
time="2021-01-16T22:19:39+08:00" level=info msg="select user_name,age from user WHERE `age`=? [21]"
time="2021-01-16T22:19:39+08:00" level=info msg="transaction commit"
time="2021-01-16T22:19:39+08:00" level=info msg="&{迈莫 21}"
--- PASS: TestClient_Transaction (0.00s)
PASS
代码目录
torm
|--raw.go // 底层与数据库交互语句
|--raw_test.go
|--schema.go // 对象表结构映射
|--schema_test.go
|--generators.go // 关键词sql语句
|--clause.go // 条件组件库
|--clause_test.go
|--statement.go // 条件组件库操作API
|--statement_test.go
|--client.go // 用户CRUD操作API
|--client_test.go
|--transaction.go // 支持事务
|--go.mod
今天的任务完成了,回顾一下,一方面大概讲述事务的ACID属性,另一方面在TORM框架中,进行支持事务操作。
到这里,【七天从零实现TORM框架】到一段落了,也非常感觉小伙伴们可以和我一起坚持到现在,你们的鼓励就是我创建的动力,后期的话我也会录制视频,关注【迈莫coding】获取视频的最新进度,也欢迎小伙伴加入我的【family】。
文章也会持续更新,可以微信搜索「 迈莫coding 」第一时间阅读,回复『1024』领取学习go资料。