gorm事务玩法

文章探讨了在数据库事务处理中常见的简单模式,并提出通过创建一个事务处理函数transHandler,以统一入口的方式来封装事务,包括commit和rollback操作。这种方式能解耦事务处理,使业务代码更专注于业务逻辑,同时保持代码风格一致,提高代码可读性和维护性。作者分享了一个实现示例,通过WithTransCommit函数执行业务并在发生错误时自动回滚,以此提高事务管理的规范性和效率。
摘要由CSDN通过智能技术生成

当前的很多都是简单粗暴,begin,然后业务,再commit, rollback

这只是其中一种最直接的玩法,相信大家都是八仙过海,能各显神通,比如通过defer做commit或者rollback,其实都没太大问题,都能基本保证功能完成,但是总感觉不太丝滑,

这里罗列几个问题:

  1. 比如直接简单粗暴的,就会在每个函数返回点要rollback或者commit,好一点的就加一个defer统一处理
  2. 千人千面,每个人用法都会不同,虽然可能没太大问题,但是代码迥异,风格各异,看着不舒服,维护起来也麻烦
  3. 每个人的用法处理起来可能都会有一点的缺失,考虑不周全的地方,但是大家又都各自瞧不上,哈哈哈

基于上述问题,是不是可以考虑统一一个入口来完成db事务,让我们写业务的时候基本无感知,解耦这些rollback和commit呢?

这里抛块砖,看看我个人认为写起来比较丝滑的方式:

 

 

这样做的好处有几点:

  1. 在打包事务的处理函数transHandler里,取它的db来用,遇到失败直接返回err就可以了,完全不感知事务
  2. 统一处理事务,有相当于做了个勾子,方便统一管理和规范处理,不再是各自写各自的事务了。
  3. 在transHandler函数里面可以肆意妄为,比如一段业务:先存一个记录到数据库,得到 Id 后要用它通知其他服务,然后再做下一个持久化,在transHandler就像没有事务的踪迹一样用

有了以上这几个优点,写起事务来让人感觉就很巴适,丝滑得很。后来我在我们的框架基础上做了个尝试实现,实现了,但是废了比较大劲,改动了一些地方,所以只能说作为一个参考,并对上述丝滑方式的做一个总结,可以有兴趣的同学可以根据这个启发在自己的框架上实现

// WithTransCommit 通过事务方式执行业务函数(通过ctx解耦),参数为函数func(ctx context.Context) error, error不为nil时回滚
func WithTransCommit(execFunc func(ctx context.Context) error) (err error) {
	transDB := WriteDBPaaS.Begin() // 这里使用db实例启动一个事务db,稍后会注入到需要执行的函数中作为参数
	defer func() {
		if errRecover := recover(); errRecover != nil {
			err = errors.New(fmt.Sprintf("panic导致事务失败: %v", errRecover))
			rollBackHandler(transDB)
		}
	}()
	// 通过ctx解耦,业务需要通过ctx获取事务db实例
	ctx := context.WithValue(context.Background(), PaasDB, transDB)
	// 这里执行业务函数
	err = execFunc(ctx)
	if err != nil {
		err = errors.Wrap(err, "任务执行失败")
		rollBackHandler(transDB)
		return
	}
	err = transDB.Commit().Error
	if err != nil {
		err = errors.Wrap(err, "提交失败")
		rollBackHandler(transDB)
		return
	}
	return
}  

// 使用
func demo(){
	err = cmn.WithTransCommit(func(ctx context.Context) (err error) {
			err = service.NewServiceRepoWithDB(ctx).Add(ser)
			if err != nil {
				return
			}
			var appclusters []iservice.AppCluster
			appclusters = append(appclusters, iservice.AppCluster{
				AppId:       ser.Id,
				ClusterId:   4,
				ClusterName: "default",
				GroupName:   "pm",
			})
			err = service.NewAppClusterRepoWithDB(ctx).AddList(appclusters)
			if err != nil {
				return
			}
			panic("模拟业务panic")
			return
	})
	// handle err
}

总结:

  1. 通过一个函数func(ctx/gorm.db)err ,作为一个handler,打包所有业务,作为一个处理器,处理器的参数是ctx(带有db的ctx,用ctx是为了解耦仓储层)
  2. 业务逻辑中,需要处理持久化时要用handler的ctx/gorm.db作为存储使用的db实例
  3. 实现一个事务执行函数

其实基于我们现有代码框架下,实现过程中还是遇到比较多问题,比如解耦,改变仓储层耦合db的使用方式(改为传参获取db)。。。 但总体来说我觉得是值得的,能让我们的代码看上去优雅简洁

实现的方式可以灵活多变,整体要解决的问题就上述罗列的问题,思路核心也就是一个handler打包事务相关的业务逻辑, 一个doTrans函数发起事务,执行事务的commit或rollback

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值