Documentation
¶
Overview ¶
Package generator generate.go 是编排器:把「表元数据 + 配置」组装成 TemplateData,再按约定路径渲染落盘。 与 render.go(模板渲染)和 templates.go(模板嵌入)处于同一包 generator 内,共享 TemplateData 定义。
核心流程:
TableMetadata + Config
↓ BuildTemplateData(类型映射、主键推导、autoFill 判定)
TemplateData
↓ Render(text/template 执行)
Java 代码字符串
↓ Generate(落盘 or dry-run)
*.java 文件 / io.Writer 输出
Go 小白知识点(全文重点):
- io.Writer 接口:只要实现了 Write(p []byte) (n int, err error) 方法,就是 Writer。 os.File、bytes.Buffer、os.Stdout 都是 Writer。Generate 只依赖接口, 测试时传 bytes.Buffer,main 传 os.Stdout,代码一字不改——这是"依赖接口"的典型好处。
- filepath.Join:跨平台路径拼接(Windows 用 \,Unix 用 /),不要手动拼字符串。
- os.MkdirAll:等同于 mkdir -p,目录已存在不报错。
- os.WriteFile:覆盖写文件(Go 1.16+,非原子:中途失败可能留下截断文件),自动创建或覆盖。
- 包级变量 since:由 main 注入,避免库代码调用 time.Now()——库内取系统时间会让测试非确定性。
Package generator 负责把元数据渲染成代码字符串(po/mapper/service/service-impl/query/converter/mapper-xml 多层)。 核心流程:embed.FS 加载模板 → text/template 解析 → Execute 写入 bytes.Buffer → 返回代码字符串(Java 或 XML)。
Package generator 负责把元数据渲染成 Java 代码字符串(po/mapper/service 三层)。 本文件通过 //go:embed 把模板目录编进二进制,实现"单文件可执行"——发布时无需附带 templates/ 目录。
Index ¶
- Variables
- func AllLayers() []string
- func Generate(cfg config.Config, meta model.TableMetadata, layers []string, dryRun bool, ...) error
- func OutputPath(layer, basePackage, javaRoot, resourcesRoot, modelUpperCamel string) (string, error)
- func Render(layer string, data TemplateData) (string, error)
- func ResolveResourcesRoot(javaRoot, configured string) string
- func SelectLayers(onlyTableModify, withoutApi bool) []string
- func SetSince(s string)
- type LayerSpec
- type TemplateData
Constants ¶
This section is empty.
Variables ¶
var Layers = map[string]LayerSpec{ "po": {PkgSuffix: "model.po", NameSuffix: "", Ext: ".java"}, "mapper": {PkgSuffix: "mapper", NameSuffix: "Mapper", Ext: ".java"}, "service": {PkgSuffix: "service", NameSuffix: "Service", Ext: ".java"}, "service-impl": {PkgSuffix: "service.impl", NameSuffix: "ServiceImpl", Ext: ".java"}, "query": {PkgSuffix: "model.query", NameSuffix: "Query", Ext: ".java"}, "converter": {PkgSuffix: "converter", NameSuffix: "Converter", Ext: ".java"}, "mapper-xml": {PkgSuffix: "mapper", NameSuffix: "Mapper", Ext: ".xml", Resource: true}, "req-dto": {PkgSuffix: "model.dto.req", NameSuffix: "ReqDto", Ext: ".java"}, "resp-dto": {PkgSuffix: "model.dto.resp", NameSuffix: "RespDto", Ext: ".java"}, "query-req-dto": {PkgSuffix: "model.dto.req", NameSuffix: "QueryReqDto", Ext: ".java"}, "page-query-req-dto": {PkgSuffix: "model.dto.req", NameSuffix: "PageQueryReqDto", Ext: ".java"}, "update-by-query-req-dto": {PkgSuffix: "model.dto.req", NameSuffix: "UpdateByQueryReqDto", Ext: ".java"}, "api": {PkgSuffix: "api", NameSuffix: "Api", Ext: ".java"}, "api-impl": {PkgSuffix: "api.impl", NameSuffix: "ApiImpl", Ext: ".java"}, }
Layers 是层 → 输出约定的全局约定表(M2-A 含非 API 的 7 层;M2-B 再补 api/dto)。
设计原则(开闭原则):新增层只需在此追加一条 LayerSpec 记录, Generate/OutputPath 等编排代码无需任何修改。
Go 小白知识点:var xxx = map[K]V{...} 是包级变量初始化, 整个程序生命周期内共享同一份 map。约定上只读;但它是导出的包级 map, 语言层面 importer 仍可改写——约定不可变即可,无需额外防护。
Functions ¶
func AllLayers ¶
func AllLayers() []string
AllLayers 返回全部 14 层,固定生成顺序(po 优先、api 收尾,便于阅读产物)。
设计原则:顺序固定(而非遍历 Layers map),因为 map 遍历在 Go 中是随机序—— 如果直接 range Layers,每次调用的输出顺序不同,对用户体验(diff、日志)不友好。 用切片显式定义顺序,保证稳定输出。
func Generate ¶
func Generate(cfg config.Config, meta model.TableMetadata, layers []string, dryRun bool, out io.Writer) error
Generate 渲染指定层并落盘(dryRun=false)或写入 out(dryRun=true)。
参数说明:
- cfg:生成器全局配置(包名、输出根目录、数据源等)
- meta:单张表的元数据(列名、类型、注释、主键标记)
- layers:要生成的层列表,如 []string{"po","mapper","service"}
- dryRun:true=只打印到 out,不创建目录/文件;false=落盘
- out:dry-run 时的输出目标(io.Writer 接口);落盘模式传 nil 即可
io.Writer 接口抽象的价值(Go 小白重点):
Generate 内部只调用 fmt.Fprintf(out, ...),不关心 out 是 bytes.Buffer 还是 os.Stdout。
测试:传 &bytes.Buffer{},事后检查 buf.Len() 和 buf.String()——完全内存操作,无副作用。
main:传 os.Stdout,内容直接打印到终端。
两个场景共用同一套代码,零重复。
func OutputPath ¶
func OutputPath(layer, basePackage, javaRoot, resourcesRoot, modelUpperCamel string) (string, error)
OutputPath 由约定推导落盘路径。
- java 层(Resource=false):javaRoot + 包路径 + 文件名(含包子目录)
- resource 层(Resource=true):resourcesRoot + PkgSuffix(仅一级,不按完整包名建子目录)+ 文件名
返回 (path, error):未知层(不在 Layers 表)返回 error,避免静默写错位置。
Go 小白知识点:多返回值 (string, error) 是 Go 惯用的错误传递方式; 调用方必须检查 error(编译器不强制,但 go vet/lint 会警告忽略 error)。
func Render ¶
func Render(layer string, data TemplateData) (string, error)
Render 渲染指定层的代码模板,返回生成的代码字符串(Java 或 XML)。 layer 取值:"po" / "mapper" / "service" / "service-impl" / "query" / "converter" / "mapper-xml", 对应 templates/<layer>.tmpl。 Go 小白知识点:Go 惯例用 (value, error) 双返回值替代异常;调用方必须检查 error。
func ResolveResourcesRoot ¶
ResolveResourcesRoot 由 java 源根派生 resources 根:把末段 src/main/java 换成 src/main/resources。 若 configured 非空则直接用它(配置优先于约定,便于非标准项目结构覆盖)。
Go 小白知识点:filepath.Join 跨平台拼路径;strings.HasSuffix 检查字符串是否以某子串结尾。 拼路径永远用 filepath.Join,不要手写 "/" 或 "\\"——Windows/Unix 共用同一套代码。
func SelectLayers ¶
SelectLayers 按两个开关对全集做交集过滤:
- 默认(两者 false):返回全 14 层。
- onlyTableModify=true:仅保留"改表影响层"(po/req-dto/resp-dto/mapper-xml/query/query-req-dto)。
- withoutApi=true:仅保留"非 API 层"(service/service-impl/po/query/mapper/mapper-xml)。
- 两者同时 true:取交集(po/query/mapper-xml 这 3 层)。
Go 小白知识点:用 map 做集合(值恒 true)做 O(1) 成员判断,按 AllLayers() 全集顺序过滤 以保持稳定输出顺序(map 遍历是随机序,切片遍历是稳定序)。
Types ¶
type LayerSpec ¶
type LayerSpec struct {
PkgSuffix string // 包后缀,如 model.po;resource 层用作子目录(mapper → resources/mapper/)
NameSuffix string // 文件名后缀,如 ServiceImpl;po 层为空(文件名即 ModelUpperCamel)
Ext string // 文件扩展名:.java 或 .xml
Resource bool // true 表示落 resources 根(如 mapper-xml),false 落 java 根
}
LayerSpec 描述一层的输出约定:包后缀、文件名在 ModelUpperCamel 后追加的后缀、扩展名、是否落 resources 根。
Go 小白知识点:结构体(struct)是 Go 中聚合多字段的基本方式,类似 Java 的 POJO。 这里用值类型(非指针),因为 LayerSpec 是不可变的配置描述,复制开销极低。
type TemplateData ¶
type TemplateData struct {
// Author 代码作者,填入 @author 注释。
Author string
// Since 生成日期,填入 @since 注释。
Since string
// TableName 数据库表名(原始下划线命名),填入 @TableName。
TableName string
// BasePackage Java 基础包名,如 com.dahaoshen.demo。
BasePackage string
// ModelUpperCamel 大驼峰实体类名,如 SysUser。
ModelUpperCamel string
// ModelCamel 小驼峰变量名,如 sysUser。
ModelCamel string
// ModelKebab 中划线名,如 sys-user,用于 URL 路径等场景。
ModelKebab string
// ModelComment 中文表注释,填入 Javadoc 与 @Schema。
ModelComment string
// PkFieldUpperCamel 主键字段的大驼峰名(如 Id),用于拼 getter 方法名。
PkFieldUpperCamel string
// IdType 主键 Java 类型(如 Long / String),模板里直接展开。
IdType string
// UseJakarta 为 true 时引入 jakarta 包并生成 @Serial 注解(Spring Boot 3+ 用 jakarta 而非 javax)。
UseJakarta bool
// IsWithAutoFill 为 true 时 import 整个 annotation 包(含 FieldFill),用于自动填充场景。
IsWithAutoFill bool
// Fields 表的全部字段元数据,模板中用 range .Fields 遍历。
// Go 小白知识点:range 内 "." 指向当前元素(FieldMetadata),父级字段要用 "$." 访问(如 $.UseJakarta)。
Fields []model.FieldMetadata
}
TemplateData 是模板渲染上下文,所有字段首字母大写(导出),才能被 text/template 的 {{.Field}} 访问。 Go 小白知识点:text/template 通过反射读取结构体字段,未导出(小写)字段对模板不可见。
func BuildTemplateData ¶
func BuildTemplateData(meta model.TableMetadata, cfg config.Config) (TemplateData, error)
BuildTemplateData 把表元数据 + 配置组装成模板上下文(TemplateData)。
步骤:
- 解析方言,获取 TypeMapper(类型映射器)
- 遍历 Columns,映射 Java/JDBC 类型、转换字段名、判定 autoFill
- 查找单列主键(无主键 / 复合主键 → error,快速失败)
- 判定 IsWithAutoFill(任意字段有 autoFill 注解)
- 填充 TemplateData 所有字段
错误情形:
- 未知方言
- 方言无对应 TypeMapper
- 无主键 / 复合主键(框架 @TableId 仅支持单列主键)