Go语言微服务框架 - 12.ORM层的自动抽象与自定义方法的扩展

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 随着接口参数校验功能的完善,我们能快速定位到接口层面的参数问题;而应用服务的分层代码,也可以通过log的trace-id发现常见的业务逻辑问题。但在最底层与数据库的操作,也就是对GORM的使用,经常会因为我们不了解ORM的一些细节,导致对数据的CRUD失败,或者没有达到预期效果。这时,我们希望能在ORM这一层也有一个通用的解决方案,来加速问题的排查。

随着接口参数校验功能的完善,我们能快速定位到接口层面的参数问题;而应用服务的分层代码,也可以通过log的trace-id发现常见的业务逻辑问题。

但在最底层与数据库的操作,也就是对GORM的使用,经常会因为我们不了解ORM的一些细节,导致对数据的CRUD失败,或者没有达到预期效果。这时,我们希望能在ORM这一层也有一个通用的解决方案,来加速问题的排查。

趁这个机会,我们也对gormer这个工具再做一次迭代,添加新的功能。

v0.7.2:ORM层的自动抽象与自定义方法的扩展

项目链接 https://github.com/Junedayday/micro_web_service/tree/v0.7.2

目标

gormer工具支持interface的抽象与自定义方法的扩展,并具备日志打印功能。

关键技术点

  1. model层的自动抽象方案
  2. dao层的代码实现
  3. MySQL的SQL打印
  4. 关于gormer工具的迭代

目录构造

--- micro_web_service            项目目录
    |-- gen                            从idl文件夹中生成的文件,不可手动修改
       |-- idl                             对应idl文件夹
          |-- demo                             对应idl/demo服务,包括基础结构、HTTP接口、gRPC接口
            |-- order                            对应idl/order服务,同上
     |-- swagger.json                    openapiv2的接口文档
    |-- idl                            原始的idl定义
       |-- demo                            业务package定义,protobuffer的原始定义
       |-- order                           业务order定义,同时干
    |-- internal                       项目的内部代码,不对外暴露
       |-- config                          配置相关的文件夹,viper的相关加载逻辑
       |-- dao                             Data Access Object层,是model层的实现
       |-- gormer                          从pkg/gormer中生成的相关代码,不允许更改
       |-- model                           修改:model层基本定义由gormer自动生成
       |-- mysql                           修改:MySQL连接,支持日志打印
       |-- server                          服务器的实现,对idl中定义服务的具体实现
       |-- service                         service层,作为领域实现的核心部分
     |-- zlog                            封装zap日志的代码实现
  |-- pkg                            开放给第三方的工具库
     |-- gormer                          gormer二进制工具,用于生成Gorm相关Dao层代码
    |-- buf.gen.yaml                   buf生成代码的定义,新增参数校验逻辑
    |-- buf.yaml                       buf工具安装所需的工具,从v1beta升到v1
    |-- gen.sh                         生成代码的脚本:buf+gormer
    |-- go.mod                         Go Module文件
    |-- gormer.yaml                    将gormer中的参数移动到这里
    |-- main.go                        项目启动的main函数
    |-- swagger.sh                     生成openapiv2的相关脚本

1.model层的自动抽象方案

之前,我们在dao层已经实现了基本的CRUD相关代码,所以实现一个model层的定义很简单。但考虑到扩展性,也就是这个model层不仅仅需要简单的CRUD代码,还可能需要一些类似于group by等复杂sql,甚至包括子查询。

这时候,如果考虑全部用gormer工具自动生成的方案,那成本会很高,所以更建议分开维护的方案:简单的CRUD用自动代码生成的方式,而复杂SQL调用GORM库自行实现。我们来阅读代码:

// *.go 自动生成的代码,标准方法
type OrderModel interface {
   
    AddOrder(ctx context.Context, order *gormer.Order) (err error)
    QueryOrders(ctx context.Context, pageNumber, pageSize int, condition *gormer.OrderOptions) (orders []gormer.Order, err error)
    CountOrders(ctx context.Context, condition *gormer.OrderOptions) (count int64, err error)
    UpdateOrder(ctx context.Context, updated, condition *gormer.OrderOptions) (err error)
    DeleteOrder(ctx context.Context, condition *gormer.OrderOptions) (err error)

    // Implement Your Method in ext model
    OrderExtModel
}

// *_ext.go 扩展方法
type OrderExtModel interface {
   
}

为了保证自定义的ext代码不被覆盖,在gormer的代码里添加如下代码:

// 如果extFile已经存在,则不要覆盖
if _, err = os.Stat(extFile); err != nil {
   
  // 创建ext文件的代码
}

2.dao层的代码实现

dao层的代码基本同model层,分为*.go*_ext.go两个。

为了保证dao层实现了model层的代码,我们也增加了一行代码,方便我们在编译期保证实现。

var _ model.OrderModel = NewOrderRepo(nil)

3.MySQL的SQL打印

在GORM工具中,提供了一个callback的方式,让用户添加自定义的插件。具体可以参考 https://gorm.io/zh_CN/docs/write_plugins.html。主要实现分下面两步:

// 1 - 操作SQL时,将ctx传入其中,用来传递一些通用参数,如traceid
func (repo *OrderRepo) AddOrder(ctx context.Context, order *gormer.Order) (err error) {
   
    repo.db.WithContext(ctx).
        Table(gormer.OrderTableName).
        Create(order)
    err = repo.db.Error
    return
}

// 2 - 在操作数据库后,注册对应的插件afterLog,用来打印SQL日志
func InitGorm(user, password, addr string, dbname string) (err error) {
   
    dsn := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
        user, password, addr, dbname)
    GormDB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
   })

    // 结束后
    _ = GormDB.Callback().Create().After("gorm:after_create").Register(callBackLogName, afterLog)
    _ = GormDB.Callback().Query().After("gorm:after_query").Register(callBackLogName, afterLog)
    _ = GormDB.Callback().Delete().After("gorm:after_delete").Register(callBackLogName, afterLog)
    _ = GormDB.Callback().Update().After("gorm:after_update").Register(callBackLogName, afterLog)
    _ = GormDB.Callback().Row().After("gorm:row").Register(callBackLogName, afterLog)
    _ = GormDB.Callback().Raw().After("gorm:raw").Register(callBackLogName, afterLog)
    return
}

const callBackLogName = "zlog"

func afterLog(db *gorm.DB) {
   
    err := db.Error
    ctx := db.Statement.Context

    sql := db.Dialector.Explain(db.Statement.SQL.String(), db.Statement.Vars...)
    if err != nil {
   
        zlog.WithTrace(ctx).Errorf("sql=%s || error=%v", sql, err)
        return
    }
    zlog.WithTrace(ctx).Infof("sql=%s", sql)
}

afterLog这里,我们引用了插件,实现了自定义日志组件的打印。

4.关于gormer工具的迭代

在这个小版本中,我们又对gormer工具做了一次迭代。从整个框架的维度来看,我们不仅仅是把它作为一种代码生成的工具,而是一种模块化的抽象能力,关注分层能力的建设。从SQL的log打印来看,我们可以区分出前后的差异:

原先 - 通过调用一个公共函数来打印,需要侵入到每个dao层的具体代码

修改后 - 通过插件注册到组件中,无需侵入到具体实现的代码

无侵入地实现自定义功能,这个特性对每个工具组件都非常重要,GORM这里就提供了一个很好的实现思路 - 注册插件,自定义hook。

总结

本次迭代的意义很大 - 标志着gormer这个组件实现了自定义方法的可扩展(ext文件)。

接下来,我们还会持续地对gormer等low code工具持续优化,实现更多的功能。

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。   相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情: https://www.aliyun.com/product/rds/mysql 
目录
相关文章
|
1月前
|
监控 算法 NoSQL
Go 微服务限流与熔断最佳实践:滑动窗口、令牌桶与自适应阈值
🌟蒋星熠Jaxonic:Go微服务限流熔断实践者。分享基于滑动窗口、令牌桶与自适应阈值的智能防护体系,助力高并发系统稳定运行。
Go 微服务限流与熔断最佳实践:滑动窗口、令牌桶与自适应阈值
|
4月前
|
人工智能 测试技术 Go
Go 语言的主流框架
本文全面解析了 Go 语言主流技术生态,涵盖 Web 框架、微服务、数据库工具、测试与部署等多个领域。重点介绍了 Gin、Echo、Beego 等高性能框架,以及 gRPC-Go、Go-Micro 等微服务组件。同时分析了 GORM、Ent 等 ORM 工具与测试部署方案,并结合场景提供选型建议,助力开发者构建高效稳定的 Go 应用。
1147 0
|
2月前
|
消息中间件 缓存 NoSQL
Redis各类数据结构详细介绍及其在Go语言Gin框架下实践应用
这只是利用Go语言和Gin框架与Redis交互最基础部分展示;根据具体业务需求可能需要更复杂查询、事务处理或订阅发布功能实现更多高级特性应用场景。
275 86
|
1月前
|
JavaScript 前端开发 Java
【GoWails】Go做桌面应用开发?本篇文章带你上手Wails框架!一步步带你玩明白前后端双端的数据绑定!
wails是一个可以让你使用Go和Web技术编写桌面应用的项目 可以将它看作Go的快并且轻量级的Electron替代品。可以使用Go的功能,并结合现代化UI完成桌面应用程序的开发
393 5
|
1月前
|
开发框架 前端开发 Go
【GoGin】(0)基于Go的WEB开发框架,GO Gin是什么?怎么启动?本文给你答案
Gin:Go语言编写的Web框架,以更好的性能实现类似Martini框架的APInet/http、Beego:开源的高性能Go语言Web框架、Iris:最快的Go语言Web框架,完备的MVC支持。
340 1
|
5月前
|
开发框架 JSON 中间件
Go语言Web开发框架实践:路由、中间件、参数校验
Gin框架以其极简风格、强大路由管理、灵活中间件机制及参数绑定校验系统著称。本文详解其核心功能:1) 路由管理,支持分组与路径参数;2) 中间件机制,实现全局与局部控制;3) 参数绑定,涵盖多种来源;4) 结构体绑定与字段校验,确保数据合法性;5) 自定义校验器扩展功能;6) 统一错误处理提升用户体验。Gin以清晰模块化、流程可控及自动化校验等优势,成为开发者的优选工具。
|
5月前
|
开发框架 安全 前端开发
Go Web开发框架实践:模板渲染与静态资源服务
Gin 是一个功能强大的 Go Web 框架,不仅适用于构建 API 服务,还支持 HTML 模板渲染和静态资源托管。它可以帮助开发者快速搭建中小型网站,并提供灵活的模板语法、自定义函数、静态文件映射等功能,同时兼容 Go 的 html/template 引擎,具备高效且安全的页面渲染能力。
|
5月前
|
缓存 负载均衡 监控
微服务架构下的电商API接口设计:策略、方法与实战案例
本文探讨了微服务架构下的电商API接口设计,旨在打造高效、灵活与可扩展的电商系统。通过服务拆分(如商品、订单、支付等模块)和标准化设计(RESTful或GraphQL风格),确保接口一致性与易用性。同时,采用缓存策略、负载均衡及限流技术优化性能,并借助Prometheus等工具实现监控与日志管理。微服务架构的优势在于支持敏捷开发、高并发处理和独立部署,满足电商业务快速迭代需求。未来,电商API设计将向智能化与安全化方向发展。
397 102
|
设计模式 Java API
微服务架构演变与架构设计深度解析
【11月更文挑战第14天】在当今的IT行业中,微服务架构已经成为构建大型、复杂系统的重要范式。本文将从微服务架构的背景、业务场景、功能点、底层原理、实战、设计模式等多个方面进行深度解析,并结合京东电商的案例,探讨微服务架构在实际应用中的实施与效果。
655 6
|
设计模式 Java API
微服务架构演变与架构设计深度解析
【11月更文挑战第14天】在当今的IT行业中,微服务架构已经成为构建大型、复杂系统的重要范式。本文将从微服务架构的背景、业务场景、功能点、底层原理、实战、设计模式等多个方面进行深度解析,并结合京东电商的案例,探讨微服务架构在实际应用中的实施与效果。
306 1

热门文章

最新文章