首页 > 数据库 > 数据库开发技术 > 这开源的生成表模型工具,好用
生成表模型工具好用的定义是什么,方便、功能齐全。
大家好,我是皇子。
外面已经有很多工具能生成Golang gorm 的表模型了,但是在个人项目中和公司的项目中发现都不方便直接使用。
原因一:模型定义的格式不完整,排版难看,没有注释。
原因二:功能单一,只是为了生成一个模型而已,缺少关于模型的常用操作方法。
原因三:可读性和扩展性差,各种表字段满天飞,各种SQL组装,容易出错。
甚至,还有些工具是收费的。
所以针对以上的问题,我开源一个表模型工具,下面开始介绍。
GitHub 地址:https://github.com/leeprince/gormstruct
这工具不仅能生成Golang gorm 的表模型,还能基于模型生成所有字段的操作方法在DAO层。

(一)关于模型
SetXxx()、GetXxx()方法:用户设置/获取模型下指定字段的值示例
生成操作表的基本方法,对应文件名:base_DAO.go 对应的表 DAO 层:xxx_dao.go 里面包含多种操作表数据的简单、易用、易维护方法。
❝自动判断是否存在软删除字段,存在则默认过滤非软删除记录
生成UpdateOrCreate()方法:存在则更新(只会更新非零值),否则插入(会忽略零值字段)
生成Save()方法:gorm 原生的 Save 会保存所有的字段,即使字段是零值。仅会判断主键是否存在,存在则更新,不存在则创建
生成Create()方法:创建数据:允许单条/批量创建,批量创建时传入切片
为所有字段生成 WithXxx方法:将单个字段的值作为查询条件
为所有字段生成 WithXxxs方法(多一个s):将单个字段的列表值作为查询条件
生成GetByOption()方法:函数选项模式获取单条记录
生成GetListByOption方法:函数选项模式获取多条记录:支持分页
生成GetCountByOption()方法:函数选项模式获取多条记录:支持统计记录总数
生成GetCustomeResultByOption()方法:函数选项模式获取多条记录到自定义结构体(result:务必使用指针变量):支持包含自定义聚合字段(自定义的聚合字段务必添加 gorm:"column:字段的别名;" 标签)
生成UpdateByOption():更新数据
生成GetFromXxx()方法:简单直接通过单个字段值获取数据(自动判断该字段是否设置唯一索引,否则自动获取单条记录,反之则是多条记录)
生成GetListFromXxx()方法:简单直接通过单个字段的列表值获取多条记录
生成FetchByXxx()方法:通过索引(唯一索引(主键、唯一索引、唯一复合索引)、非唯一索引(普通索引))作为查询条件获取数据(自动判断索引类型确定获取单条记录还是多条记录)
示例
type UsersDAO struct {
*_BaseDAO
}
// NewUsersDAO 初始化
func NewUsersDAO(ctx context.Context, db *gorm.DB) *UsersDAO {
if db == nil {
panic(fmt.Errorf("UsersDAO need init by db"))
}
ctx, cancel := context.WithCancel(ctx)
return &UsersDAO{
_BaseDAO: &_BaseDAO{
DB: db.Model(&Users{}),
db: db,
model: Users{},
ctx: ctx,
cancel: cancel,
timeout: -1,
columns: UsersAllColumns,
isDefaultColumns: true,
},
}
}
// GetTableName 获取表名称
func (obj *UsersDAO) GetTableName() string {
users := &Users{}
return users.TableName()
}
// UpdateOrCreate 存在则更新,否则插入,会忽略零值字段
func (obj *UsersDAO) UpdateOrCreate(users *Users) (rowsAffected int64, err error) {
if users.PrimaryKeyValue() > 0 {
return obj.UpdateByOption(users, obj.WithPrimaryKey(users.PrimaryKeyValue()))
}
return obj.Create(users)
}
// Save gorm 原生的 Save 会保存所有的字段,即使字段是零值。仅会判断主键是否存在,存在则更新,不存在则创建
func (obj *UsersDAO) Save(users *Users) (rowsAffected int64) {
return obj.db.Save(users).RowsAffected
}
// Create 创建数据:允许单条/批量创建,批量创建时传入切片
func (obj *UsersDAO) Create(users interface{}) (rowsAffected int64, err error) {
tx := obj.withContext().Create(users)
return tx.RowsAffected, tx.Error
}
// --- 表中的字段作为 option 条件 ---
// WithPrimaryKey 设置真正的主键 字段作为 option 条件
func (obj *UsersDAO) WithPrimaryKey(primaryKeyValue interface{}) Option {
return queryMapOptionFunc(func(o *options) { o.queryMap[(&Users{}).PrimaryKey()] = primaryKeyValue })
}
// WithID 设置 id(主键) 字段作为 option 条件
func (obj *UsersDAO) WithID(id int64) Option {
return queryMapOptionFunc(func(o *options) { o.queryMap[UsersColumns.ID] = id })
}
// WithIDs 设置 id(主键) 字段的切片作为 option 条件
func (obj *UsersDAO) WithIDs(ids []int64) Option {
return queryMapOptionFunc(func(o *options) { o.queryMap[UsersColumns.ID] = ids })
}
...
// --- 单个字段作为查询条件 ---
// GetFromID 通过单个 id(主键) 字段值,获取单条记录
func (obj *UsersDAO) GetFromID(id int64) (result *Users, err error) {
result, err = obj.GetByOption(obj.WithID(id))
return
}
// GetListFromID 通过多个 id(主键) 字段值,获取多条记录
func (obj *UsersDAO) GetListFromID(ids []int64) (results []*Users, err error) {
results, err = obj.GetListByOption(obj.WithIDs(ids))
return
}
...
// --- 通过索引(唯一索引(主键、唯一索引、唯一复合索引)、非唯一索引(普通索引))作为查询条件 ---
// FetchByPrimaryKey 通过 id 字段值,获取单条记录
func (obj *UsersDAO) FetchByPrimaryKey(id int64) (result *Users, err error) {
result, err = obj.GetByOption(obj.WithID(id))
return
}
// FetchUniqueByCardNo 通过 card_no 字段值,获取单条记录
func (obj *UsersDAO) FetchUniqueByCardNo(cardNo string) (result *Users, err error) {
result, err = obj.GetByOption(obj.WithCardNo(cardNo))
return
}
...
❝所有的方法:在项目中都包含相应的测试用例
test/xxx_test.go
./config/config.bak.yaml为./config/config.yamldbinfo为自己的数据库链接配置# 数据库设置
dbinfo:
host: 127.0.0.1
port: 3306
username: root
password: root
database: ticket # 该参数可通过命令行设置,命令行设置会覆盖该值。如果未设置则必须在命令中指定
type: 0 # 数据库类型:0:mysql
go run main.go -t={数据库下的表名}会在./out/model下生成3个文件❝go run main.go --help go run main.go gen --help
表名参数(-t)必填!
go run main.go -t=users [-p=model] [-s=Users]
go run main.go --table=users [--packageName=model] [--structName=Users]
-d, --database string 指定连接的数据库名【在yaml配置时可选,命令设置优先级大于配置】
-t, --table string 指定的表名【必填】
-h, --help help for gen
-p, --packageName string 生成的包名【可选,默认:model】
-s, --structName string 表名对应结构体【可选,默认:表名的大驼峰命名】
略
WithWhere 支持原生的SQL组装。之前不考虑支持的原因是希望减少SQL组装的手动设置,防止查询条件扩散难于维护。
工作中我都会只用一个WithWhere ,并将所有需要组装的条件包含。
目前,在公司新项目中,我都是使用了这个工具,同事也很推荐,所以也向我提了反馈想要WithWhere支持多次使用。
理由是:多次使用更加灵活,更加可读。
也给了我一些建议,所以考虑后,决定更新并向前兼容。
未更新前:只能使用一次WithWhere方法,所以需要多条件一次性组装完成
userDAO := model.NewUsersDAO(context.Background(), db)
opt := []model.Option{
// 只能使用一次WithWhere方法
userDAO.WithWhere("age >= ? AND age <= ?", 18, 30)
}
users, err := userDAO.GetListByOption(opt...)
更新后:支持使用多次WithWhere方法
userDAO := model.NewUsersDAO(context.Background(), db)
opt := []model.Option{
// 支持使用多次WithWhere方法
userDAO.WithWhere("age >= ?", 18),
userDAO.WithWhere("age <= ?", 30),
}
users, err := userDAO.GetListByOption(opt...)
使用场景
接口请求中要求查询年龄区间的所有用户。
有两个参数:minAge(最小年龄)、maxAge(最大年龄)
说明:minAge(最小年龄),maxAge(最大年龄)都可选
那么未更新前:只能使用一次WithWhere方法,所以需要多条件一次性组装完成
var sql string
if req.minAge > 0 {
sql = "age >= " + req.minAge
}
if req.maxAge > 0 {
if sql == "" {
sql = "age <=" + req.maxAge
} else {
sql += " AND age <=" + req.maxAge
}
}
userDAO := model.NewUsersDAO(context.Background(), db)
opt = []model.Option{
// 只能使用一次WithWhere方法,所以前面必须组装好SQL再传入WithWhere方法中
userDAO.WithWhere(sql),
}
users, err := userDAO.GetListByOption(opt...)
而更新后:支持使用多次WithWhere方法
// 支持使用多次WithWhere方法,所以判断后直接新增查询条件即可
opt := []model.Option{}
if req.minAge > 0 {
opt = append(opt, userDAO.WithWhere("age >= ?", req.minAge))
}
if req.maxAge > 0 {
opt = append(opt, userDAO.WithWhere("age <= ?", req.maxAge))
}
userDAO := model.NewUsersDAO(context.Background(), db)
users, err := userDAO.GetListByOption(opt...)
点个【赞和在看】,让开源更有更有意义。
推荐阅读
关于作者:一位热爱技术,并在职场与自媒体间探索的实践者,希望通过分享个人经验和见解,帮助更多人实现自我成长和价值。
如果您对我的公众号内容感兴趣,欢迎关注我。也可以在下方添加我微信和一支烟花AI社区技术群,一起交流技术、职场心得、自媒体运营经验。欢迎~
原文作者:皇子谈技术;如有侵权,请联系管理员删除。