generator

package
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 20, 2026 License: MIT Imports: 13 Imported by: 0

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

Constants

This section is empty.

Variables

View Source
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

func ResolveResourcesRoot(javaRoot, configured string) string

ResolveResourcesRoot 由 java 源根派生 resources 根:把末段 src/main/java 换成 src/main/resources。 若 configured 非空则直接用它(配置优先于约定,便于非标准项目结构覆盖)。

Go 小白知识点:filepath.Join 跨平台拼路径;strings.HasSuffix 检查字符串是否以某子串结尾。 拼路径永远用 filepath.Join,不要手写 "/" 或 "\\"——Windows/Unix 共用同一套代码。

func SelectLayers

func SelectLayers(onlyTableModify, withoutApi bool) []string

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 遍历是随机序,切片遍历是稳定序)。

func SetSince

func SetSince(s string)

SetSince 供 main 包调用,设置生成文件的日期戳(yyyy-MM-dd)。 测试时无需调用——since="" 时模板里 @since 注释为空白,不影响功能正确性。

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
	// ServiceName Feign 服务名(注册中心应用名),填入 @FeignClient(name=...)。来自 config api.service-name。
	ServiceName string
	// BasePath 所有 API 端点的基础路径前缀,如 /admin-api/hello。来自 config api.base-path。
	BasePath 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)。

步骤:

  1. 解析方言,获取 TypeMapper(类型映射器)
  2. 遍历 Columns,映射 Java/JDBC 类型、转换字段名、判定 autoFill
  3. 查找单列主键(无主键 / 复合主键 → error,快速失败)
  4. 判定 IsWithAutoFill(任意字段有 autoFill 注解)
  5. 填充 TemplateData 所有字段

错误情形:

  • 未知方言
  • 方言无对应 TypeMapper
  • 无主键 / 复合主键(框架 @TableId 仅支持单列主键)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL