Go语言微服务框架 - 4.初识GORM库

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介: GORM库是一个很强大、但同时也是一个非常复杂的工具。为了支持复杂的SQL语言,它比之前的配置文件加载工具github.com/spf13/viper要复杂不少。今天,我们不会全量地引入GORM里的所有特性,而是从一个最简单的场景入手,对它的基本特性有所了解。而后续随着框架的完善,我们会逐渐细化功能。

数据持久化是服务的必要特性,最常见的组件就是关系型数据库MySQL。而在Go语言里,GORM已经成了对接MySQL事实上的标准,那么也就不去横向对比其它库了。

GORM库是一个很强大、但同时也是一个非常复杂的工具。为了支持复杂的SQL语言,它比之前的配置文件加载工具github.com/spf13/viper要复杂不少。今天,我们不会全量地引入GORM里的所有特性,而是从一个最简单的场景入手,对它的基本特性有所了解。而后续随着框架的完善,我们会逐渐细化功能。

v0.4.0:引入GORM库

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

目标

利用GORM实现简单的增删改查功能。

关键技术点

  1. MySQL工具库的必要功能
  2. GORM官方示例分析
  3. 使用GORM的思考

目录构造

--- micro_web_service            项目目录
    |-- gen                            从idl文件夹中生成的文件,不可手动修改
       |-- idl                             对应idl文件夹
          |-- demo                             对应idl/demo服务
             |-- demo.pb.go                        demo.proto的基础结构
             |-- demo.pb.gw.go                     demo.proto的HTTP接口,对应gRPC-Gateway
             |-- demo_grpc.pb.go                   demo.proto的gRPC接口代码
    |-- idl                            原始的idl定义
       |-- demo                            业务package定义
          |-- demo.proto                       protobuffer的原始定义
    |-- internal                       项目的内部代码,不对外暴露
       |-- config                          配置相关的文件夹
          |-- viper.go                         viper的相关加载逻辑
       |-- dao                             新增:Data Access Object层
          |-- order.go                         新增:示例的一个Order对象,订单表
       |-- mysql                           新增:MySQL连接
          |-- init.go                          新增:初始化连接到MySQL的工作
       |-- server                          服务器的实现
          |-- demo.go                          server中对demo这个服务的接口实现
          |-- server.go                        server的定义,须实现对应服务的方法
     |-- zlog                            封装日志的文件夹
        |-- zap.go                           zap封装的代码实现
    |-- buf.gen.yaml                   buf生成代码的定义
    |-- buf.yaml                       buf工具安装所需的工具
    |-- gen.sh                         buf生成的shell脚本
    |-- go.mod                         Go Module文件
    |-- main.go                        项目启动的main函数

1.MySQL工具库的必要功能

对于MySQL数据库来说,我们对它的日常操作其实就关注在CRUD上(也就是增删改查)。

除此以外,还有一些是我们需要关注的点:

  • 便捷性:能快速、方便地实现实现功能,而不用写大量重复性的SQL
  • 透明性:ORM经过层层封装,最终与MySQL交互的SQL语句可供排查问题
  • 扩展性:支持原生的SQL,在复杂场景下的ORM框架不如原始的SQL好用

这里,我们先聚焦于第一点,后面两块GORM框架是支持的。

2.GORM官方示例分析

接下来,我们对照着官方文档,来看看有什么样的注意点。

创建

中文文档链接 - https://gorm.io/zh_CN/docs/create.html

// 推荐使用方式:定义一个结构体,填充字段
user := User{
   Name: "Jinzhu", Age: 18, Birthday: time.Now()}
result := db.Create(&user)

// 不推荐:指定要创建的字段名,也就是user中部分生效,很容易产生迷惑
// 更建议新建一个user结构体进行创建
db.Select("Name", "Age", "CreatedAt").Create(&user)

// 批量创建同推荐
var users = []User{
   {
   Name: "jinzhu1"}, {
   Name: "jinzhu2"}, {
   Name: "jinzhu3"}}
db.Create(&users)

// 不推荐:钩子相关的特性,类似于数据库里的trigger,隐蔽而迷惑,不易维护
func (u *User) BeforeCreate(tx *gorm.DB) (err error){
   }

// 不推荐:用Map硬编码创建记录,改动成本大
db.Model(&User{
   }).Create(map[string]interface{
   }{
   
  "Name": "jinzhu", "Age": 18,
})

// 争议点:gorm.Model中预定了数据库中的四个字段,是否应该把它引入到模型的定义中
// 我个人不太喜欢将这四个字段强定义为数据库表中的字段名
type Model struct {
   
    ID        uint `gorm:"primarykey"`
    CreatedAt time.Time
    UpdatedAt time.Time
    DeletedAt DeletedAt `gorm:"index"`
}

从上面的一些操作可以看到,我推荐的使用方式有2个特点:

  1. 尽可能简单,不要出现魔法变量,比如常量字符串
  2. 不要让框架强约束表结构的设计,也是为了后续迁移框架、甚至语言时成本更低

查询

中文文档链接 - https://gorm.io/zh_CN/docs/query.html

// 不推荐: 我个人不太建议使用First/Last这种和原生SQL定义不一致的语法,扩展性也不好
// 在这种情况下,我更建议采用Find+Order+Limit这样的组合方式,通用性也更强
db.First(&user)
db.Last(&user)

// 推荐:Find支持返回多个记录,是最常用的方法,但需要结合一定的限制
result := db.Find(&users)

// 不推荐:条件查询的字段名采用hard code,体验不好
db.Where("name = ?", "jinzhu").First(&user)
db.Where(map[string]interface{
   }{
   "name": "jinzhu", "age": 20}).Find(&users)

// 推荐:结合结构体的方式定义,体验会好很多
// 但是,上面这种方法不支持结构体中Field为默认值的情况,如0,'',false等
// 所以,更推荐采用下面这种方式,虽然会带来一定的hard code,但能指定要查询的结构体名称。
db.Where(&User{
   Name: "jinzhu", Age: 20}).First(&user)
db.Where(&User{
   Name: "jinzhu"}, "name", "Age").Find(&users)

// 推荐:指定排序
db.Order("age desc, name").Find(&users)

// 推荐:限制查询范围,结合Find
db.Limit(10).Offset(5).Find(&users)

查询还有很多的特性,今天我们暂不细讲。其中,希望大家能重点看一下默认值问题:

我们固然可以通过在定义字段时,排除这些默认值的情况,如定义int类型字段时跳过0、从1开始。但在实际的项目中,定义往往很难控制,我们不得不做一定的妥协,这部分hard code的成本也是可以接受的。

我们不能因为框架里的一些特性,过度地限制其余组件的使用

更新

中文文档链接 - https://gorm.io/zh_CN/docs/update.html

更新其实是最麻烦的事情,它包括更新的字段与条件。我们来看看几个重点的。

// 不推荐:单字段的更新,不常用
db.Model(&User{
   }).Where("active = ?", true).Update("name", "hello")

// 不推荐:指定主键的多字段更新,但不支持默认类型
db.Model(&user).Updates(User{
   Name: "hello", Age: 18, Active: false})

// 不推荐:指定主键的多字段的更新,但字段多了硬编码很麻烦
db.Model(&user).Updates(map[string]interface{
   }{
   "name": "hello", "age": 18, "active": false})

// 推荐:指定主键的多字段的更新,指定要更新的字段,*为全字段
db.Model(&user).Select("Name", "Age").Updates(User{
   Name: "new_name", Age: 0})
db.Model(&user).Select("*").Update(User{
   Name: "jinzhu", Role: "admin", Age: 0})

// 推荐:指定更新方式的多字段的更新
db.Model(User{
   }).Where("role = ?", "admin").Updates(User{
   Name: "hello", Age: 18})

删除

中文文档链接 - https://gorm.io/zh_CN/docs/delete.html

删除我不太建议使用,更推荐用软删除的方式,也就是更新一个标识是否已经删除字段

db.Delete(&email)

3.使用GORM的思考

GORM是一个非常重量级的工具,尤其是*gorm.DB提供了大量的类似于Builder模式的方法,用来拼接SQL

整个使用过程,对于一个不熟悉SQL语言的同学来说是很痛苦的,需要大量调试问题的时间;而对于熟悉SQL的朋友也会很疑惑,例如First等这种自定义命名的底层实现。所以,基于GORM库做一个简单封装是非常必要的,能大幅度地降低用户的使用和理解的门槛,也是这个微服务框架后续的改善方向之一。

对微服务框架的延伸思考

从之前的分析可以看到,我对微服务的框架有一个很重要的要求 - 透明,比如不要引入大量和原始SQL无关的特性、不要过度依赖ORM而忘记了原生SQL才是我们最重要的技能。

透明也是一个框架能实现简单性的重要特质,减少使用方的理解成本,也就能提高研发效能。

从更高的层面来看整个微服务框架,我们会有更深的体会:

  1. 为什么Spring Boot那么成功?主要是Spring Boot的设计理念是比较符合工程化的,而JVM也提供了一套很好的运行时的机制;与此同时,社区提供了大量的Spring Boot组件供开发者调用,自然比较受欢迎。
  2. Go的微服务框架为什么没有统一?Go的运行时非常轻量级,很难巧妙地像Spring Boot完成框架层面对组件的大一统。Go语言提供的各类组件,很多都是开源社区对传统服务或云原生理念的自我实践,没有绝对的正确与错误。
  3. 那如今社区上的那些微服务框架都不值一提吗?并不是。如果你仔细看这些框架,其实都是对各类Go优秀组件的拼装,只是各有各的想法。我觉得,所谓的Go微服务框架短期内很难统一,但这些组件都会趋于一致。
  4. 那你做这个框架的意义是什么呢?其实我个人并不觉得本框架比现有框架好,我更关注两点:一是分享引入并迭代各个开源组件的过程,让大家更好地理解框架是怎么完善的;第二个是从工程化的角度去思考微服务框架的问题,从会用框架变得理解框架、并改造框架。

总结

我们简单地引入了GORM并实现了一套简单的增删改查的代码,更多地是讨论一些技术选型的思考,希望能给大家带来启发。

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
9天前
|
JavaScript Java Go
探索Go语言在微服务架构中的优势
在微服务架构的浪潮中,Go语言以其简洁、高效和并发处理能力脱颖而出。本文将深入探讨Go语言在构建微服务时的性能优势,包括其在内存管理、网络编程、并发模型以及工具链支持方面的特点。通过对比其他流行语言,我们将揭示Go语言如何成为微服务架构中的一股清流。
|
6天前
|
监控 Go API
Go语言在微服务架构中的应用实践
在微服务架构的浪潮中,Go语言以其简洁、高效和并发处理能力脱颖而出,成为构建微服务的理想选择。本文将探讨Go语言在微服务架构中的应用实践,包括Go语言的特性如何适应微服务架构的需求,以及在实际开发中如何利用Go语言的特性来提高服务的性能和可维护性。我们将通过一个具体的案例分析,展示Go语言在微服务开发中的优势,并讨论在实际应用中可能遇到的挑战和解决方案。
|
7天前
|
Go 数据处理 API
Go语言在微服务架构中的应用与优势
本文摘要采用问答形式,以期提供更直接的信息获取方式。 Q1: 为什么选择Go语言进行微服务开发? A1: Go语言的并发模型、简洁的语法和高效的编译速度使其成为微服务架构的理想选择。 Q2: Go语言在微服务架构中有哪些优势? A2: 主要优势包括高性能、高并发处理能力、简洁的代码和强大的标准库。 Q3: 文章将如何展示Go语言在微服务中的应用? A3: 通过对比其他语言和展示Go语言在实际项目中的应用案例,来说明其在微服务架构中的优势。
|
26天前
|
Cloud Native Go API
Go语言在微服务架构中的创新应用与实践
本文深入探讨了Go语言在构建高效、可扩展的微服务架构中的应用。Go语言以其轻量级协程(goroutine)和强大的并发处理能力,成为微服务开发的首选语言之一。通过实际案例分析,本文展示了如何利用Go语言的特性优化微服务的设计与实现,提高系统的响应速度和稳定性。文章还讨论了Go语言在微服务生态中的角色,以及面临的挑战和未来发展趋势。
|
27天前
|
运维 Go 开发者
Go语言在微服务架构中的应用与优势
本文深入探讨了Go语言在构建微服务架构中的独特优势和实际应用。通过分析Go语言的核心特性,如简洁的语法、高效的并发处理能力以及强大的标准库支持,我们揭示了为何Go成为开发高性能微服务的首选语言。文章还详细介绍了Go语言在微服务架构中的几个关键应用场景,包括服务间通信、容器化部署和自动化运维等,旨在为读者提供实用的技术指导和启发。
|
30天前
|
Dubbo Java 应用服务中间件
Dubbo学习圣经:从入门到精通 Dubbo3.0 + SpringCloud Alibaba 微服务基础框架
尼恩团队的15大技术圣经,旨在帮助开发者系统化、体系化地掌握核心技术,提升技术实力,从而在面试和工作中脱颖而出。本文介绍了如何使用Dubbo3.0与Spring Cloud Gateway进行整合,解决传统Dubbo架构缺乏HTTP入口的问题,实现高性能的微服务网关。
|
1月前
|
负载均衡 Go API
探索Go语言在微服务架构中的应用与优势
在这篇技术性文章中,我们将深入探讨Go语言(又称为Golang)在构建微服务架构时的独特优势。文章将通过对比分析Go语言与其他主流编程语言,展示Go在并发处理、性能优化、以及开发效率上的优势。同时,我们将通过一个实际的微服务案例,详细说明如何利用Go语言构建高效、可扩展的微服务系统。
|
Dubbo Cloud Native 算法
Dubbo-go v3.0 —— 打造一流开源 Go 服务框架
2021 年底 dubbogo 社区正式推出集成 新通信协议、新序列化协议、新应用注册模型、新路由以及新的服务治理能力的 v3.0 版本
447 0
Dubbo-go v3.0  —— 打造一流开源 Go 服务框架
|
5天前
|
存储 JSON 监控
Viper,一个Go语言配置管理神器!
Viper 是一个功能强大的 Go 语言配置管理库,支持从多种来源读取配置,包括文件、环境变量、远程配置中心等。本文详细介绍了 Viper 的核心特性和使用方法,包括从本地 YAML 文件和 Consul 远程配置中心读取配置的示例。Viper 的多来源配置、动态配置和轻松集成特性使其成为管理复杂应用配置的理想选择。
23 2
|
3天前
|
Go 索引
go语言中的循环语句
【11月更文挑战第4天】
11 2