Skip to content

MyBatis-Flex 代码生成器

简介

在 mybatis-flex 中,有了一个名称为 mybatis-flex-codegen 的模块,提供了可以通过数据库表,生成代码的功能。当我们把数据库表设计完成后, 就可以使用其快速生成 Entity、 Mapper、 Service、 Controller 等产物。

除此之外,我们还可以通过扩展生成更多的产物,文档参考 #添加其他产物的生成

快速开始

在使用之前,我们需要先添加 mybatis-flex-codegen 的 Maven 依赖:

xml
<dependency>
    <groupId>com.mybatis-flex</groupId>
    <artifactId>mybatis-flex-codegen</artifactId>
    <version>1.8.3</version>
</dependency>

同时需要添加数据源的 Maven 依赖和 jdbc 驱动依赖:

xml
<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
    <version>4.0.3</version>
</dependency>

<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <version>8.0.32</version>
</dependency>

然后,编写一个任意带有 main 方法的类,如下所示:

java
public class Codegen {

    public static void main(String[] args) {
        //配置数据源
        HikariDataSource dataSource = new HikariDataSource();
        dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/your-database?characterEncoding=utf-8");
        dataSource.setUsername("root");
        dataSource.setPassword("******");

        //创建配置内容,两种风格都可以。
        GlobalConfig globalConfig = createGlobalConfigUseStyle1();
        //GlobalConfig globalConfig = createGlobalConfigUseStyle2();

        //通过 datasource 和 globalConfig 创建代码生成器
        Generator generator = new Generator(dataSource, globalConfig);

        //生成代码
        generator.generate();
    }

    public static GlobalConfig createGlobalConfigUseStyle1() {
        //创建配置内容
        GlobalConfig globalConfig = new GlobalConfig();

        //设置根包
        globalConfig.setBasePackage("com.test");

        //设置表前缀和只生成哪些表
        globalConfig.setTablePrefix("tb_");
        globalConfig.setGenerateTable("tb_account", "tb_account_session");

        //设置生成 entity 并启用 Lombok
        globalConfig.setEntityGenerateEnable(true);
        globalConfig.setEntityWithLombok(true);
        //设置项目的JDK版本,项目的JDK为14及以上时建议设置该项,小于14则可以不设置
        globalConfig.setJdkVersion(17);

        //设置生成 mapper
        globalConfig.setMapperGenerateEnable(true);

        //可以单独配置某个列
        ColumnConfig columnConfig = new ColumnConfig();
        columnConfig.setColumnName("tenant_id");
        columnConfig.setLarge(true);
        columnConfig.setVersion(true);
        globalConfig.setColumnConfig("tb_account", columnConfig);

        return globalConfig;
    }

    public static GlobalConfig createGlobalConfigUseStyle2() {
        //创建配置内容
        GlobalConfig globalConfig = new GlobalConfig();

        //设置根包
        globalConfig.getPackageConfig()
                .setBasePackage("com.test");

        //设置表前缀和只生成哪些表,setGenerateTable 未配置时,生成所有表
        globalConfig.getStrategyConfig()
                .setTablePrefix("tb_")
                .setGenerateTable("tb_account", "tb_account_session");

        //设置生成 entity 并启用 Lombok
        globalConfig.enableEntity()
                .setWithLombok(true)
                .setJdkVersion(17);

        //设置生成 mapper
        globalConfig.enableMapper();

        //可以单独配置某个列
        ColumnConfig columnConfig = new ColumnConfig();
        columnConfig.setColumnName("tenant_id");
        columnConfig.setLarge(true);
        columnConfig.setVersion(true);
        globalConfig.getStrategyConfig()
                .setColumnConfig("tb_account", columnConfig);

        return globalConfig;
    }
}

注意:由于 MyBatis-Flex 的 APT 功能会自动帮我们生成了 Mapper 的 Java 类,如果我们在代码生成器中选择生成 Mapper, 则建议把 APT 的 Mapper 生成功能给关闭掉,否则系统中会存在两份一样功能的 Mapper。

关闭 APT 的 Mapper 类文件生成,请参考:APT 设置章节

功能介绍

在 Mybatis-Flex 的代码生成器中,支持如下 8 种类型的的产物生成:

  • Entity 实体类
  • Mapper 映射类
  • TableDef 表定义辅助类
  • Service 服务类
  • ServiceImpl 服务实现类
  • Controller 控制类
  • MapperXml 文件
  • package-info.java 文件

除此之外,我们可以添加扩展生成更多类型的产物,文档参考 #添加其他产物的生成

启用或关闭某种类型产物的生成,代码如下:

java
// 开启 Entity 的生成
globalConfig.enableEntity();
// 关闭 Entity 的生成
globalConfig.disableEntity();

所有方法均支持链式调用配置,代码如下:

java
// 设置生成 Entity 并启用 Lombok、设置父类
globalConfig.enableEntity()
        .setWithLombok(true)
        .setSuperClass(BaseEntity.class);

全局配置 GlobalConfig

详细配置

获取配置描述
getJavadocConfig()注释配置
getPackageConfig()包配置
getStrategyConfig()策略配置
getTemplateConfig()模板配置
getEntityConfig()Entity 生成配置
getMapperConfig()Mapper 生成配置
getServiceConfig()Service 生成配置
getServiceImplConfig()ServiceImpl 生成配置
getControllerConfig()Controller 生成配置
getTableDefConfig()TableDef 生成配置
getMapperXmlConfig()MapperXml 生成配置
java
globalConfig.getPackageConfig()
        .setSourceDir("D://files/java")
        .setBasePackage("com.your.domain");
启用生成描述
enableEntity()启用 Entity 生成
enableMapper()启用 Mapper 生成
enableService()启用 Service 生成
enableServiceImpl()启用 ServiceImpl 生成
enableController()启用 Controller 生成
enableTableDef()启用 TableDef 生成
enableMapperXml()启用 MapperXml 生成

启用生成之后可以继续链式进行配置,例如:

java
// 设置生成 Entity 并启用 Lombok、设置父类
globalConfig.enableEntity()
        .setWithLombok(true)
        .setSuperClass(BaseEntity.class);

注释配置 JavadocConfig

配置描述默认值
setAuthor(String)作者System.getProperty("user.name")
setSince(String)日期(yyyy-MM-dd)
setTableCommentFormat(Function)表注释格式化原表注释
setColumnCommentFormat(Function)字段注释格式化原字段注释
setEntityPackage(String)Entity 包注释"实体类层(Entity)软件包。"
setMapperPackage(String)Mapper 包注释"映射层(Mapper)软件包。"
setServicePackage(String)Service 包注释"服务层(Service)软件包。"
setServiceImplPackage(String)ServiceImpl 包注释"服务层实现(ServiceImpl)软件包。"
setControllerPackage(String)Controller 包注释"控制层(Controller)软件包。"
setTableDefPackage(String)TableDef 包注释"表定义层(TableDef)软件包。"
java
globalConfig.getJavadocConfig()
        .setAuthor("Your Name")
        .setSince("1.0.1");

包配置 PackageConfig

配置描述默认值
setSourceDir(String)文件输出目录System.getProperty("user.dir") + "/src/main/java"
setBasePackage(String)根包名"com.mybatisflex"
setEntityPackage(String)Entity 包名getBasePackage() + ".entity"
setMapperPackage(String)Mapper 包名getBasePackage() + ".mapper"
setServicePackage(String)Service 包名getBasePackage() + ".service"
setServiceImplPackage(String)ServiceImpl 包名getBasePackage() + ".service.impl"
setControllerPackage(String)Controller 包名getBasePackage() + ".controller"
setTableDefPackage(String)TableDef 包名getEntityPackage() + ".tables"
setMapperXmlPath(String)MapperXml 路径System.getProperty("user.dir") + "/src/main/resources/mapper"
java
globalConfig.getPackageConfig()
        .setSourceDir("D:\\files\\java")
        .setBasePackage("com.your.domain");

策略配置 StrategyConfig

配置描述默认值
setTablePrefix(String)数据库表前缀,多个前缀用英文逗号(,) 隔开null
setLogicDeleteColumn(String)逻辑删除的默认字段名称null
setVersionColumn(String)乐观锁的字段名称null
setGenerateForView(boolean)是否生成视图映射false
setTableConfig(TableConfig)单独为某张表添加独立的配置null
setColumnConfig(ColumnConfig)设置某个列的全局配置null
setGenerateSchema(String)生成哪个schema下的表null
setGenerateTables(String...)生成哪些表,白名单null
setUnGenerateTables(String...)不生成哪些表,黑名单null
setIgnoreColumns(String...)需要忽略的列,父类定义的字段null
java
globalConfig.getStrategyConfig()
        .setGenerateSchema("schema")
        .setTablePrefix("sys_")
        .setGenerateTables("sys_user","sys_dept");

setGenerateTablessetUnGenerateTables 未配置时,生成所有表。

模板配置 TemplateConfig

配置描述默认值
setTemplate(ITemplate)
setEntity(String)Entity 模板路径"/templates/enjoy/entity.tpl"
setMapper(String)Mapper 模板路径"/templates/enjoy/mapper.tpl"
setService(String)Service 模板路径"/templates/enjoy/service.tpl"
setServiceImpl(String)ServiceImpl 模板路径"/templates/enjoy/serviceImpl.tpl"
setController(String)Controller 模板路径"/templates/enjoy/controller.tpl"
setTableDef(String)TableDef 模板路径"/templates/enjoy/tableDef.tpl"
setMapperXml(String)MapperXml 模板路径"/templates/enjoy/mapperXml.tpl"
java
globalConfig.getTemplateConfig()
        .setTemplate(new FreeMarkerTemplate())
        .setEntity("D:\\your-template-file\\my-entity.tpl");

Entity 生成配置 EntityConfig

配置描述默认值
setClassPrefix(String)Entity 类的前缀""
setClassSuffix(String)Entity 类的后缀""
setSuperClass(Class)Entity 类的父类,可以自定义一些 BaseEntity 类null
setSuperClassFactory(Function<Table, Class>)Entity 类的父类工厂,可以用于对特定的 Class 设置父类,而非全部 Entity 的父类null
setOverwriteEnable(boolean)是否覆盖之前生成的文件false
setImplInterfaces(Class[])Entity 默认实现的接口Serializable.class
setWithLombok(boolean)Entity 是否使用 Lombok 注解false
setWithSwagger(boolean)Entity 是否使用 Swagger 注解false
setSwaggerVersion(EntityConfig.SwaggerVersion)Swagger 注解版本SwaggerVersion.FOX
setWithActiveRecord(boolean)是否生成 Active Record 模式的 Entityfalse
setDataSource(String)统一使用的数据源null
setJdkVersion(int)设置项目的jdk版本0
java
globalConfig.getEntityConfig()
        .setWithLombok(true)
        .setClassPrefix("My")
        .setClassSuffix("Entity")
        .setSuperClass(BaseEntity.class);

注意: setSuperClassFactory(Function<Table, Class>) 的优先级要大于 setSuperClass(Class),当两者同时配置时,setSuperClass(Class) 的配置无效。

setEntitySuperClassFactory 示例代码:

java
globalConfig.setEntitySuperClassFactory(table -> {

    // 在这里,可以通过 table 来指定对应 SuperClass
    // 返回 null,则表示不需要设置父类
    return null;
});

Mapper 生成配置 MapperConfig

配置描述默认值
setClassPrefix(String)Mapper 类的前缀""
setClassSuffix(String)Mapper 类的后缀"Mapper"
setSuperClass(Class)Mapper 类的父类BaseMapper.class
setOverwriteEnable(boolean)是否覆盖之前生成的文件false
setMapperAnnotation(boolean)是否生成 @Mapper 注解false
java
globalConfig.getMapperConfig()
        .setClassPrefix("My")
        .setClassSuffix("Mapper")
        .setSuperClass(BaseMapper.class);

Service 生成配置 ServiceConfig

配置描述默认值
setClassPrefix(String)Service 类的前缀""
setClassSuffix(String)Service 类的后缀"Service"
setSuperClass(Class)Service 类的父类IService.class
setOverwriteEnable(boolean)是否覆盖之前生成的文件false
java
globalConfig.getServiceConfig()
        .setClassPrefix("My")
        .setClassSuffix("Service")
        .setSuperClass(IService.class);

ServiceImpl 生成配置 ServiceImplConfig

配置描述默认值
setClassPrefix(String)ServiceImpl 类的前缀""
setClassSuffix(String)ServiceImpl 类的后缀"ServiceImpl"
setSuperClass(Class)ServiceImpl 类的父类ServiceImpl.class
setOverwriteEnable(boolean)是否覆盖之前生成的文件false
setCacheExample(boolean)是否添加缓存示例代码false
java
globalConfig.getServiceImplConfig()
        .setClassPrefix("My")
        .setClassSuffix("ServiceImpl")
        .setSuperClass(ServiceImpl.class);

Controller 生成配置 ControllerConfig

配置描述默认值
setClassPrefix(String)Controller 类的前缀""
setClassSuffix(String)Controller 类的后缀"Controller"
setSuperClass(Class)Controller 类的父类null
setOverwriteEnable(boolean)是否覆盖之前生成的文件false
setRestStyle(boolean)REST 风格的 Controllertrue
java
globalConfig.getControllerConfig()
        .setClassPrefix("My")
        .setClassSuffix("Controller")
        .setSuperClass(BaseController.class);

TableDef 生成配置 TableDefConfig

配置描述默认值
setClassPrefix(String)TableDef 类的前缀""
setClassSuffix(String)TableDef 类的后缀"TableDef"
setOverwriteEnable(boolean)是否覆盖之前生成的文件false
setPropertiesNameStyle(TableDefConfig.NameStyle)生成辅助类的字段风格TableDefConfig.NameStyle.LOWER_CAMEL_CASE
setInstanceSuffix(String)生成辅助类常量对应的变量后缀""
java
globalConfig.getTableDefConfig()
        .setClassPrefix("My")
        .setClassSuffix("Def");

MapperXml 生成配置 MapperXmlConfig

配置描述默认值
setFilePrefix(String)MapperXml 文件的前缀""
setFileSuffix(String)MapperXml 文件的后缀"Mapper"
setOverwriteEnable(boolean)是否覆盖之前生成的文件false
java
globalConfig.getMapperXmlConfig()
        .setFilePrefix("My")
        .setFileSuffix("Mapper");

表配置 TableConfig

TableConfig 支持的配置如下:

java
public class TableConfig {

    /**
     * 表名。
     */
    private String tableName;

    /**
     * 数据库的 schema(模式)。
     */
    private String schema;

    /**
     * 默认为 驼峰属性 转换为 下划线字段。
     */
    private Boolean camelToUnderline;

    /**
     * 监听 entity 的 insert 行为。
     */
    private Class<? extends InsertListener> insertListenerClass;

    /**
     * 监听 entity 的 update 行为。
     */
    private Class<? extends UpdateListener> updateListenerClass;

    /**
     * 监听 entity 的查询数据的 set 行为。
     */
    private Class<? extends SetListener> setListenerClass;

    /**
     * 对应列的配置。
     */
    private Map<String, ColumnConfig> columnConfigMap;

    /**
     * 是否开启 Mapper 生成。
     */
    private Boolean mapperGenerateEnable = Boolean.TRUE;

}

列配置 ColumnConfig

ColumnConfig 支持的配置如下:

java
public class ColumnConfig implements Serializable {

    /**
     * 字段名称。
     */
    private String columnName;

    /**
     * insert 的时候默认值,这个值会直接被拼接到 sql 而不通过参数设置。
     */
    private String onInsertValue;

    /**
     * update 的时候自动赋值,这个值会直接被拼接到 sql 而不通过参数设置。
     */
    private String onUpdateValue;

    /**
     * 是否是大字段,大字段 APT 不会生成到 DEFAULT_COLUMNS 里。
     */
    private Boolean isLarge;

    /**
     * 是否是逻辑删除字段,一张表中只能存在 1 一个逻辑删除字段。
     */
    private Boolean isLogicDelete;

    /**
     * 是否为乐观锁字段。
     */
    private Boolean version;

    /**
     * 配置的 jdbcType。
     */
    private JdbcType jdbcType;

    /**
     * <p>属性的类型。
     *
     * <p>原始类型直接写类型名称,例如:int/long/float/double/boolean<br/>
     * 对象类型请写对应类的全限定名,例如:java.lang.String/com.example.enums.Gender
     */
    private String propertyType;

    /**
     * 属性的默认值,例如:long 类型默认值:0L,枚举类型默认值:Gender.MALE。
     */
    private String propertyDefaultValue;

    /**
     * 自定义 TypeHandler。
     */
    private Class<? extends TypeHandler> typeHandler;

    /**
     * 脱敏方式。
     */
    private String mask;

    /**
     * 字段是否为主键。
     */
    private boolean isPrimaryKey = false;

    /**
     * ID 生成策略。
     */
    private KeyType keyType;

    /**
     * ID 生成器值。
     */
    private String keyValue;

    /**
     * sequence 序列执行顺序。
     */
    private Boolean keyBefore;

    /**
     * 是否是租户 ID。
     */
    private Boolean tenantId;

}

自定 Entity 的义属性类型

方式 1:通过 JdbcTypeMapping

MyBatis-Flex 内置了一个名为:JdbcTypeMapping 的 java 类,我们可以用其配置映射 Jdbc 驱动的数据类型为自定义的 数据类型,在开始生成代码之前,可以先调用其进行配置,例如:

java
JdbcTypeMapping.registerMapping(LocalDateTime.class, Date.class);

那么,当我们生成代码的时候,发现 JDBC 驱动的数据类型为 LocalDateTime,则 Entity 对应的属性类型为 Date

方式 2:通过 JdbcTypeMapper

示例代码如下:

java
JdbcTypeMapping.setTypeMapper(new JdbcTypeMapping.JdbcTypeMapper() {
    @Override
    public String getType(String jdbcType, Table table, Column column) {
        if (table.getName().equals("tb_sys_permission")
            && column.getName().equals("type")){
            return PermissionType.class.getName();
        }
        return null;
    }
});

在以上的示例中,如果表名为 tb_sys_permission 且 列名为 type,生成的 Entity 的属性类型为 PermissionType

注意,通过 JdbcTypeMapper 设置的优先级要高于 JdbcTypeMapping.registerMapping 设置的内容。

方式 3:使用 ColumnConfig 定义

如下方示例代码所示:

java
ColumnConfig columnConfig = new ColumnConfig();

//定义该属性的类型为 java.util.List<String>
columnConfig.setPropertyType("java.util.List<String>");
columnConfig.setTypeHandler(CommaSplitTypeHandler.class);
columnConfig.setColumnName("your_column_name");

GlobalConfig globalConfig = new GlobalConfig();
globalConfig.setColumnConfig("your_table_name", columnConfig);

Generator generator = new Generator(dataSource, globalConfig);
generator.generate();

自定义代码模板

通过 GlobalConfig(全局配置)的 setTemplateEngine() 方法,可以配置自己的模板引擎以及模板,以下是内置的 EnjoyTemplate 的代码示例:

java
public class EnjoyTemplate implements ITemplate {

    private Engine engine;

    public EnjoyTemplate() {
        Engine engine = Engine.use(engineName);
        if (engine == null) {
            engine = Engine.create(engineName, e -> {
                e.addSharedStaticMethod(StringUtil.class);
                e.setSourceFactory(new FileAndClassPathSourceFactory());
            });
        }
        this.engine = engine;

        // 以下配置将支持 user.girl 表达式去调用 user 对象的 boolean isGirl() 方法
        Engine.addFieldGetterToFirst(new FieldGetters.IsMethodFieldGetter());
    }

    @Override
    public void generate(Map<String, Object> params, String templateFilePath, File generateFile) {
        if (!generateFile.getParentFile().exists() && !generateFile.getParentFile().mkdirs()) {
            throw new IllegalStateException("Can not mkdirs by dir: " + generateFile.getParentFile());
        }
        // 开始生成文件
        try (FileOutputStream fileOutputStream = new FileOutputStream(generateFile)) {
            engine.getTemplate(templateFilePath).render(params, fileOutputStream);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

自定义数据方言

在 MyBatis-Flex 的代码生成器中,已经内置了 4 种方言,他们分别是:

  • 默认方言
  • MySQL 方言
  • Oracle 方言
  • SQLite 方言

方言可以通过如下的方式进行使用:

java
Generator generator = new Generator(dataSource
    , globalConfig
    , IDialect.ORACLE); //使用哪个方言

generator.generate();

不传入方言的情况下,使用默认方言。

针对不同的数据库,我们也可以通过自定义方言来实现代码生成,例如:

MyDialect.java

java
class MyDialect implements IDialect {
   //重写相关构建方法
}

开始使用 MyDialect

java
Generator generator = new Generator(dataSource
    , globalConfig
    , new MyDialect()); //使用哪个方言

generator.generate();

添加其他产物的生成

通过实现 IGenerator 来实现,比如 Entity 实体类的代码如下:

java
public class EntityGenerator implements IGenerator {

    private String templatePath = "/templates/enjoy/entity.tpl";

    @Override
    public void generate(Table table, GlobalConfig globalConfig) {

        if (!globalConfig.isEntityGenerateEnable()) {
            return;
        }

        PackageConfig packageConfig = globalConfig.getPackageConfig();
        EntityConfig entityConfig = globalConfig.getEntityConfig();

        String entityPackagePath = packageConfig.getEntityPackage().replace(".", "/");
        File entityJavaFile = new File(packageConfig.getSourceDir(), entityPackagePath + "/" +
                table.buildEntityClassName() + ".java");


        if (entityJavaFile.exists() && !entityConfig.isOverwriteEnable()) {
            return;
        }


        Map<String, Object> params = new HashMap<>(4);
        params.put("table", table);
        params.put("entityConfig", entityConfig);
        params.put("packageConfig", packageConfig);
        params.put("javadocConfig", globalConfig.getJavadocConfig());

        globalConfig.getTemplateConfig().getTemplate().generate(params, templatePath, entityJavaFile);
    }
}

如果我们想生成其他产物,比如 html ,可以通过编写自己的类,来实现 IGenerator 接口,例如:

java
public class HtmlGenerator implements IGenerator {

    @Override
    public void generate(Table table, GlobalConfig globalConfig) {
        //在这里生成 html 代码
    }
}

最后,通过 GeneratorFactory 来注册 HtmlGenerator 即可:

java
GeneratorFactory.registerGenerator("html",new HtmlGenerator());

注意事项!!!

在 MySQL 或者 Oracle 的某些版本中,代码生成器可能无法获取 或者 字段 的注释内容,我们在数据源配置时,注意添加如下的配置信息:

MySQL

JdbcUrl 上注意添加 useInformationSchema=true 配置,如下代码所示:

java
HikariDataSource dataSource = new HikariDataSource();

//注意:url 需添加上 useInformationSchema=true 才能正常获取表的注释
dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/db?useInformationSchema=true&characterEncoding=utf-8");
dataSource.setUsername("username");
dataSource.setPassword("password");

Oracle

JdbcUrl 上注意添加 remarksReporting=true 配置,如下代码所示:

java
HikariDataSource dataSource = new HikariDataSource();

//注意:url 需添加上 remarksReporting=true 才能正常获取表的注释
dataSource.setJdbcUrl("jdbc:oracle:thin:@localhost:1521:orcl?remarksReporting=true");
dataSource.setUsername("username");
dataSource.setPassword("password");