当前位置: 首页 > news >正文

莱西网站建设哪家好十大免费域名

莱西网站建设哪家好,十大免费域名,cp wordpress,郑州富士康还招人吗现在在写代码过程中遇到了需要使用gorm执行sql事务的情况,研究了一下各位大佬的实现方案,结合了自身遇到的问题,特此记录。 代码架构介绍 . ├── apis ├── config ├── internal │ ├── constant │ ├── controller │ ├──…

在写代码过程中遇到了需要使用gorm执行sql事务的情况,研究了一下各位大佬的实现方案,结合了自身遇到的问题,特此记录。



代码架构介绍

.
├── apis
├── config
├── internal
│   ├── constant
│   ├── controller
│   ├── logic
│   ├── model
│   ├── repo
│   ├── router
│   └── service
├── library
├── pkg
├── scripts

目录结构其实很简单,主要参考了goframe的架构。internal/logic放逻辑代码,internal/repo是dao层,负责与数据库进行交互。



演变过程

最开始的时候是直接把对事务操作放在repo层中了。比如说有两张表,一张用户消费记录表,一张用户积分表,在用户消费完成后就要增加对应的积分。那么在repo层就会有一个函数负责开启事务,写消费记录表,写积分表,提交事务。在logic层中只需要调用这个函数就可以同时完成这两个操作。


看起来很美好,逻辑分的很清晰,但是这么做其实有个问题。如果logic层中有逻辑只操作消费记录表呢,或者用户消费积分只操作积分表呢,或者又有其他的表需要进行联合操作。这种写法会在repo层不断增加一个又一个函数,导致函数越来越多,因为承载了过多的业务逻辑。


发现这种情况之后,我们想可不可以把业务逻辑从repo层抽离,放在logic层进行处理,repo层只做简单的增删改查。那么repo层就可以只实现几个基础的函数,Create(),Update(),Selete()等,然后在logic层通过调用这几个函数实现复杂的逻辑操作。还是之前那个例子,比如说先消费再增加积分就可以变成对两个函数调用。

	// logic 层func buy() {tx := db.begin()cosume.Create(tx)point.Update(tx)tx.commit()}

分层是明确了,但是又引出了新的问题,就是如果我想使用事务,那么就需要在logic层对事务进行开启和提交,并且logic层还需要保存db的连接(用于开启事务),并且将tx传给repo层进行操作,这不合理。logic应该专注于代码逻辑的处理,而不是如何开启一个数据库事务。
目标很明确,如何让logic层只关注与逻辑的操作,而不是开启事务。上文也说到repo层只做一些简单的增删改查的操作,在repo里实现是不可能了,那么只能采取终极方案,在logic和repo之间增加一层中间层,专门用于开启事务。
这个中间层更像一个管理层,用于管理具体操作数据库的repo类和事务的操作,当需要开启事务时,logic只需要编写好业务逻辑,然后调用管理层的接口。管理层负责开启事务,调用logic层编写好的逻辑,提交/回滚事务即可。
这样logic层既不用处理事务相关的任务,repo层也不需要处理复杂的业务逻辑。



实现方案

总结一下上面所说,我们需要实现一个管理类,在管理类里面提供一个事务接口,这个接口的入参有一个func,这个func就是logic层编写好的各种逻辑,为了将tx传递给repo,我们是将其放在了ctx中。

管理层示例

Internal/repo/transaction.go

这个代码参考的时 gorm.Transaction(),做了简单修改。注释写的很详细了。

// Mysql Mysql数据库
type Mysql struct {db *gorm.DB
}func (m *Mysql) Transaction(ctx context.Context, fc func(txCtx context.Context) error) error {panicked := truevar err error// 拿到 ctx 里面的 db 连接,如果没有则使用默认的 db 连接db := m.getDB(ctx)// ...// 使用这个连接开启事务tx := db.WithContext(ctx).Begin()if tx.Error != nil {return tx.Error}defer func() {// Make sure to rollback when panic, Block error or Commit errorif panicked || err != nil {tx.Rollback()}}()// 将事务的 tx 写入 ctx 中,为了真正操作数据库的函数拿到 txtxCtx := generateContextWithDB(ctx, tx)// 调用 logic 层传入的 funcif err = fc(txCtx); err == nil {panicked = false// 如果没有报错就提交事务return tx.Commit().Error}panicked = false// 把事务内的报错转发出去return err
}

调用示例

/internal/logic/manager.go

func (m *manager) Buy(ctx context.Context) error {// 调用 Transaction 函数,txCtx 则为带有 tx 的 ctxif err := m.mysql.Transaction(ctx, func(txCtx context.Context) {// 这里只需要实现对应的业务即可,无需关注事务的开启和提交/回滚逻辑m.mysql.Consume(txCtx).Create()m.mysql.Point(txCtx).Update()}); err != nil {// do something}return nil
}

从 manager 这个函数的角度来看,整个事务执行的逻辑为,通过 Transaction 第一个参数 ctx 获取要执行事务的 db 连接,开启事务后将 tx 封到 context 中,然后传入 Transaction 第二个参数 func() 的入参中。通过将 txCtx 传入到不同的 repo 类中来实现各个 repo 使用同一个 tx 进行事务的操作。Transaction 函数保证了中间有任何错误都会进行回滚和无错误的提交操作。

Consume()和Point()做的是解析 txCtx 中的 db 连接,然后 new 一个repo 类返回。getDB() 负责返回 ctx 里的 db 接。

func (m *Mysql) Consume(ctx context.Context) ConsumeRepo {return NewConsumeRepo(m.getDB(ctx))
}

整体的代码逻辑就是这样,如果需要支持多个引擎,其实可以考虑抽出一个interface,各个引擎实现这个 interface 的接口即可。



gorm 一些写法

多表联查

我们的表设计的不是很规范,导致表和表之间基本上没有什么关联,外键也没有,所以没有办法使用gorm提供的preload,采取的是手动join的方式。

func (c *ConsumePoint) SelectForUpdate(ctx context.Context) (*model.ConsumeAndPoint, error) {// 接收数据的结构var record model.ConsumeAndPointif err := c.db.WithContext(ctx).// 主表Table("t_consume").// 返回什么Select("t_consume.*, t_point.f_point").// 连接方式Joins("join t_point on t_consume.f_user_id = t_point.f_user_id").// 只查询一条数据First(&record).Error; err != nil {// do something}return &record, nil
}

select for update

先读后更新的数据竞争且应该将加锁操作放到事务中,防止锁被自动释放,其实主要就在于 Set(“gorm:query_option”, “FOR UPDATE”) 这一行。如果采用上面多表联查的方式,设置了 FOR UPDATE 后两张表的内容都会有X锁,直到事务提交。可以用这个特性来搞分布式的竞争更新。

func UpdateUser(db *gorm.DB, id int64) error {tx := db.Begin()defer func() {if r := recover(); r != nil {tx.Rollback()}}()if err := tx.Error; err != nil {return err}user := User{}// 锁住指定 id 的 User 记录if err := tx.Set("gorm:query_option", "FOR UPDATE").First(&user, id).Error; err != nil {tx.Rollback()return err}// 更新操作...// commit事务,释放锁if err := tx.Commit().Error; err != nil {return err}return nil
}
http://www.zhongyajixie.com/news/7090.html

相关文章:

  • 怎么样做网站的目录结构百度关键词挖掘工具爱站网
  • 做网站的公司cnfg做app找什么公司
  • 做的网站里面显示乱码怎么解决方法软文范例大全800字
  • 做网站就是做app谷歌优化排名哪家强
  • 号码之家官网网站做优化一开始怎么做
  • 点点 网站建设今日的最新新闻
  • 哪个网站可以免费做初级试题推广学院seo教程
  • 个人博客网站怎么做台州关键词优化报价
  • 网站在什么地方设关键词app推广引流
  • 私人做的不错的网站谷歌 google
  • h5做的分销网站网络销售怎么聊客户
  • 网页视频下载网址seo网站诊断文档案例
  • 网站和虚拟服务器免费私人网站建设软件
  • 淘宝客做连接网站吗百度教育app
  • 企业oa系统哪个好seo关键词优化技巧
  • 自己建个网站需要多少钱网站怎么建立
  • 做网站首页cdr长沙网站制作公司哪家好
  • 大学网站建设与功能开发百度指数免费查询入口
  • 网站访客北京网络优化
  • 个人电子商务网站建设关键词优化seo优化排名
  • 产品如何推广济南seo网站优化公司
  • 团委网站开发企业网站系统
  • 用织梦做的网站seo优化一般包括哪些
  • 包装盒在线设计网站广州百度seo 网站推广
  • 做网站线上线下价格混乱免费直链平台
  • 开发网站实时监控关键词优化平台有哪些
  • 网站后台管理系统哪个好网站接广告平台
  • 推广普通话的演讲稿百度seo收录
  • 介绍自己做的电影网站西安全网优化
  • 东莞市国外网站建设平台百度识图网页版