V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
longmeier90
V2EX  ›  Go 编程语言

各位大佬在 go 中都是怎么使用事务?有没有更好的方式

  •  
  •   longmeier90 · 2022-02-25 11:07:06 +08:00 · 7089 次点击
    这是一个创建于 999 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近在写 go 项目中发现一个问题,我有一个对外提供的支付接口,接口中又去调了很多封装好的更改数据库的操作函数。然后我发现要是想对整个接口用一个事务的话,需要在接口最开始初始一个*db 连接,然后传给各个函数。这样才能实现一个事务接口。

    tx, _ := dao.MysqlDB.Begin()  // 初始化 db 连接
    defer tx.Rollback()
    logic.UpdateProjectDiscountMemo(*tx,proDisMemoList)  // 更新项目折扣
    logicCreateUserEventsRecord(*tx,groupId)       // 更新用户记录
    tx.Commit()
    
    41 条回复    2023-06-11 16:22:44 +08:00
    cheng6563
        2
    cheng6563  
       2022-02-25 11:20:10 +08:00
    光接口最开始初始一个*db 连接还不一定行
    比如你各个函数里面又进行了 begin 和 commit ,形成事务嵌套,这还要看你事务管理器有没有实现,没实现的话还得自己再包一层逻辑。
    imherer
        3
    imherer  
       2022-02-25 11:24:46 +08:00
    放在中间件里 commit 吧
    godlovesxcjtest
        4
    godlovesxcjtest  
       2022-02-25 12:50:41 +08:00   ❤️ 1
    可以考虑把 db 放到 context 里面,context 一直向下传递,logic 层获取 db 只需要从 context 获取就好。
    如果上层的 logic 想开启事务,就把 db=db.Begin()放到 context 里面,这下下层的 logic 获取到的就是开启事务的 db 了,然后上层根据返回的 error 进行 commit 或者 rollback
    qieqie
        5
    qieqie  
       2022-02-25 12:56:40 +08:00
    封装一个 session 或者叫 context ,receriver 中实现各种数据库逻辑,最后返回自身实现链式调用。
    labulaka521
        6
    labulaka521  
       2022-02-25 13:25:20 +08:00
    db.Transaction(func(tx *gorm.DB) error {
    orderHelp := orderhelp{tx: tx}
    orderHelp 实现业务逻辑
    exampleHelp := examplehelp{tx:tx}
    })
    nekoneko
        7
    nekoneko  
       2022-02-25 15:40:44 +08:00
    go 用事务这么麻烦吗, 没现成框架吗....
    zoharSoul
        8
    zoharSoul  
       2022-02-25 15:48:11 +08:00
    @nekoneko #7 大道至简
    9c04C5dO01Sw5DNL
        9
    9c04C5dO01Sw5DNL  
       2022-02-25 15:54:23 +08:00
    惊讶,同 7 楼所问。。。
    BeautifulSoap
        10
    BeautifulSoap  
       2022-02-25 16:17:22 +08:00 via Android
    把事务塞进 context 传下去,反正 context 是 go 标准功能了,下面的方法要事务的话就从 context 里拿

    go 就是这样的没办法
    tiedan
        11
    tiedan  
       2022-02-25 16:46:58 +08:00
    自己包一下
    28Sv0ngQfIE7Yloe
        12
    28Sv0ngQfIE7Yloe  
       2022-02-25 16:56:51 +08:00
    @nekoneko #7
    @giiiiiithub #9

    java 转过来的 ,写了一段时间 crud 痛不欲生,一吐槽就说是人的问题,和语言无关
    Qseven
        13
    Qseven  
       2022-02-25 17:00:46 +08:00
    一个字,大道至简。
    Hanggi
        14
    Hanggi  
       2022-02-25 17:01:59 +08:00   ❤️ 1
    @longmeier90
    @nekoneko
    @giiiiiithub
    @Morii
    https://gorm.io/zh_CN/docs/transactions.html
    https://entgo.io/zh/docs/transactions/

    想怎么写怎么写,还特意帮你们点好了中文,慢慢享用。

    还有 Go 本身就是一种拆开写的语言,用惯了语法糖的确实容易不习惯,关键能否接受这种思想吧。
    9c04C5dO01Sw5DNL
        15
    9c04C5dO01Sw5DNL  
       2022-02-25 17:22:01 +08:00
    @Hanggi 手动控制事务,好原始
    9c04C5dO01Sw5DNL
        16
    9c04C5dO01Sw5DNL  
       2022-02-25 17:22:45 +08:00
    @Morii 看到 14 楼贴的方案,能体会到你说的痛不欲生。。。。
    28Sv0ngQfIE7Yloe
        17
    28Sv0ngQfIE7Yloe  
       2022-02-25 17:40:54 +08:00
    @giiiiiithub #16

    只能说大道至简,另外吐槽下,go 木有 set ,github 有一百多个 set 实现
    evan0724
        18
    evan0724  
       2022-02-25 17:44:44 +08:00   ❤️ 1
    我是这么写的
    type Store struct {
    db *gorm.DB
    tx *gorm.DB
    }

    func (s *Store) Begin() {

    }
    evan0724
        19
    evan0724  
       2022-02-25 17:48:08 +08:00
    没编辑完就回复了。。
    func (s *Store) Begin() {
    s.tx = s.db.Begin()
    }
    // Commit 或 Rollback
    func (s *Store) Commit() {
    s. tx.Commit()
    s.tx = s.db
    }
    Hanggi
        20
    Hanggi  
       2022-02-25 18:16:23 +08:00 via Android
    @giiiiiithub Go 语言要求你处理每个阶段产生的错误,所以不存在异常。对于大的团队项目来说只要你不瞎搞一般开发者也能写出比较高质量的代码。看你对自身的要求了。
    9c04C5dO01Sw5DNL
        21
    9c04C5dO01Sw5DNL  
       2022-02-25 18:34:10 +08:00
    @Hanggi 你看,我还没说 if err 的问题你自己就先忙着解释了。。。。我这说的是手动控制事务的事呢
    joesonw
        22
    joesonw  
       2022-02-25 18:46:31 +08:00
    都是自己套的. Database 封装一层,

    一般是
    func BeginReadWrite(ctx context.Context, f func(ctx context.Context) error) error
    去调用 f 的时候, ctx 里会带上 transaction 对象, 然后在这里处理错误, 和 defer 里面抓错误, 来 commit 或 rollback

    然后 Repository 层被调用的时候, 从 context 里取出来.

    transaction 一般最好是 interface, 这样方便有时候不需要事务, 传入裸的连接直接用, 也不用改里面的代码.
    Hanggi
        23
    Hanggi  
       2022-02-25 18:54:53 +08:00 via Android   ❤️ 1
    @giiiiiithub 又来一个小丑。国内现状的代表
    9c04C5dO01Sw5DNL
        24
    9c04C5dO01Sw5DNL  
       2022-02-25 18:56:59 +08:00   ❤️ 4
    @Hanggi 这就飙脏字了?这素质能代表 go 开发者集体么?不能就咽回去(*^_^*)
    FrankAdler
        25
    FrankAdler  
       2022-02-25 19:18:42 +08:00
    @Hanggi #14 gorm 那种写法,如果逻辑都在一起还好说,但是对应楼主说那种,调用链比较长,每个环节都可能有事务就麻烦了,需要共用一个初始的事务实例,类似 Context 那种一路传下去还挺难受的。
    wunonglin
        26
    wunonglin  
       2022-02-25 20:39:44 +08:00
    @nekoneko #7 mongo 官方的驱动比 mysql 的好用太多了。一个天一个地
    Hanggi
        27
    Hanggi  
       2022-02-25 20:54:12 +08:00 via iPhone
    @FrankAdler 首先事务的范围还是尽量控制在小范围,非要长调用链,可以试试上面 gorm 里的其他写法。
    joesonw
        28
    joesonw  
       2022-02-26 14:54:18 +08:00 via iPhone
    gorm 怎么说呢,不一定全部盲目采信它的模式。它也是 v2 才支持传递 context 的,v1 的时候做链路追踪都只能挂到 Scope 上。
    28Sv0ngQfIE7Yloe
        29
    28Sv0ngQfIE7Yloe  
       2022-02-28 10:13:14 +08:00
    @giiiiiithub #24

    看到了吗,国内 go 圈子的现状,不接受质疑。质疑的话你就是「🤡」

    关键大多数都是 crud boy ,对于我这种渣渣,go 确实生产力不够强大。
    9c04C5dO01Sw5DNL
        30
    9c04C5dO01Sw5DNL  
       2022-02-28 11:17:59 +08:00
    拿着步枪当宝贝。你看他那样,上来就先给自己裹小脚“控制范围”,这用得着他说嘛,没有就是没有,原始就是原始,麻烦就是麻烦,跟控制范围有鸡毛关系。还什么 if err 能产出较高质量代码,头一回见这么能扯的。

    啥是宗教?这玩意就是典型的宗教信徒。
    9c04C5dO01Sw5DNL
        31
    9c04C5dO01Sw5DNL  
       2022-02-28 11:18:58 +08:00
    @Morii 忘了圈你,在楼上。
    zzy11
        32
    zzy11  
       2022-02-28 17:00:16 +08:00   ❤️ 1
    dany813
        33
    dany813  
       2022-02-28 17:31:06 +08:00
    大道至简 好牛逼
    whyso
        34
    whyso  
       2022-03-01 10:14:23 +08:00
    @Morii 所以为啥 java 转 go ,图个啥呢?
    28Sv0ngQfIE7Yloe
        35
    28Sv0ngQfIE7Yloe  
       2022-03-01 13:39:25 +08:00
    @whyso #34

    换组了,就换技术栈了
    lessMonologue
        36
    lessMonologue  
       2022-03-01 14:47:58 +08:00
    @Morii 我也是从 Java 转到 go 的,我发现接手的项目竟然没有事务控制!每一步更新表都能从表里读出来。。。。
    28Sv0ngQfIE7Yloe
        37
    28Sv0ngQfIE7Yloe  
       2022-03-01 15:57:36 +08:00
    @lessMonologue #36

    这个就是设计的问题了,和语言无关哈哈,这里讨论的是 go 主流的 orm 事务太难用了哈哈
    aurtech
        38
    aurtech  
       2022-03-10 17:52:15 +08:00
    在深圳,求一枚 Golang 大佬!!欢迎砸简历 V:Ifboredgunquxuexi.
    xsen
        39
    xsen  
       2022-03-10 20:33:21 +08:00
    确实是与语言无关,只是现成已有的 orm 没有封装好;当然,最大问题还是 go 相对来说比较新的语言,成熟的轮子少,而且主要场景是基础设施

    毕竟传统的企业应用,Java 都有现成的;没必要为了用新 语言就全部重写
    而且现在微服务(包括服务网格这类的),都支持 sidecar ;所以一个系统多语言架构也越来越多常见
    aurtech
        40
    aurtech  
       2022-03-11 15:23:11 +08:00
    坐标深圳,求一枚 Golang 大佬!!欢迎砸简历 V:Ifboredgunquxuexi.
    jarome
        41
    jarome  
       2023-06-11 16:22:44 +08:00
    @zzy11 不错的文章
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2989 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 14:43 · PVG 22:43 · LAX 06:43 · JFK 09:43
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.