Golang 数据库事务实践
事务是很多业务的基础,本文介绍了如何在 Golang 里实现数据库事务操作,并以一个用户注册场景给出了完整实现。原文: Transactions in Go application
Go 是一种年轻而强大的语言,专为编写小型、简单的服务而创建。但随着时间推移,越来越多复杂应用和系统也在采用 Go 进行开发,这就出现了一些问题:如何处理事务?
为了深入探讨这个问题,我们假设一个简单的业务场景:用户注册
作为一个系统,我希望在注册时创建用户和个人资料
RDBMS/DBMS 的现代 Go 库不像 C# 和 Java 的 Hibernate、Entity Framework 那样强大,因此我们必须自己处理。为了实现用户注册业务场景,我们将创建并评估几种处理事务的方法。
由于每种事务处理方法都必须与 sql.DB 和 sql.Tx 配合使用,因此需要引入接口来封装对数据库的访问。
生成的应用有两个域实体和一个用于访问数据库的 DB 低级接口。
准备工作完成后,就可以采用如下两种方法。
1. 事务感知上下文
工作原理:transaction.Manager
启动事务并将其放入上下文。当存储库执行查询时,助手会检查上下文中是否有事务,并使用创建的事务来执行查询,或者如果上下文为空,则不使用事务来执行查询。
为了启动事务,我们需要实体:Manager
transaction.Manager
实现
DB 实现
RegistrationService
负责用户注册业务场景
User
和ProfileRepository
的实现
优点
简单:存储库会自动使用由 TransactionManager 启动的事务
与存储无关:客户端代码对存储类型一无所知
缺点
不符合 Go 的使用习惯
控制较少:无法防止在事务中启动事务,可能会产生意想不到的副作用,代码审查时必须考虑到这一点
2. 事务感知存储库
工作原理:事务管理器启动事务并将事务放入回调,存储库工厂方法使用事务创建自己。
为了启动事务,我们需要实体:Manager
transaction.Manager
实现
DB 实现
RegistrationService
负责用户注册业务场景
有两种方法可以创建带有事务的存储库:
存储库带有结构方法
WithTransaction
(示例中使用了该方法)存储库工厂
userRepositoryFactory.CreateFromTransaction(tx)
User
和ProfileRepository
的实现
优点
更明确:在注册服务内部创建事务,可避免副作用
缺点
客户端代码知道存储类型
客户端代码负责创建新的存储库
我相信任何一种方法都能使代码更易读、更简单,但建议使用第一种方法,从而可以隐藏存储细节,使我们能够在一个项目中使用多个存储,而无需考虑实现和存储细节。
你好,我是俞凡,在 Motorola 做过研发,现在在 Mavenir 做技术工作,对通信、网络、后端架构、云原生、DevOps、CICD、区块链、AI 等技术始终保持着浓厚的兴趣,平时喜欢阅读、思考,相信持续学习、终身成长,欢迎一起交流学习。为了方便大家以后能第一时间看到文章,请朋友们关注公众号"DeepNoMind",并设个星标吧,如果能一键三连(转发、点赞、在看),则能给我带来更多的支持和动力,激励我持续写下去,和大家共同成长进步!
版权声明: 本文为 InfoQ 作者【俞凡】的原创文章。
原文链接:【http://xie.infoq.cn/article/1be428695b6b44b24697af646】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论