一、核心概念

1. 模板引擎

  • 作用:将数据库元数据与模板结合,动态生成代码文件
  • 核心组件
    • 模板文件(.ftl/.vm):包含代码结构和占位符
    • 数据模型:包含表结构、字段信息等元数据
    • 引擎处理器:将数据注入模板生成最终代码

2. MyBatis-Plus支持

  • 内置引擎
    • FreemarkerTemplateEngine(推荐)
    • VelocityTemplateEngine
    • BeetlTemplateEngine
  • 默认模板路径resources/templates
  • 文件扩展名
    • Freemarker: .ftl
    • Velocity: .vm

二、详细操作步骤

步骤1:添加依赖

<!-- Freemarker 依赖 -->
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.31</version>
</dependency>

<!-- Velocity 依赖 -->
<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity-engine-core</artifactId>
    <version>2.3</version>
</dependency>

<!-- MyBatis-Plus 生成器 -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.5.3.1</version>
</dependency>

步骤2:配置模板引擎

// Freemarker 配置
AutoGenerator generator = new AutoGenerator();
generator.setTemplateEngine(new FreemarkerTemplateEngine());

// 或 Velocity 配置
// generator.setTemplateEngine(new VelocityTemplateEngine());

// 自定义模板路径(可选)
TemplateConfig templateConfig = new TemplateConfig();
templateConfig.setEntity("templates/my-entity.java.ftl");  // 自定义实体模板
generator.setTemplate(templateConfig);

步骤3:创建自定义模板

Freemarker模板示例 (resources/templates/my-entity.java.ftl)

package ${package.Entity};

<#list table.importPackages as pkg>
import ${pkg};
</#list>

/**
 * <p>
 * ${table.comment!}
 * </p>
 */
@Data
@TableName("${table.name}")
public class ${entity} implements Serializable {

    private static final long serialVersionUID = 1L;

<#-- 主键字段 -->
<#list table.fields as field>
    <#if field.keyFlag>
    @TableId(value = "${field.name}", type = IdType.${idType})
    </#if>
</#list>

<#-- 普通字段 -->
<#list table.commonFields as field>
    /**
     * ${field.comment!}
     */
    @TableField("${field.name}")
    private ${field.propertyType} ${field.propertyName};
</#list>

<#-- 版本控制字段 -->
<#if table.versionFieldName??>
    @Version
    private Integer version;
</#if>

<#-- 逻辑删除字段 -->
<#if table.logicDeleteFieldName??>
    @TableLogic
    private Integer deleted;
</#if>
}

步骤4:配置生成器使用自定义模板

public class CodeGenerator {
    public static void main(String[] args) {
        // ... [数据源配置、全局配置省略] ...
        
        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setEntityLombokModel(true);
        strategy.setInclude("user", "order"); 
        
        // 模板配置
        TemplateConfig templateConfig = new TemplateConfig();
        templateConfig.setEntity("templates/my-entity.java.ftl"); // 自定义实体模板
        templateConfig.setMapper("templates/mapper.java.ftl");    // 自定义Mapper模板
        
        AutoGenerator generator = new AutoGenerator();
        generator.setTemplate(templateConfig);
        generator.setTemplateEngine(new FreemarkerTemplateEngine());
        
        // ... [执行生成] ...
    }
}

步骤5:模板变量参考

变量名 描述 示例值
package.Entity 实体类包名 com.example.entity
entity 实体类名 User
table.name 数据库表名 tbl_user
table.comment 表注释 用户信息表
field.name 数据库字段名 user_name
field.propertyName 实体类属性名 userName
field.propertyType 属性类型 String
field.comment 字段注释 用户名

三、常见错误及解决方案

1. 模板文件找不到

  • 错误信息TemplateNotFoundException
  • 原因:模板路径配置错误
  • 解决
    // 使用 classpath: 前缀
    templateConfig.setEntity("classpath:/templates/entity.ftl");
    

2. 模板变量解析失败

  • 错误信息InvalidReferenceException
  • 原因:使用了未定义的变量
  • 解决
    <#-- 安全访问变量 -->
    ${table.comment!''}
    
    <#-- 检查变量是否存在 -->
    <#if table.comment??>
      ${table.comment}
    </#if>
    

3. 生成文件编码问题

  • 现象:中文乱码
  • 解决
    // 全局配置中设置编码
    globalConfig.setCharset("UTF-8");
    
    // 模板引擎设置编码(Velocity)
    Properties props = new Properties();
    props.put("input.encoding", "UTF-8");
    props.put("output.encoding", "UTF-8");
    generator.setTemplateEngine(new VelocityTemplateEngine(props));
    

4. 模板缓存问题

  • 现象:修改模板后生成结果未更新
  • 解决
    // Freemarker 配置(开发环境禁用缓存)
    Configuration cfg = new Configuration(Configuration.VERSION_2_3_31);
    cfg.setTemplateUpdateDelayMilliseconds(0); // 立即检查更新
    generator.setTemplateEngine(new FreemarkerTemplateEngine(cfg));
    

四、注意事项

1. 模板路径优先级

  • 自定义路径 > resources/templates > 内置模板
  • 未配置的模板使用默认生成器

2. 多模板引擎共存

// 不同文件类型使用不同引擎
generator.setTemplateEngine(new MultiTemplateEngine()
    .register("ftl", new FreemarkerTemplateEngine())
    .register("vm", new VelocityTemplateEngine()));

3. 敏感信息处理

<#-- 避免输出密码字段 -->
<#list table.fields as field>
  <#if field.name?contains("password")>
    @TableField(exist = false)
  <#else>
    @TableField("${field.name}")
  </#if>
  private ${field.propertyType} ${field.propertyName};
</#list>

五、高级使用技巧

1. 自定义模板函数

// 注册自定义方法
public class CustomFreemarkerEngine extends FreemarkerTemplateEngine {
    @Override
    public void init(Configuration config) {
        config.setSharedVariable("toCamelCase", new TemplateMethodModelEx() {
            public Object exec(List args) {
                return NamingStrategy.capitalFirst(NamingStrategy.underlineToCamel((String) args.get(0)));
            }
        });
    }
}

// 模板中使用
${toCamelCase('user_name')} // 输出: userName

2. 条件生成逻辑

<#-- 根据字段类型添加校验注解 -->
<#list table.fields as field>
  <#if field.propertyType == "String">
    @Size(max = 50, message = "${field.comment}长度不能超过50")
  <#elseif field.propertyType == "Integer">
    @Min(value = 0, message = "${field.comment}不能小于0")
  </#if>
  private ${field.propertyType} ${field.propertyName};
</#list>

3. 多文件输出

<#-- 生成DTO文件 -->
<#assign dtoPackage = package.Entity?replace(".entity", ".dto")>
package ${dtoPackage};

public class ${entity}DTO {
    <#-- 只包含部分字段 -->
    private String username;
    private String email;
}

六、最佳实践与性能优化

1. 模板组织策略

resources/
└── templates/
    ├── entity/       # 实体模板
    │   ├── base.ftl  # 基础模板
    │   └── ext.ftl   # 扩展模板
    ├── mapper/       # Mapper模板
    └── common/       # 公共片段
        └── header.ftl

2. 模板片段复用

<#-- header.ftl -->
/**
 * 生成时间: ${.now?string("yyyy-MM-dd HH:mm:ss")}
 * 表名: ${table.name}
 */

<#-- entity.ftl -->
<#include "common/header.ftl">
package ${package.Entity};

public class ${entity} {
    // ...
}

3. 模板预编译与缓存

// 生产环境启用模板缓存
Configuration cfg = new Configuration(Configuration.VERSION_2_3_31);
cfg.setTemplateUpdateDelayMilliseconds(3600000); // 1小时更新检查

// 使用模板缓存管理器
TemplateCacheManager cacheManager = new TemplateCacheManager();
cacheManager.setCacheStorage(new StrongCacheStorage());
generator.setTemplateEngine(new FreemarkerTemplateEngine(cfg, cacheManager));

4. 批量生成优化

// 分批次处理大表
List<String> tables = Arrays.asList("user", "order", "product");
int batchSize = 10;

for (int i = 0; i < tables.size(); i += batchSize) {
    List<String> batch = tables.subList(i, Math.min(i + batchSize, tables.size()));
    strategy.setInclude(batch.toArray(new String[0]));
    generator.execute();
}

七、性能对比(Freemarker vs Velocity)

特性 Freemarker Velocity
语法复杂度 ★★★ (较复杂) ★★ (简单)
性能 ★★★ (快) ★★ (中等)
错误处理 ★★★ (详细错误信息) ★★ (基本错误信息)
模板继承 支持 不支持
宏定义 强大 有限
社区活跃度 ★★★ (活跃) ★★ (维护中)
推荐场景 复杂模板、企业级应用 简单模板、快速开发

八、完整配置示例

public class AdvancedGenerator {
    public static void main(String[] args) {
        // 1. 全局配置
        GlobalConfig globalConfig = new GlobalConfig();
        globalConfig.setOutputDir(System.getProperty("user.dir") + "/src/main/java");
        globalConfig.setAuthor("Generator");
        globalConfig.setOpen(false);
        globalConfig.setFileOverride(true);
        globalConfig.setSwagger2(true);
        globalConfig.setDateType(DateType.TIME_PACK);
        
        // 2. 数据源配置
        DataSourceConfig dataSource = new DataSourceConfig();
        dataSource.setUrl("jdbc:mysql://localhost:3306/db?useSSL=false");
        dataSource.setDriverName("com.mysql.cj.jdbc.Driver");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        
        // 3. 包配置
        PackageConfig packageConfig = new PackageConfig();
        packageConfig.setParent("com.example.project");
        packageConfig.setEntity("entity");
        packageConfig.setMapper("mapper");
        
        // 4. 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setEntityLombokModel(true);
        strategy.setRestControllerStyle(true);
        strategy.setInclude("user", "product");
        strategy.setControllerMappingHyphenStyle(true);
        strategy.setTablePrefix("tbl_");
        strategy.setEntitySerialVersionUID(true);
        strategy.setEntityBuilderModel(true);
        
        // 5. 自定义模板配置
        TemplateConfig templateConfig = new TemplateConfig();
        templateConfig.setEntity("templates/advanced-entity.java.ftl");
        templateConfig.setMapper("templates/mapper.java.ftl");
        templateConfig.setXml(null); // 不生成XML
        
        // 6. 自定义注入配置
        InjectionConfig injectionConfig = new InjectionConfig() {
            @Override
            public void initMap() {
                Map<String, Object> map = new HashMap<>();
                map.put("company", "Acme Corp");
                this.setMap(map);
            }
        };
        
        // 7. 配置模板引擎
        Configuration freemarkerCfg = new Configuration(Configuration.VERSION_2_3_31);
        freemarkerCfg.setDefaultEncoding("UTF-8");
        freemarkerCfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
        
        // 8. 构建生成器
        AutoGenerator generator = new AutoGenerator();
        generator.setGlobalConfig(globalConfig);
        generator.setDataSource(dataSource);
        generator.setPackageInfo(packageConfig);
        generator.setStrategy(strategy);
        generator.setTemplate(templateConfig);
        generator.setCfg(injectionConfig);
        generator.setTemplateEngine(new CustomFreemarkerEngine(freemarkerCfg));
        
        // 9. 执行生成
        generator.execute();
    }
    
    static class CustomFreemarkerEngine extends FreemarkerTemplateEngine {
        public CustomFreemarkerEngine(Configuration configuration) {
            super(configuration);
        }
        
        @Override
        public void init(Configuration config) {
            config.setSharedVariable("currentYear", new SimpleNumber(Year.now().getValue()));
        }
    }
}

总结

关键实践要点

  1. 模板分层:基础模板 + 业务模板组合使用
  2. 安全处理:对敏感字段特殊处理
  3. 性能优化:生产环境启用模板缓存
  4. 错误防御:模板中使用空值处理(!)
  5. 版本管理:模板纳入Git版本控制

选择建议

  • 推荐 Freemarker:功能强大,适合复杂项目
  • 简单项目用 Velocity:学习曲线平缓
  • 避免混用:统一项目中的模板引擎

通过合理使用模板引擎,MyBatis-Plus代码生成效率可提升300%,同时保持代码风格一致性。建议将通用模板封装为公司级代码生成组件,实现架构统一管理。