数据库 model/dao 层代码生成 + Go 项目脚手架生成
gentol 是一个 Golang 代码生成工具,旨在减少重复编码工作,提升开发效率。核心特性:
- 项目脚手架生成:从模板仓库 clone 项目骨架到内存,自动替换模块路径和项目名后写入磁盘,一键生成可运行的 Go 项目。模板预编译进二进制(
//go:embed),开箱即用无需网络 - 数据库代码生成:连接数据库自动分析表结构,生成 GORM Model、DAO 接口/实现、流式 Condition Builder 等代码,支持 MySQL / PostgreSQL / SQLite / Oracle / SQL Server / 达梦 等多种数据库
- Service / Manager 模板:在已有项目中快速添加 service 或 manager 代码骨架,遵循 sync.Once 单例 + 接口分离模式
go install github.com/jasonlabz/gentol@mastergentol new|init <project_name|module_name>从模板项目 clone 到内存,替换模块路径和项目名后写入磁盘。默认使用内置嵌入模板,无需网络。
# 短项目名
gentol new myapp
# 完整模块路径
gentol new github.com/myorg/myapp| 参数 | 说明 |
|---|---|
--template_repo=<git_url> |
从指定 Git 仓库克隆模板 |
--template_dir=<local_path> |
从本地目录加载模板 |
--offline |
离线模式,不访问网络(使用嵌入数据或本地缓存) |
# 从指定 Git 仓库克隆模板
gentol new github.com/myorg/myapp --template_repo=https://github.com/xxx/my-template.git
# 从本地目录加载模板(开发调试用)
gentol new github.com/myorg/myapp --template_dir=/path/to/template
# 离线模式:不访问网络,使用嵌入数据或本地缓存
gentol new github.com/myorg/myapp --offlinegentol 会自动读取模板项目 go.mod 中的 module 路径,执行以下替换:
| 上下文 | 替换方式 | 示例 |
|---|---|---|
| Go 文件 import 语句 | 完整模块路径 | template/bootstrap → github.com/myorg/myapp/bootstrap |
| go.mod module 行 | 完整模块路径 | module template → module github.com/myorg/myapp |
| Go 文件非 import 行 | 项目短名称 | 注释、字符串中的项目名 |
| Makefile / YAML / 其他文件 | 项目短名称 | TARGETNAME = myapp、name: myapp |
| 文件名 / 目录名 | 项目短名称 | demo_program/ → myapp_program/、demo.yaml → myapp.yaml |
从模板重新加载文件,覆盖同名文件,保留项目中自定义文件不变。
# 在项目目录内执行
gentol update
# 在外层指定项目名
gentol update myapp
gentol update github.com/myorg/myapp
# 离线更新
gentol update --offline# 添加 service(在项目目录内执行)
gentol add user
gentol add user_service # 同上
# 添加 manager(可调用多个 service,位于 controller 和 service 之间)
gentol add user_manager--service 参数:指定输出子目录
# 默认输出到 server/service/
gentol add user
# 指定子目录,输出到 server/service/workflow/
gentol add user --service workflow
# manager 同样支持
gentol add user_manager --service workflow # 输出到 server/manager/workflow/
# 支持多级目录
gentol add user --service workflow/sub生成的文件结构:
server/service/
├── user_service.go # 接口定义 + sync.Once 单例 Getter
└── user/
├── user_service_impl.go # 实现
└── body/
├── request.go
├── response.go
├── vo.go
└── dto.go
使用 --service workflow 后:
server/service/workflow/
├── user_service.go # 接口定义 + sync.Once 单例 Getter
└── user/
├── user_service_impl.go # 实现(import 路径自动包含 /workflow)
└── body/
├── request.go
├── response.go
├── vo.go
└── dto.go
Manager 模式同理,输出到 server/manager/ 或 server/manager/<subdir>/。
项目生成采用内存化处理,不产生临时目录残留:
加载模板到内存(嵌入数据 / git clone / 本地目录)
→ 内存中替换模块路径 + 项目名 + 文件路径
→ 一次性写入磁盘目标目录
→ 执行 go mod tidy
gentol 提供三层离线保障,无需网络即可创建项目:
嵌入式模板(优先级最高):通过 //go:embed 将模板预编译进二进制,gentol new 开箱即用。
# 1. 更新嵌入模板(从模板仓库下载并打包到 embedded/template.tar.gz)
bash scripts/update_template.sh # Linux/macOS
scripts\update_template.bat # Windows
# 2. 重新编译(模板编译进二进制)
go build .
# 3. 之后无需任何网络
gentol new myapp # 直接从嵌入数据创建本地缓存:每次网络 clone 成功后,自动将模板缓存到 ~/.gentol/cache/<hash>/(tar.gz 格式)。
自动回退:网络不可用时自动尝试缓存,无需手动指定 --offline。
加载优先级:
| 场景 | 默认模板 | 自定义 --template_repo |
|---|---|---|
| 在线 | 嵌入数据 → 网络 → 缓存 | 网络 → 缓存 |
--offline |
嵌入数据 → 缓存 | 缓存 |
只需维护一个标准的 Go 项目作为模板,push 到 Git 仓库即可。模板项目的唯一约定:
go.mod中的module行定义了模块路径,gentol 自动读取并替换- 建议模板使用完整模块路径(如
github.com/jasonlabz/gentol-template),这样替换逻辑最精确
通过 gentol ddl 子命令直接对数据库执行 DDL 文件(仅允许 CREATE / ALTER / DROP / TRUNCATE / RENAME / COMMENT 语句):
gentol ddl <sql文件路径> [--db_type=...] [--host=...] [--port=...] [--username=...] [--password=...] [--database=...] [--schema=...]也可以直接提供 DSN:
gentol ddl schema.sql --db_type=postgres --dsn="user=postgres password=XXX host=127.0.0.1 port=5432 dbname=mydb sslmode=disable"| 参数 | 说明 |
|---|---|
--db_type |
数据库类型(必填):mysql, postgres, sqlserver, oracle, greenplum, sqlite, dm |
--dsn |
数据库连接字符串,提供后无需 host/port 等参数 |
--host |
数据库主机 |
--port |
数据库端口 |
--username |
数据库用户名 |
--password |
数据库密码 |
--database |
数据库名 |
--schema |
Schema 名(PostgreSQL/Oracle/DM 等),用于 DAO/Model 层的 schema 标识 |
# PostgreSQL(指定 schema)
gentol ddl conf/sql/pg.sql \
--db_type=postgres \
--host=127.0.0.1 --port=5432 \
--username=postgres --password=XXX \
--database=mydb --schema=myschema
# MySQL
gentol ddl conf/sql/mysql.sql \
--db_type=mysql \
--host=127.0.0.1 --port=3306 \
--username=root --password=XXX \
--database=mydb
# 使用 DSN
gentol ddl migration.sql --db_type=postgres \
--dsn="user=postgres password=XXX host=127.0.0.1 port=5432 dbname=mydb sslmode=disable"- DDL 执行前会校验 SQL 内容,仅允许以下语句:
CREATE、ALTER、DROP、TRUNCATE、RENAME、COMMENT - 包含 DML 语句(INSERT / UPDATE / DELETE / SELECT 等)会被拒绝
- 建议建表 SQL 使用
CREATE TABLE IF NOT EXISTS,加列使用IF NOT EXISTS,保证幂等
不指定子命令时进入 DB 代码生成模式:
gentol --db_type=<type> --dsn=<connection_string> [options...]也可以通过 YAML 配置文件(conf/table.yaml 或 ./table.yaml)指定生成参数。
| 参数 | 简写 | 默认值 | 说明 |
|---|---|---|---|
--db_type |
postgres |
数据库类型:mysql, postgres, sqlserver, oracle, greenplum, sqlite, dm | |
--dsn |
数据库连接字符串,提供后无需 host/port/username/password | ||
--host |
-h |
数据库主机 | |
--port |
-p |
数据库端口 | |
--username |
-U |
数据库用户名 | |
--password |
-P |
数据库密码 | |
--database |
-d |
数据库名 | |
--schema |
-s |
Schema 名(PostgreSQL/Oracle 等) | |
--table |
-t |
表名列表(逗号分隔),不提供则生成当前 schema 下所有表 | |
--model |
dal/db/model |
Model 层输出路径 | |
--dao |
dal/db/dao |
DAO 层输出路径 | |
--service |
server/service |
Service 层输出路径 | |
--module |
-m |
Go module 名(用于 import 路径) | |
--json_format |
snake |
JSON tag 命名格式:snake / upper_camel / lower_camel | |
--protobuf_format |
snake |
Protobuf tag 命名格式 | |
--only_model |
仅生成 Model,不生成 DAO | ||
--gen_hook |
生成 GORM Hook 文件 | ||
--use_sql_nullable |
使用 sql.Null 类型替代 guregu/null | ||
--proto |
添加 Protobuf 注解 | ||
--rungofmt |
生成后执行 gofmt |
PostgreSQL
gentol --db_type="postgres" \
--dsn="user=postgres password=XXXXX host=127.0.0.1 port=5432 dbname=mydb sslmode=disable" \
--schema="public"MySQL
gentol --db_type="mysql" \
--dsn="root:password@tcp(127.0.0.1:3306)/mydb?parseTime=True&loc=Local" \
--table="users,orders"SQLite
gentol --db_type="sqlite" \
--dsn="/path/to/database.db" \
--table="users" \
--gen_hookOracle
gentol --db_type="oracle" \
--dsn="username/password@host:1521/service_name" \
--table="USERS" \
--gen_hookSQL Server
gentol --db_type="sqlserver" \
--dsn="user id=sa;password=XXX;server=127.0.0.1;port=1433;database=mydb;encrypt=disable" \
--table="users"DM(达梦)
gentol --db_type="dm" \
--dsn="dm://username:password@host:5236?schema=SCHEMA_NAME" \
--table="USERS"DSN 格式参考
var DatabaseDsnMap = map[DBType]string{
DBTypeSQLite: "%s",
DatabaseTypeDM: "dm://%s:%s@%s:%d?schema=%s",
DBTypeOracle: "%s/%s@%s:%d/%s",
DBTypeMySQL: "%s:%s@tcp(%s:%d)/%s?parseTime=True&loc=Local&timeout=30s",
DBTypePostgres: "user=%s password=%s host=%s port=%d dbname=%s sslmode=disable",
DBTypeSqlserver: "user id=%s;password=%s;server=%s;port=%d;database=%s;encrypt=disable",
DBTypeGreenplum: "user=%s password=%s host=%s port=%d dbname=%s sslmode=disable",
}每张表生成以下文件:
| 文件 | 路径 | 覆盖策略 |
|---|---|---|
{table}.go |
dal/db/model/ |
始终覆盖 |
{table}_hook.go |
dal/db/model/ |
仅首次生成(可手动编辑) |
base.go |
dal/db/model/ |
始终覆盖 |
{table}_dao.go |
dal/db/dao/ |
始终覆盖 |
{table}_dao_ext.go |
dal/db/dao/ |
仅首次生成(可手动扩展) |
{table}_dao_impl.go |
dal/db/dao/impl/ |
始终覆盖 |
{table}_dao_ext_impl.go |
dal/db/dao/impl/ |
仅首次生成(可手动扩展) |
db.go |
dal/db/dao/ |
始终覆盖 |
Model 生成示例
// Code generated by jasonlabz/gentol. DO NOT EDIT.
package model
type UserField string
type User struct {
UserID int64 `gorm:"primaryKey;autoIncrement;column:user_id;not null;type:bigint" json:"user_id"`
Nickname string `gorm:"column:nickname;not null;type:varchar(255)" json:"nickname"`
Phone string `gorm:"unique;column:phone;not null;type:varchar(255)" json:"phone"`
// ...
}
type UserCondition struct { Condition }
// 流式查询构建
func (u *UserCondition) UserIDEqualTo(value int64) *UserCondition {
return u.Where("user_id = ?", value)
}
func (u *UserCondition) NicknamePrefixLike(value string) *UserCondition {
return u.Where("nickname like ?", value+"%")
}DAO 接口生成示例
type UserDao interface {
UserDaoExt // 用户自定义扩展接口
SelectAll(ctx context.Context, selectFields ...model.UserField) ([]*model.User, error)
SelectOneByPrimaryKey(ctx context.Context, id int64, selectFields ...model.UserField) (*model.User, error)
SelectRecordByCondition(ctx context.Context, condition *model.Condition, selectFields ...model.UserField) ([]*model.User, error)
SelectPageRecordByCondition(ctx context.Context, condition *model.Condition, page *model.Pagination, selectFields ...model.UserField) ([]*model.User, error)
CountByCondition(ctx context.Context, condition *model.Condition) (int64, error)
Insert(ctx context.Context, record *model.User) (int64, error)
BatchInsert(ctx context.Context, records []*model.User) (int64, error)
UpdateRecord(ctx context.Context, record *model.User) (int64, error)
UpdateByCondition(ctx context.Context, condition *model.Condition, updateField *model.UpdateField) (int64, error)
DeleteByPrimaryKey(ctx context.Context, id int64) (int64, error)
DeleteByCondition(ctx context.Context, condition *model.Condition) (int64, error)
UpsertRecord(ctx context.Context, record *model.User) (int64, error)
InsertOrUpdateOnDuplicateKey(ctx context.Context, record *model.User) (int64, error)
// ...
}使用示例
// 按主键查询
user, err := userDao.SelectOneByPrimaryKey(ctx, userID)
// 条件查询
cond := &model.UserCondition{}
cond.UserIDEqualTo(userID).GenderEqualTo(1)
// 指定查询字段
col := cond.ColumnInfo()
users, err := userDao.SelectRecordByCondition(ctx, cond.Build(), col.UserID, col.Nickname)
// 分页查询
users, err := userDao.SelectPageRecordByCondition(ctx, cond.Build(), pagination)- 请确保已安装 Golang 开发环境及相应数据库驱动
- 生成的代码仅供参考,可能需要根据实际需求修改
_ext.go和_hook.go文件仅首次生成,后续不会被覆盖,可安全编辑- Oracle 驱动运行报错时:
go env -w CGO_ENABLED=1
如有问题或建议,欢迎提 Issue 或 PR。