Go - 代码生成工具

分享两个常用的代码生成工具:

  • gormgen
  • handlergen

gormgen

基于 MySQL 数据表结构进行生成 3 个文件:

  1. 生成表的 struct 结构体
  2. 生成表的 Markdown 文档
  3. 生成表的 CURD 方法

场景

在进行业务需求开发时,创建完数据表后,执行代码生成工具,常用的 CURD 操作全部生成完毕,使用的时候只需要 . 后面的方法即可,这样大大提高了业务开发效率。

示例

表结构:

CREATE TABLE `user_demo` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `user_name` varchar(32) NOT NULL DEFAULT '' COMMENT '用户名',
  `nick_name` varchar(100) NOT NULL DEFAULT '' COMMENT '昵称',
  `mobile` varchar(20) NOT NULL DEFAULT '' COMMENT '手机号',
  `is_deleted` tinyint(1) NOT NULL DEFAULT '-1' COMMENT '是否删除 1:是  -1:否',
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户Demo表';

1、在 config 中设置需要自动生成的表,参数为 cmd.genTables,如果设置为空,表示生成当前数据库中的所有的表,如果设置多张表还可以用 “,” 分割。

[cmd]
genTables = 'user_demo'

2、在根目录下执行脚本

./scripts/gormgen.sh

执行完毕后,会在 /internal/api/repository/db_repo 中生成 user_demo_repo 目录,同时也会生成 3 个文件:

  • gen_model.go
  • gen_table.md
  • gen_user_demo.go

gen_model.go 内容如下:

package user_demo_repo

import "time"

// 用户Demo表
//go:generate gormgen -structs UserDemo -input .
type UserDemo struct {
    Id        int32     // 主键
    UserName  string    // 用户名
    NickName  string    // 昵称
    Mobile    string    // 手机号
    IsDeleted int32     // 是否删除 1:是  -1:否
    CreatedAt time.Time `gorm:"time"` // 创建时间
    UpdatedAt time.Time `gorm:"time"` // 更新时间
}

gen_table.md Markdown 内容如下:

image.png

gen_user_demo.go 内容如下:

func NewModel() *UserDemo {...}

func NewQueryBuilder() *userDemoRepoQueryBuilder {...}

func (t *UserDemo) Create(db *gorm.DB) (id int32, err error) {...}

func (t *UserDemo) Delete(db *gorm.DB) (err error) {...}

func (t *UserDemo) Updates(db *gorm.DB, m map[string]interface{}) (err error) {...}

type userDemoRepoQueryBuilder struct {...}

func (qb *userDemoRepoQueryBuilder) buildQuery(db *gorm.DB) *gorm.DB {...}

func (qb *userDemoRepoQueryBuilder) Count(db *gorm.DB) (int64, error) {...}

func (qb *userDemoRepoQueryBuilder) First(db *gorm.DB) (*UserDemo, error) {...}

func (qb *userDemoRepoQueryBuilder) QueryOne(db *gorm.DB) (*UserDemo, error) {...}

func (qb *userDemoRepoQueryBuilder) QueryAll(db *gorm.DB) ([]*UserDemo, error) {...}

func (qb *userDemoRepoQueryBuilder) Limit(limit int) *userDemoRepoQueryBuilder {...}

func (qb *userDemoRepoQueryBuilder) Offset(offset int) *userDemoRepoQueryBuilder {...}

func (qb *userDemoRepoQueryBuilder) WhereId(p db_repo.Predicate, value int32) *userDemoRepoQueryBuilder {...}

func (qb *userDemoRepoQueryBuilder) OrderById(asc bool) *userDemoRepoQueryBuilder {...}

func (qb *userDemoRepoQueryBuilder) WhereUserName(p db_repo.Predicate, value string) *userDemoRepoQueryBuilder {...}

func (qb *userDemoRepoQueryBuilder) OrderByUserName(asc bool) *userDemoRepoQueryBuilder {...}

func (qb *userDemoRepoQueryBuilder) WhereNickName(p db_repo.Predicate, value string) *userDemoRepoQueryBuilder {...}

func (qb *userDemoRepoQueryBuilder) OrderByNickName(asc bool) *userDemoRepoQueryBuilder {...}

func (qb *userDemoRepoQueryBuilder) WhereMobile(p db_repo.Predicate, value string) *userDemoRepoQueryBuilder {...}

func (qb *userDemoRepoQueryBuilder) OrderByMobile(asc bool) *userDemoRepoQueryBuilder {...}

func (qb *userDemoRepoQueryBuilder) WhereIsDeleted(p db_repo.Predicate, value int32) *userDemoRepoQueryBuilder {...}

func (qb *userDemoRepoQueryBuilder) OrderByIsDeleted(asc bool) *userDemoRepoQueryBuilder {...}

func (qb *userDemoRepoQueryBuilder) WhereCreatedAt(p db_repo.Predicate, value time.Time) *userDemoRepoQueryBuilder {...}

func (qb *userDemoRepoQueryBuilder) OrderByCreatedAt(asc bool) *userDemoRepoQueryBuilder {...}

func (qb *userDemoRepoQueryBuilder) WhereUpdatedAt(p db_repo.Predicate, value time.Time) *userDemoRepoQueryBuilder {...}

func (qb *userDemoRepoQueryBuilder) OrderByUpdatedAt(asc bool) *userDemoRepoQueryBuilder {...}

使用

这样使用生成后的方法:

// 查询示例:
user_demo_repo.NewQueryBuilder().
    WhereUserName(db_repo.EqualPredicate, "tom").
    OrderById(true).
    QueryOne(u.db.GetDbR().WithContext(ctx.RequestContext()))

handlergen

基于定义的 Handler 文件中 type interface 中接口方法,进行生成文件。

场景

本次需求的研发负责人通过定义 type interface 的方式,定义出需要开发的方法,执行代码生成工具,每个方法的空实现都会生成在一个单独的文件中,开发人员只需去实现各自方法即可,便于进行分工和代码管理。

示例

比如 test_handler 中定义的 type interface 如下:

var _ Handler = (*handler)(nil)

type Handler interface {
    // i 为了避免被其他包实现
    i()
    // Create 创建用户
    Create() core.HandlerFunc
    // Update 编辑用户
    Update() core.HandlerFunc
    // Delete 删除用户
    Delete() core.HandlerFunc
    // Detail 用户详情
    Detail() core.HandlerFunc
}

type handler struct {
    logger      *zap.Logger
    cache       cache.Repo
    userService user_service.UserService
}

func New(logger *zap.Logger, db db.Repo, cache cache.Repo) Handler {
    return &handler{
        logger:      logger,
        cache:       cache,
        userService: user_service.NewUserService(db, cache),
    }
}

func (h *handler) i() {}

在根目录下执行脚本:

./scripts/handlergen.sh test_handler

// test_handler 为需要生成 handler 的包名

执行完毕后,会在 /internal/api/controller/test_handler 中生成 4 个文件:

  • func_create.go
  • func_update.go
  • func_detail.go
  • func_delete.go

func_create.go 内容如下:

type createRequest struct{}

type createResponse struct{}

func (h *handler) Create() core.HandlerFunc {
    return func(c core.Context) {

    }
}

其中 createRequest 为入参结构体,createResponse 为出参结构体。

func_update.go 内容如下:

type updateRequest struct{}

type updateResponse struct{}

func (h *handler) Update() core.HandlerFunc {
    return func(c core.Context) {

    }
}

func_detail.go 内容如下:

type detailRequest struct{}

type detailResponse struct{}

func (h *handler) Detail() core.HandlerFunc {
    return func(c core.Context) {

    }
}

func_delete.go 内容如下:

type deleteRequest struct{}

type deleteResponse struct{}

func (h *handler) Delete() core.HandlerFunc {
    return func(c core.Context) {

    }
}
以上代码都在 go-gin-api 项目中,地址:https://github.com/xinliangno...

新亮笔记
技术的深度和广度只能靠自己努力去发掘,谁也不能替你学习,在这里希望你能有所收获。

日拱一卒

2.9k 声望
1.2k 粉丝
0 条评论
推荐阅读
关于项目中 Repository 层的思考
维护这一层的开发者,可以称为 仓库管理员 ,当使用者需要查询数据的时候,需要告诉仓库管理员,由仓库管理员拿给他,至于仓库管理员从哪拿的数据,使用者无需关系。

新亮2阅读 1.8k评论 1

前端如何入门 Go 语言
类比法是一种学习方法,它是通过将新知识与已知知识进行比较,从而加深对新知识的理解。在学习 Go 语言的过程中,我发现,通过类比已有的前端知识,可以更好地理解 Go 语言的特性。

robin23阅读 3.2k评论 6

封面图
Golang 中 []byte 与 string 转换
string 类型和 []byte 类型是我们编程时最常使用到的数据结构。本文将探讨两者之间的转换方式,通过分析它们之间的内在联系来拨开迷雾。

机器铃砍菜刀24阅读 58k评论 2

年度最佳【golang】map详解
这篇文章主要讲 map 的赋值、删除、查询、扩容的具体执行过程,仍然是从底层的角度展开。结合源码,看完本文一定会彻底明白 map 底层原理。

去去100216阅读 11.5k评论 2

年度最佳【golang】GMP调度详解
Golang最大的特色可以说是协程(goroutine)了, 协程让本来很复杂的异步编程变得简单, 让程序员不再需要面对回调地狱, 虽然现在引入了协程的语言越来越多, 但go中的协程仍然是实现的是最彻底的. 这篇文章将通过分析...

去去100215阅读 11.9k评论 4

万字详解,吃透 MongoDB!
MongoDB 是一个基于 分布式文件存储 的开源 NoSQL 数据库系统,由 C++ 编写的。MongoDB 提供了 面向文档 的存储方式,操作起来比较简单和容易,支持“无模式”的数据建模,可以存储比较复杂的数据类型,是一款非常...

JavaGuide8阅读 1.7k

封面图
数据结构与算法:二分查找
一、常见数据结构简单数据结构(必须理解和掌握)有序数据结构:栈、队列、链表。有序数据结构省空间(储存空间小)无序数据结构:集合、字典、散列表,无序数据结构省时间(读取时间快)复杂数据结构树、 堆图二...

白鲸鱼9阅读 5.3k

日拱一卒

2.9k 声望
1.2k 粉丝
宣传栏