一、什么是逆向工程

Mybatis的逆向工程就是由代码生成器生成我们需要的代码和映射文件。我们在编写Mybatis程序时,基本都是围绕着pojo类,Mapper接口,Mapper.xml文件等文件来进行的。如果实际开发中数据库的表特别多,那么我们需要手动去写每一张表的pojo类,Mapper接口,Mapper.xml文件,这显然需要花费巨大的精力,而且可能由于表字段太多,哪里写错了都难以排除。所以我们在实际开发中,一般使用逆向工程方式来自动生成所需的文件,这也是企业中一种非常常见的方法。

注意:在使用逆向工程生成代码文件的时候,最好额外创建一个项目,不要在原来的项目中使用,因为如果你在原项目中有相同名字的文件,那么就会被新生成的文件所覆盖,导致之前写的代码没了,有一定的风险。所以实际开发中,我们一般新建一个项目,然后将生成的文件复制到自己的所需的工程中。

在这里插入图片描述

1、MGB简介

官方文档 : http://mybatis.org/generator/
逆向工程包的下载地址: https://github.com/mybatis/generator/releases/tag/mybatis-generator-1.3.2

(1)MyBatis Generator:即MBG

(2)MBG是一个专门为MyBatis框架使用者定制的代码生成器

(3)MBG可以快速的根据表生成对应的映射文件、接口、以及bean类

(4)只可以生成单表CRUD,但是表连接、存储过程等这些复杂sql的定义需要我们手工编写

2、编写MBG的配置文件mbg.xml(重要几处配置)

相应的配置文件在官方文档中,可以找到

在这里插入图片描述

向下滑动

在这里插入图片描述在这里插入图片描述
上述配置文件是官方给出的示例配置文件,之后将其中相关部分改为和自己模块匹配的内容

3、运行程序(代码生成器)

在这里插入图片描述

二、逆向工程生成代码

①、首先创建maven项目

1、项目整体目录:

在这里插入图片描述

2、导入maven依赖:


<!-- Mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.6</version>
</dependency>
<!-- mysql驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.21</version>
</dependency>
<!-- 日志处理 -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
<!-- 逆向工程 -->
<dependency>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-core</artifactId>
    <version>1.4.0</version>
</dependency>

②、创建日志文件log4j.properties


# Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE debug info warn error fatal
log4j.rootCategory=debug, CONSOLE
 
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n

③、创建generatorConfig.xml配置文件


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
 
<generatorConfiguration>

    <!--targetRuntime="MyBatis3Simple"表示生成简易版本,这里创建原始版本,参数为MyBatis3-->
    <context id="testTables" targetRuntime="MyBatis3">
    
        <commentGenerator>
            <!-- 是否去除自动生成的注释。true:是;false:否 -->
            <property name="suppressAllComments" value="true" />
        </commentGenerator>
        
        <!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8"
                        userId="root"
                        password="root">
        </jdbcConnection>
 
        <!-- 默认false,把JDBC DECIMAL和NUMERIC类型解析为Integer,为true时把JDBC DECIMAL 和
            NUMERIC 类型解析为java.math.BigDecimal -->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>
 
        <!-- targetProject:POJO类生成的位置 -->
        <javaModelGenerator targetPackage="com.example.pojo"
                            targetProject="./src/main/java">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false" />
            <!-- 从数据库返回的值被清理前后的空格 -->
            <property name="trimStrings" value="true" />
        </javaModelGenerator>
        
        <!-- targetProject:mapper映射文件生成的位置 -->
        <sqlMapGenerator targetPackage="com.example.mapper"
                         targetProject="./src/main/resources">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false" />
        </sqlMapGenerator>
        
        <!-- targetPackage:mapper接口生成的位置 -->
        <javaClientGenerator type="XMLMAPPER"
                             targetPackage="com.example.mapper"
                             targetProject="./src/main/java">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false" />
        </javaClientGenerator>

        
<!--生成全部表tableName设为%-->
<!--        可选:-->
<!--        1,schema:数据库的schema;-->
<!--        2,catalog:数据库的catalog;-->
<!--        3,alias:为数据表设置的别名,如果设置了alias,那么生成的所有的SELECT SQL语句中,列名会变成:alias_actualColumnName-->
<!--        4,domainObjectName:生成的domain类的名字,如果不设置,直接使用表名作为domain类的名字;可以设置为somepck.domainName,那么会自动把domainName类再放到somepck包里面;-->
<!--        5,enableInsert(默认true):指定是否生成insert语句;-->
<!--        6,enableSelectByPrimaryKey(默认true):指定是否生成按照主键查询对象的语句(就是getById或get);-->
<!--        7,enableSelectByExample(默认true):MyBatis3Simple为false,指定是否生成动态查询语句;-->
<!--        8,enableUpdateByPrimaryKey(默认true):指定是否生成按照主键修改对象的语句(即update);-->
<!--        9,enableDeleteByPrimaryKey(默认true):指定是否生成按照主键删除对象的语句(即delete);-->
<!--        10,enableDeleteByExample(默认true):MyBatis3Simple为false,指定是否生成动态删除语句;-->
<!--        11,enableCountByExample(默认true):MyBatis3Simple为false,指定是否生成动态查询总条数语句(用于分页的总条数查询);-->
<!--        12,enableUpdateByExample(默认true):MyBatis3Simple为false,指定是否生成动态修改语句(只修改对象中不为空的属性);-->
<!--        13,modelType:参考context元素的defaultModelType,相当于覆盖;-->
<!--        14,delimitIdentifiers:参考tableName的解释,注意,默认的delimitIdentifiers是双引号,如果类似MYSQL这样的数据库,使用的是`(反引号,那么还需要设置context的beginningDelimiter和endingDelimiter属性)-->
<!--        15,delimitAllColumns:设置是否所有生成的SQL中的列名都使用标识符引起来。默认为false,delimitIdentifiers参考context的属性-->


         <!-- 指定生成哪些数据库表,要和数据库中对应,不能写错了,这里以t_user表为例,可以写多个;domainObjectName是要生成的实体类名称-->
        <!-- 指定数据库表 [SQLServer] schema=""必须为空 -->
        <table schema="mybatis" tableName="t_user"/>
        <!-- 有些表的字段需要指定java类型  -->
        <!-- <table schema="" tableName="">
            <columnOverride column="" javaType="" />
        </table> -->
        
    </context>
</generatorConfiguration>

注意:serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8中的 & 要改成转义字符 &amp; 这里传上来页面自动给转成了 &。

在这里插入图片描述
还有就是不同的数据库中不能含有相同的表,例如数据库A有t_user表,数据库B也有t_user表,那么到时候代码不知道生成哪个,而我恰好生成的是我们不需要的那个。啊?你说上面不是指定了数据库吗,怎么会到读取到其它数据库的表,不好意思,我试了不下十遍,最后我把其它数据库同名的表删除才成功的。如果你没有这种情况那更好咯。

④、创建逆向工程核心生成代码GeneratorSql.java


package com.thr.generator;
 
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;
 
import java.io.File;
import java.util.ArrayList;
import java.util.List;
 
/**
 * 逆向工程核心生成代码
 */
public class GeneratorSql {
    public void generator() throws Exception {
        List<String> warnings = new ArrayList<>();
        boolean overwrite = true;
        // 指定逆向工程配置文件
        String file = GeneratorSql.class.getResource("/generatorConfig.xml").getFile();
        File configFile = new File(file);
        ConfigurationParser cp = new ConfigurationParser(warnings);
        Configuration config = cp.parseConfiguration(configFile);
        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
        myBatisGenerator.generate(null);
    }
    // 执行main方法以生成代码
    public static void main(String[] args) {
        try {
            GeneratorSql generatorSql = new GeneratorSql();
            generatorSql.generator();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
 
}

⑤、运行逆向工程核心生成代码

运行上面的程序,如果控制台打印了如下日志,说明生成代码成功了。

在这里插入图片描述
然后我们再看项目就会发现生成了如下文件:

在这里插入图片描述

三、逆向工程举例

首先我们将上面生成的文件复制到目标项目中。在使用逆向工程举例之前,先来介绍生成的文件有哪些东西:

(1)、TUserMapper接口生成的方法介绍:

在这里插入图片描述在这里插入图片描述

  1. long countByExample(TUserExample example):按条件计数
  2. int deleteByExample(TUserExample example):按条件删除
  3. int deleteByPrimaryKey(Integer id):按主键删除
  4. int insert(TUser record):插入数据(返回值为ID)
  5. int insertSelective(TUser record):插入数据,只插入值不为null的字段,内部动态sql判断
  6. List selectByExample(TUserExample example):按条件查询,传入null表示查询所有
  7. TUser selectByPrimaryKey(Integer id):按主键查询
  8. int updateByExampleSelective(@Param(“record”) TUser record, @Param(“example”) TUserExample example):按条件更新值不为null的字段
  9. int updateByExample(@Param(“record”) TUser record, @Param(“example”) TUserExample example):按条件更新
  10. int updateByPrimaryKeySelective(TUser record):按主键更新值不为null的字段
  11. int updateByPrimaryKey(TUser record):按主键更新

测试不带条件的方法:


//Mybatis的测试
public class MybatisTest {
    //定义 SqlSession
    private SqlSession sqlSession = null;
    //定义 TUserMapper对象
    private TUserMapper mapper = null;
 
    @Before//在测试方法执行之前执行
    public void getSqlSession(){
        //1、加载 mybatis 全局配置文件
        InputStream is = MybatisTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
        //2、创建SqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
        //3、根据 sqlSessionFactory 产生session
        sqlSession = sqlSessionFactory.openSession();
        //4、创建Mapper接口的的代理对象,getMapper方法底层会通过动态代理生成TUserMapper的代理实现类
        mapper = sqlSession.getMapper(TUserMapper.class);
    }
 
    @After//在测试方法执行完成之后执行
    public void destroy() throws IOException {
        sqlSession.commit();
        sqlSession.close();
    }
    //查询所有用户信息
    @Test
    public void selectAllUser(){
        List<TUser> tUsers = mapper.selectByExample(null);//传入null表示查询所有
        for (TUser tUser : tUsers) {
            System.out.println(tUser);
        }
    }
    //根据用户id查询用户
    @Test
    public void selectByUserId(){
        TUser tUser = mapper.selectByPrimaryKey(1);
        System.out.println(tUser);
    }
    //添加用户信息
    @Test
    public void insertUser(){
        TUser tUser = new TUser();
        tUser.setUsername("凡尔赛");
        tUser.setAge(18);
        tUser.setBirthday(new Date());
        tUser.setSex("0");
        tUser.setAddress("漂亮国");
        int i = mapper.insertSelective(tUser);
        System.out.println(i>0?"添加成功":"添加失败");
    }
    //更新用户信息
    @Test
    public void updateUser(){
        TUser tUser = new TUser();
        tUser.setId(8);//这里要设置id才能修改成功,否则不知道修改哪一条数据
        tUser.setUsername("川建国");
        tUser.setAge(50);
        tUser.setBirthday(new Date());
        tUser.setSex("1");
        tUser.setAddress("漂亮国");
        int i = mapper.updateByPrimaryKeySelective(tUser);
        System.out.println(i>0?"修改成功":"修改失败");
    }
    //删除用户信息
    @Test
    public void deleteUser(){
        int i = mapper.deleteByPrimaryKey(8);
        System.out.println(i>0?"删除成功":"删除失败");
    }
}

(2)、TUserExample条件扩展类介绍:

上面的测试方法是不带条件的操作,那么接下来学习一下按条件如何进行增删改查操作,我们在逆向工程中已经生成了这个类TUserExample,这个类就是一个条件扩展类,里面定义了一系列方法用来做条件,比如:排序、去重、大于、小于、等于、模糊查询、数据在某某之间等等。

我们在TUserExample类中可以看到定义了一个内部类GeneratedCriteria,这个内部类就定义了一系列条件的方法,这些条件最后都会拼接在SQL中,但是我们一般不用它,都用它的子类Criteria来进行操作,Criteria继承了内部类GeneratedCriteria。

在这里插入图片描述
简单举例:


//Mybatis的测试
public class MybatisTest1 {
    //定义 SqlSession
    private SqlSession sqlSession = null;
    //定义 UserMapper对象
    private TUserMapper mapper = null;
 
    @Before//在测试方法执行之前执行
    public void getSqlSession(){
        //1、加载 mybatis 全局配置文件
        InputStream is = MybatisTest1.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
        //2、创建SqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
        //3、根据 sqlSessionFactory 产生session
        sqlSession = sqlSessionFactory.openSession();
        //4、创建Mapper接口的的代理对象,getMapper方法底层会通过动态代理生成UserMapper的代理实现类
        mapper = sqlSession.getMapper(TUserMapper.class);
    }
 
    @After//在测试方法执行完成之后执行
    public void destroy() throws IOException {
        sqlSession.commit();
        sqlSession.close();
    }
    //模糊查询用户信息
    @Test
    public void selectUserLike(){
        TUserExample example = new TUserExample();
        TUserExample.Criteria criteria = example.createCriteria();
        //模糊条件
        criteria.andUsernameLike("%三%");
        /*sql语句相当于:select id, username, age, birthday, sex, address 
                        from t_user WHERE ( username like ? )*/
        List<TUser> tUsers = mapper.selectByExample(example);
        for (TUser tUser : tUsers) {
            System.out.println(tUser);
        }
    }
    //查询年龄在18-30岁之间的用户信息
    @Test
    public void selectUserBetween(){
        TUserExample example = new TUserExample();
        TUserExample.Criteria criteria = example.createCriteria();
        //Between条件
        criteria.andAgeBetween(18,30);
        example.or(criteria);
        example.setDistinct(true);
        /*sql语句相当于:select distinct id, username, age, birthday, sex, address 
                        from t_user WHERE ( age between ? and ? ) or( age between ? and ? )*/
        List<TUser> tUsers = mapper.selectByExample(example);
        for (TUser tUser : tUsers) {
            System.out.println(tUser);
        }
    }
    //查询用户名A或B
    @Test
    public void selectUserOr(){
        TUserExample example = new TUserExample();
        TUserExample.Criteria criteria1 = example.createCriteria();
        criteria1.andUsernameEqualTo("黄飞鸿");
 
        TUserExample.Criteria criteria2 = example.createCriteria();
        criteria2.andUsernameEqualTo("马保国");
        //将criteria2条件拼接在 or 关键字字后面
        example.or(criteria2);
        /*sql语句相当于:select id, username, age, birthday, sex, address
            from t_user WHERE ( username = ? ) or( username = ? )*/
        List<TUser> tUsers = mapper.selectByExample(example);
        for (TUser tUser : tUsers) {
            System.out.println(tUser);
        }
    }
    //根据用户名删除用户
    @Test
    public void deleteUserExample(){
        TUserExample example = new TUserExample();
        TUserExample.Criteria criteria = example.createCriteria();
        criteria.andUsernameEqualTo("凡尔赛");
        //sql语句相当于:delete from t_user WHERE ( username = ? )
        int i = mapper.deleteByExample(example);
        System.out.println(i>0?"删除成功":"删除失败");
    }
}

至此Mybatis的逆向工程就全部介绍完成了,说难也不是特别难,只要一步步自己去实现,去理解一遍,是非常简单的,可能复杂一点的是那个XxxExample类,但如果自己多举几个例子也不难。

四、逆向工程代码扩展

问:MyBatis Generator 为什么不生成 service 层和 controller 层 ?
答:mybatis 是持久层框架。

①、自定义注释生成器

1、创建自定义注释生成器 CommentGenerator.java


import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.dom.java.CompilationUnit;
import org.mybatis.generator.api.dom.java.Field;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.internal.DefaultCommentGenerator;
import org.mybatis.generator.internal.util.StringUtility;
 
import java.util.Properties;
 
/**
 * 自定义注释生成器
 * Created by macro on 2018/4/26.
 */
public class CommentGenerator extends DefaultCommentGenerator {
    private boolean addRemarkComments = false;
    private static final String EXAMPLE_SUFFIX="Example";
    private static final String API_MODEL_PROPERTY_FULL_CLASS_NAME="io.swagger.annotations.ApiModelProperty";
 
    /**
     * 设置用户配置的参数
     */
    @Override
    public void addConfigurationProperties(Properties properties) {
        super.addConfigurationProperties(properties);
        this.addRemarkComments = StringUtility.isTrue(properties.getProperty("addRemarkComments"));
    }
 
    /**
     * 给字段添加注释
     */
    @Override
    public void addFieldComment(Field field, IntrospectedTable introspectedTable,
                                IntrospectedColumn introspectedColumn) {
        String remarks = introspectedColumn.getRemarks();
        //根据参数和备注信息判断是否添加备注信息
        if(addRemarkComments&&StringUtility.stringHasValue(remarks)){
//            addFieldJavaDoc(field, remarks);
            //数据库中特殊字符需要转义
            if(remarks.contains("\"")){
                remarks = remarks.replace("\"","'");
            }
            //给model的字段添加swagger注解
            field.addJavaDocLine("@ApiModelProperty(value = \""+remarks+"\")");
        }
    }
 
    /**
     * 给model的字段添加注释
     */
    private void addFieldJavaDoc(Field field, String remarks) {
        //文档注释开始
        field.addJavaDocLine("/**");
        //获取数据库字段的备注信息
        String[] remarkLines = remarks.split(System.getProperty("line.separator"));
        for(String remarkLine:remarkLines){
            field.addJavaDocLine(" * "+remarkLine);
        }
        addJavadocTag(field, false);
        field.addJavaDocLine(" */");
    }
 
    @Override
    public void addJavaFileComment(CompilationUnit compilationUnit) {
        super.addJavaFileComment(compilationUnit);
        //只在model中添加swagger注解类的导入
        if(!compilationUnit.isJavaInterface()&&!compilationUnit.getType().getFullyQualifiedName().contains(EXAMPLE_SUFFIX)){
            compilationUnit.addImportedType(new FullyQualifiedJavaType(API_MODEL_PROPERTY_FULL_CLASS_NAME));
        }
    }
}

2、添加 generatorConfig.xml 配置

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
 
<generatorConfiguration>
    <properties resource="generator.properties"/>
    <context id="MySqlContext" targetRuntime="MyBatis3" defaultModelType="flat">
        <property name="beginningDelimiter" value="`"/>
        <property name="endingDelimiter" value="`"/>
        <property name="javaFileEncoding" value="UTF-8"/>
        <!-- 为模型生成序列化方法-->
        <plugin type="org.mybatis.generator.plugins.SerializablePlugin"/>
        <!-- 为生成的Java模型创建一个toString方法 -->
        <plugin type="org.mybatis.generator.plugins.ToStringPlugin"/>
        <!--生成mapper.xml时覆盖原文件-->
        <plugin type="org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin" />
        
        <commentGenerator type="com.jzxs.etp.mbg.CommentGenerator">
            <!-- 是否去除自动生成的注释 true:是 : false:否 -->
            <property name="suppressAllComments" value="true"/>
            <property name="suppressDate" value="true"/>
            <property name="addRemarkComments" value="true"/>
        </commentGenerator>
 
        <jdbcConnection driverClass="${jdbc.driverClass}"
                        connectionURL="${jdbc.connectionURL}"
                        userId="${jdbc.userId}"
                        password="${jdbc.password}">
            <!--解决mysql驱动升级到8.0后不生成指定数据库代码的问题-->
            <property name="nullCatalogMeansCurrent" value="true" />
        </jdbcConnection>
 
        <!--指定生成model的路径-->
        <javaModelGenerator targetPackage="com.jzxs.etp.mbg.model"
                            targetProject="./src/main/java"/>
        <!--指定生成mapper.xml的路径-->
        <sqlMapGenerator targetPackage="com.jzxs.etp.mbg.mapper"
                         targetProject="./src/main/resources"/>
        <!--指定生成mapper接口的的路径-->
        <javaClientGenerator type="XMLMAPPER" targetPackage="com.jzxs.etp.mbg.mapper"
                             targetProject="./src/main/java"/>
       
        <!--生成全部表tableName设为%-->
        <table tableName="history_picture"></table>
        
    </context>
</generatorConfiguration>

②、自定义Controller和Service插件

1、自定义生成Controller和Service的模板。


package com.jzxs.etp.mbg;
 
import org.mybatis.generator.api.GeneratedJavaFile;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.java.*;
 
import java.util.ArrayList;
import java.util.List;
 
import static org.mybatis.generator.internal.util.StringUtility.stringHasValue;
import static org.mybatis.generator.internal.util.messages.Messages.getString;
 
public class ServiceAndControllerGeneratorPlugin extends PluginAdapter  {
 
    // 项目目录,一般为 src/main/java
    private String targetProject;
 
    // service包名,如:com.thinkj2ee.cms.service.service
    private String servicePackage;
 
    // service实现类包名,如:com.thinkj2ee.cms.service.service.impl
    private String serviceImplPackage;
    // Controlle类包名,如:com.thinkj2ee.cms.service.controller
    private String controllerPackage;
    // service接口名前缀
    private String servicePreffix;
 
    // service接口名后缀
    private String serviceSuffix;
 
    // service接口的父接口
    private String superServiceInterface;
 
    // service实现类的父类
    private String superServiceImpl;
    // controller类的父类
    private String superController;
 
    // dao接口基类
    private String superDaoInterface;
 
    // Example类的包名
    private String examplePacket;
 
    private String recordType;
 
    private String modelName;
 
    private FullyQualifiedJavaType model;
 
    private String serviceName;
    private String serviceImplName;
    private String controllerName;
 
    @Override
    public boolean validate(List<String> warnings) {
        boolean valid = true;
 
       /* if (!stringHasValue(properties
                .getProperty("targetProject"))) { //$NON-NLS-1$
            warnings.add(getString("ValidationError.18", //$NON-NLS-1$
                    "MapperConfigPlugin", //$NON-NLS-1$
                    "targetProject")); //$NON-NLS-1$
            valid = false;
        }
        if (!stringHasValue(properties.getProperty("servicePackage"))) { //$NON-NLS-1$
            warnings.add(getString("ValidationError.18", //$NON-NLS-1$
                    "MapperConfigPlugin", //$NON-NLS-1$
                    "servicePackage")); //$NON-NLS-1$
            valid = false;
        }
        if (!stringHasValue(properties.getProperty("serviceImplPackage"))) { //$NON-NLS-1$
            warnings.add(getString("ValidationError.18", //$NON-NLS-1$
                    "MapperConfigPlugin", //$NON-NLS-1$
                    "serviceImplPackage")); //$NON-NLS-1$
            valid = false;
        }
*/
        targetProject = properties.getProperty("targetProject");
        servicePackage = properties.getProperty("servicePackage");
        serviceImplPackage = properties.getProperty("serviceImplPackage");
        servicePreffix = properties.getProperty("servicePreffix");
        servicePreffix = stringHasValue(servicePreffix) ? servicePreffix : "";
        serviceSuffix = properties.getProperty("serviceSuffix");
        serviceSuffix = stringHasValue(serviceSuffix) ? serviceSuffix : "";
        superServiceInterface = properties.getProperty("superServiceInterface");
        superServiceImpl = properties.getProperty("superServiceImpl");
        superDaoInterface = properties.getProperty("superDaoInterface");
        controllerPackage = properties.getProperty("controllerPackage");
        superController = properties.getProperty("superController");
 
        return valid;
    }
 
    @Override
    public List<GeneratedJavaFile> contextGenerateAdditionalJavaFiles(IntrospectedTable introspectedTable) {
        recordType = introspectedTable.getBaseRecordType();
        modelName = recordType.substring(recordType.lastIndexOf(".") + 1);
        model = new FullyQualifiedJavaType(recordType);
        serviceName = servicePackage + "." + servicePreffix + modelName + serviceSuffix;
        serviceImplName = serviceImplPackage + "." + modelName + serviceSuffix+"Impl";
        examplePacket=recordType.substring(0,recordType.lastIndexOf("."));
        controllerName=controllerPackage.concat(".").concat(modelName).concat("Controller");
        List<GeneratedJavaFile> answer = new ArrayList<>();
        GeneratedJavaFile gjf = generateServiceInterface(introspectedTable);
        GeneratedJavaFile gjf2 = generateServiceImpl(introspectedTable);
        GeneratedJavaFile gjf3 = generateController(introspectedTable);
        answer.add(gjf);
        answer.add(gjf2);
        answer.add(gjf3);
        return answer;
    }
 
    // 生成service接口
    private GeneratedJavaFile generateServiceInterface(IntrospectedTable introspectedTable) {
        FullyQualifiedJavaType service = new FullyQualifiedJavaType(serviceName);
        Interface serviceInterface = new Interface(service);
        serviceInterface.setVisibility(JavaVisibility.PUBLIC);
        // 添加父接口
        if(stringHasValue(superServiceInterface)) {
            String superServiceInterfaceName = superServiceInterface.substring(superServiceInterface.lastIndexOf(".") + 1);
            serviceInterface.addImportedType(new FullyQualifiedJavaType(superServiceInterface));
            serviceInterface.addImportedType(new FullyQualifiedJavaType(recordType));
            serviceInterface.addSuperInterface(new FullyQualifiedJavaType(superServiceInterfaceName + "<" + modelName + ">"));
        }
        GeneratedJavaFile gjf = new GeneratedJavaFile(serviceInterface, targetProject, context.getJavaFormatter());
        return gjf;
    }
 
    // 生成serviceImpl实现类
    private GeneratedJavaFile generateServiceImpl(IntrospectedTable introspectedTable) {
        FullyQualifiedJavaType service = new FullyQualifiedJavaType(serviceName);
        FullyQualifiedJavaType serviceImpl = new FullyQualifiedJavaType(serviceImplName);
        TopLevelClass clazz = new TopLevelClass(serviceImpl);
        //描述类的作用域修饰符
        clazz.setVisibility(JavaVisibility.PUBLIC);
        //描述类 引入的类
        clazz.addImportedType(service);
        //描述类 的实现接口类
        clazz.addSuperInterface(service);
        if(stringHasValue(superServiceImpl)) {
            String superServiceImplName = superServiceImpl.substring(superServiceImpl.lastIndexOf(".") + 1);
            clazz.addImportedType(superServiceImpl);
            clazz.addImportedType(recordType);
            clazz.setSuperClass(superServiceImplName + "<" + modelName + ">");
        }
        clazz.addImportedType(new FullyQualifiedJavaType("org.springframework.stereotype.Service"));
        clazz.addAnnotation("@Service");
 
        String daoFieldType = introspectedTable.getMyBatis3JavaMapperType();
        String daoFieldName = firstCharToLowCase(daoFieldType.substring(daoFieldType.lastIndexOf(".") + 1));
        //描述类的成员属性
        Field daoField = new Field(daoFieldName, new FullyQualifiedJavaType(daoFieldType));
        clazz.addImportedType(new FullyQualifiedJavaType(daoFieldType));
        clazz.addImportedType(new FullyQualifiedJavaType("org.springframework.beans.factory.annotation.Autowired"));
        //描述成员属性 的注解
        daoField.addAnnotation("@Autowired");
        //描述成员属性修饰符
        daoField.setVisibility(JavaVisibility.PRIVATE);
        clazz.addField(daoField);
 
        //描述 方法名
        Method method = new Method("getMapper");
        //方法注解
        method.addAnnotation("@Override");
        FullyQualifiedJavaType methodReturnType = new FullyQualifiedJavaType("Object");
        //返回值
        method.setReturnType(methodReturnType);
        //方法体,逻辑代码
        method.addBodyLine("return " + daoFieldName + ";");
        //修饰符
        method.setVisibility(JavaVisibility.PUBLIC);
        clazz.addMethod(method);
 
 
        Method method1 = new Method("getExample");
        method1.addAnnotation("@Override");
        FullyQualifiedJavaType methodReturnType1 = new FullyQualifiedJavaType("Object");
        clazz.addImportedType(new FullyQualifiedJavaType(examplePacket.concat(".").concat(modelName).concat("Example")));
        method1.setReturnType(methodReturnType1);
        method1.addBodyLine("return new " + modelName + "Example();");
        method1.setVisibility(JavaVisibility.PUBLIC);
        clazz.addMethod(method1);
 
        GeneratedJavaFile gjf2 = new GeneratedJavaFile(clazz, targetProject, context.getJavaFormatter());
        return gjf2;
    }
 
 
    // 生成controller类
    private GeneratedJavaFile generateController(IntrospectedTable introspectedTable) {
 
        FullyQualifiedJavaType controller = new FullyQualifiedJavaType(controllerName);
        TopLevelClass clazz = new TopLevelClass(controller);
        //描述类的作用域修饰符
        clazz.setVisibility(JavaVisibility.PUBLIC);
 
        //添加@Controller注解,并引入相应的类
        clazz.addImportedType(new FullyQualifiedJavaType("org.springframework.web.bind.annotation.RestController"));
        clazz.addAnnotation("@RestController");
        //添加@RequestMapping注解,并引入相应的类
        clazz.addImportedType(new FullyQualifiedJavaType("org.springframework.web.bind.annotation.RequestMapping"));
        clazz.addAnnotation("@RequestMapping(\"/"+firstCharToLowCase(modelName)+"\")");
        //添加@Api注解,并引入相应的类
        clazz.addImportedType(new FullyQualifiedJavaType("io.swagger.annotations.Api"));
        String controllerSimpleName = controllerName.substring(controllerName.lastIndexOf(".") + 1);
        clazz.addAnnotation("@Api(tags = \""+controllerSimpleName+"\", description = \""+controllerSimpleName+"\")");
 
        //引入controller的父类和model,并添加泛型
        if(stringHasValue(superController)) {
            clazz.addImportedType(superController);
            clazz.addImportedType(recordType);
            FullyQualifiedJavaType superInterfac = new FullyQualifiedJavaType(superController+"<"+modelName+">");
            clazz.addSuperInterface(superInterfac);
        }
 
        //引入Service
        FullyQualifiedJavaType service = new FullyQualifiedJavaType(serviceName);
        clazz.addImportedType(service);
 
        //添加Service成员变量
        String serviceFieldName = firstCharToLowCase(serviceName.substring(serviceName.lastIndexOf(".") + 1));
        Field daoField = new Field(serviceFieldName, new FullyQualifiedJavaType(serviceName));
        clazz.addImportedType(new FullyQualifiedJavaType(serviceName));
        clazz.addImportedType(new FullyQualifiedJavaType("org.springframework.beans.factory.annotation.Autowired"));
        //描述成员属性 的注解
        daoField.addAnnotation("@Autowired");
        //描述成员属性修饰符
        daoField.setVisibility(JavaVisibility.PRIVATE);
        clazz.addField(daoField);
 
 
        //描述 方法名
        Method method = new Method("getService");
        //方法注解
        method.addAnnotation("@Override");
        String simpleSuperServiceName = superServiceInterface.substring(superServiceInterface.lastIndexOf(".") + 1);
        FullyQualifiedJavaType methodReturnType = new FullyQualifiedJavaType(simpleSuperServiceName+"<"+modelName+">");
        //返回类型
        method.setReturnType(methodReturnType);
        //方法体,逻辑代码
        method.addBodyLine("return " + serviceFieldName + ";");
        //修饰符
        method.setVisibility(JavaVisibility.PUBLIC);
        clazz.addImportedType(superServiceInterface);
        clazz.addMethod(method);
 
 
        GeneratedJavaFile gjf2 = new GeneratedJavaFile(clazz, targetProject, context.getJavaFormatter());
        return gjf2;
    }
 
 
    private String firstCharToLowCase(String str) {
        char[] chars = new char[1];
        //String str="ABCDE1234";
        chars[0] = str.charAt(0);
        String temp = new String(chars);
        if(chars[0] >= 'A'  &&  chars[0] <= 'Z') {
            return str.replaceFirst(temp,temp.toLowerCase());
        }
        return str;
    }
}


2、添加 generatorConfig.xml 配置


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
 
<generatorConfiguration>
    <properties resource="generator.properties"/>
    <context id="MySqlContext" targetRuntime="MyBatis3" defaultModelType="flat">
        <property name="beginningDelimiter" value="`"/>
        <property name="endingDelimiter" value="`"/>
        <property name="javaFileEncoding" value="UTF-8"/>
        <!-- 为模型生成序列化方法-->
        <plugin type="org.mybatis.generator.plugins.SerializablePlugin"/>
        <!-- 为生成的Java模型创建一个toString方法 -->
        <plugin type="org.mybatis.generator.plugins.ToStringPlugin"/>
        <!--生成mapper.xml时覆盖原文件-->
        <plugin type="org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin" />
        <plugin type="com.jzxs.etp.mbg.ServiceAndControllerGeneratorPlugin" >
            <property name="targetProject" value="./src/main/java"/>
            <property name="servicePackage" value="com.jzxs.etp.service"/>
            <property name="serviceImplPackage" value="com.jzxs.etp.service.impl"/>
            <property name="controllerPackage" value="com.jzxs.etp.controller"/>
            <!--UserService,该值则为Service-->
            <property name="serviceSuffix" value="Service"/>
            <!--Service接口的父接口-->
            <property name="superServiceInterface" value="org.aurochsframework.boot.commons.service.GeneralService"/>
            <!--ServiceImpl的父类-->
            <property name="superServiceImpl" value="org.aurochsframework.boot.commons.service.AbstractGeneralService"/>
            <!--controller的父类接口-->
            <property name="superController" value="org.aurochsframework.boot.commons.controller.GeneralCrudController"/>
        </plugin>
        <commentGenerator type="com.jzxs.etp.mbg.CommentGenerator">
            <!-- 是否去除自动生成的注释 true:是 : false:否 -->
            <property name="suppressAllComments" value="true"/>
            <property name="suppressDate" value="true"/>
            <property name="addRemarkComments" value="true"/>
        </commentGenerator>
 
        <jdbcConnection driverClass="${jdbc.driverClass}"
                        connectionURL="${jdbc.connectionURL}"
                        userId="${jdbc.userId}"
                        password="${jdbc.password}">
            <!--解决mysql驱动升级到8.0后不生成指定数据库代码的问题-->
            <property name="nullCatalogMeansCurrent" value="true" />
        </jdbcConnection>
 
        <!--指定生成model的路径-->
        <javaModelGenerator targetPackage="com.jzxs.etp.mbg.model"
                            targetProject="./src/main/java"/>
        <!--指定生成mapper.xml的路径-->
        <sqlMapGenerator targetPackage="com.jzxs.etp.mbg.mapper"
                         targetProject="./src/main/resources"/>
        <!--指定生成mapper接口的的路径-->
        <javaClientGenerator type="XMLMAPPER" targetPackage="com.jzxs.etp.mbg.mapper"
                             targetProject="./src/main/java"/>
 
        <table tableName="test"></table>
    </context>
</generatorConfiguration>


五、MyBatis Generator Gui

mybatis-generator-gui是基于mybatis generator开发的一款界面工具,该工具可以使你非常容易及快速生成Mybatis的Java POJO文件及数据库Mapping文件。官方提供的xml配置非常灵活,对于熟悉的同学可能更加喜欢,这个工具对于新手来说可以更加容易上手。

① 、核心特性

1、按照界面步骤轻松生成代码,省去XML繁琐的学习与配置过程

2、保存数据库连接与Generator配置,每次代码生成轻松搞定

3、内置常用插件,比如offset分页 、可选的去除掉对版本管理不友好的注释,这样新增或删除字段重新生成的文件比较过来清楚

4、目前已经支持Mysql、Oracle与PostgreSQL

下载地址: https://github.com 搜索 mybatis-generator-gui 随便下载一个就可以了
也可以上 gitee 、 gitcode 下载

② 、下载代码并编译


# 下载代码
git clone https://github.com/zouzg/mybatis-generator-gui

# 进入 mybatis-generator-gui 目录
cd mybatis-generator-gui
# 执行 maven 命令
mvn jfx:jar
# 进入 target/jfx/app/ 目录
cd target/jfx/app/
# 运行jar 包
java -jar mybatis-generator-gui.jar

③ 、界面效果

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

④ 、项目整体目录:

在这里插入图片描述
在这里插入图片描述

⑤ 、MyBatis Generator 逆向工程代码扩展

这里我们主要讲的还是 关于第四章 MyBatis Generator 的逆向工程代码扩展,在MyBatis Generator gui 要怎么写,因为这里没有 generatorConfig.xml 配置 了
至于 其他的知识,比如Java FX 怎么写 请自行 先学习相关内容

②、自定义Controller和Service插件

1、自定义生成Controller和Service的模板。


package com.jzxs.etp.mbg;
 
import org.mybatis.generator.api.GeneratedJavaFile;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.java.*;
 
import java.util.ArrayList;
import java.util.List;
 
import static org.mybatis.generator.internal.util.StringUtility.stringHasValue;
import static org.mybatis.generator.internal.util.messages.Messages.getString;
 
public class ServiceAndControllerGeneratorPlugin extends PluginAdapter  {
 
    // 项目目录,一般为 src/main/java
    private String targetProject;
 
    // service包名,如:com.thinkj2ee.cms.service.service
    private String servicePackage;
 
    // service实现类包名,如:com.thinkj2ee.cms.service.service.impl
    private String serviceImplPackage;
    // Controlle类包名,如:com.thinkj2ee.cms.service.controller
    private String controllerPackage;
    // service接口名前缀
    private String servicePreffix;
 
    // service接口名后缀
    private String serviceSuffix;
 
    // service接口的父接口
    private String superServiceInterface;
 
    // service实现类的父类
    private String superServiceImpl;
    // controller类的父类
    private String superController;
 
    // dao接口基类
    private String superDaoInterface;
 
    // Example类的包名
    private String examplePacket;
 
    private String recordType;
 
    private String modelName;
 
    private FullyQualifiedJavaType model;
 
    private String serviceName;
    private String serviceImplName;
    private String controllerName;
 
    @Override
    public boolean validate(List<String> warnings) {
        boolean valid = true;
 
       /* if (!stringHasValue(properties
                .getProperty("targetProject"))) { //$NON-NLS-1$
            warnings.add(getString("ValidationError.18", //$NON-NLS-1$
                    "MapperConfigPlugin", //$NON-NLS-1$
                    "targetProject")); //$NON-NLS-1$
            valid = false;
        }
        if (!stringHasValue(properties.getProperty("servicePackage"))) { //$NON-NLS-1$
            warnings.add(getString("ValidationError.18", //$NON-NLS-1$
                    "MapperConfigPlugin", //$NON-NLS-1$
                    "servicePackage")); //$NON-NLS-1$
            valid = false;
        }
        if (!stringHasValue(properties.getProperty("serviceImplPackage"))) { //$NON-NLS-1$
            warnings.add(getString("ValidationError.18", //$NON-NLS-1$
                    "MapperConfigPlugin", //$NON-NLS-1$
                    "serviceImplPackage")); //$NON-NLS-1$
            valid = false;
        }
*/
        targetProject = properties.getProperty("targetProject");
        servicePackage = properties.getProperty("servicePackage");
        serviceImplPackage = properties.getProperty("serviceImplPackage");
        servicePreffix = properties.getProperty("servicePreffix");
        servicePreffix = stringHasValue(servicePreffix) ? servicePreffix : "";
        serviceSuffix = properties.getProperty("serviceSuffix");
        serviceSuffix = stringHasValue(serviceSuffix) ? serviceSuffix : "";
        superServiceInterface = properties.getProperty("superServiceInterface");
        superServiceImpl = properties.getProperty("superServiceImpl");
        superDaoInterface = properties.getProperty("superDaoInterface");
        controllerPackage = properties.getProperty("controllerPackage");
        superController = properties.getProperty("superController");
 
        return valid;
    }
 
    @Override
    public List<GeneratedJavaFile> contextGenerateAdditionalJavaFiles(IntrospectedTable introspectedTable) {
        recordType = introspectedTable.getBaseRecordType();
        modelName = recordType.substring(recordType.lastIndexOf(".") + 1);
        model = new FullyQualifiedJavaType(recordType);
        serviceName = servicePackage + "." + servicePreffix + modelName + serviceSuffix;
        serviceImplName = serviceImplPackage + "." + modelName + serviceSuffix+"Impl";
        examplePacket=recordType.substring(0,recordType.lastIndexOf("."));
        controllerName=controllerPackage.concat(".").concat(modelName).concat("Controller");
        List<GeneratedJavaFile> answer = new ArrayList<>();
        GeneratedJavaFile gjf = generateServiceInterface(introspectedTable);
        GeneratedJavaFile gjf2 = generateServiceImpl(introspectedTable);
        GeneratedJavaFile gjf3 = generateController(introspectedTable);
        answer.add(gjf);
        answer.add(gjf2);
        answer.add(gjf3);
        return answer;
    }
 
    // 生成service接口
    private GeneratedJavaFile generateServiceInterface(IntrospectedTable introspectedTable) {
        FullyQualifiedJavaType service = new FullyQualifiedJavaType(serviceName);
        Interface serviceInterface = new Interface(service);
        serviceInterface.setVisibility(JavaVisibility.PUBLIC);
        // 添加父接口
        if(stringHasValue(superServiceInterface)) {
            String superServiceInterfaceName = superServiceInterface.substring(superServiceInterface.lastIndexOf(".") + 1);
            serviceInterface.addImportedType(new FullyQualifiedJavaType(superServiceInterface));
            serviceInterface.addImportedType(new FullyQualifiedJavaType(recordType));
            serviceInterface.addSuperInterface(new FullyQualifiedJavaType(superServiceInterfaceName + "<" + modelName + ">"));
        }
        GeneratedJavaFile gjf = new GeneratedJavaFile(serviceInterface, targetProject, context.getJavaFormatter());
        return gjf;
    }
 
    // 生成serviceImpl实现类
    private GeneratedJavaFile generateServiceImpl(IntrospectedTable introspectedTable) {
        FullyQualifiedJavaType service = new FullyQualifiedJavaType(serviceName);
        FullyQualifiedJavaType serviceImpl = new FullyQualifiedJavaType(serviceImplName);
        TopLevelClass clazz = new TopLevelClass(serviceImpl);
        //描述类的作用域修饰符
        clazz.setVisibility(JavaVisibility.PUBLIC);
        //描述类 引入的类
        clazz.addImportedType(service);
        //描述类 的实现接口类
        clazz.addSuperInterface(service);
        if(stringHasValue(superServiceImpl)) {
            String superServiceImplName = superServiceImpl.substring(superServiceImpl.lastIndexOf(".") + 1);
            clazz.addImportedType(superServiceImpl);
            clazz.addImportedType(recordType);
            clazz.setSuperClass(superServiceImplName + "<" + modelName + ">");
        }
        clazz.addImportedType(new FullyQualifiedJavaType("org.springframework.stereotype.Service"));
        clazz.addAnnotation("@Service");
 
        String daoFieldType = introspectedTable.getMyBatis3JavaMapperType();
        String daoFieldName = firstCharToLowCase(daoFieldType.substring(daoFieldType.lastIndexOf(".") + 1));
        //描述类的成员属性
        Field daoField = new Field(daoFieldName, new FullyQualifiedJavaType(daoFieldType));
        clazz.addImportedType(new FullyQualifiedJavaType(daoFieldType));
        clazz.addImportedType(new FullyQualifiedJavaType("org.springframework.beans.factory.annotation.Autowired"));
        //描述成员属性 的注解
        daoField.addAnnotation("@Autowired");
        //描述成员属性修饰符
        daoField.setVisibility(JavaVisibility.PRIVATE);
        clazz.addField(daoField);
 
        //描述 方法名
        Method method = new Method("getMapper");
        //方法注解
        method.addAnnotation("@Override");
        FullyQualifiedJavaType methodReturnType = new FullyQualifiedJavaType("Object");
        //返回值
        method.setReturnType(methodReturnType);
        //方法体,逻辑代码
        method.addBodyLine("return " + daoFieldName + ";");
        //修饰符
        method.setVisibility(JavaVisibility.PUBLIC);
        clazz.addMethod(method);
 
 
        Method method1 = new Method("getExample");
        method1.addAnnotation("@Override");
        FullyQualifiedJavaType methodReturnType1 = new FullyQualifiedJavaType("Object");
        clazz.addImportedType(new FullyQualifiedJavaType(examplePacket.concat(".").concat(modelName).concat("Example")));
        method1.setReturnType(methodReturnType1);
        method1.addBodyLine("return new " + modelName + "Example();");
        method1.setVisibility(JavaVisibility.PUBLIC);
        clazz.addMethod(method1);
 
        GeneratedJavaFile gjf2 = new GeneratedJavaFile(clazz, targetProject, context.getJavaFormatter());
        return gjf2;
    }
 
 
    // 生成controller类
    private GeneratedJavaFile generateController(IntrospectedTable introspectedTable) {
 
        FullyQualifiedJavaType controller = new FullyQualifiedJavaType(controllerName);
        TopLevelClass clazz = new TopLevelClass(controller);
        //描述类的作用域修饰符
        clazz.setVisibility(JavaVisibility.PUBLIC);
 
        //添加@Controller注解,并引入相应的类
        clazz.addImportedType(new FullyQualifiedJavaType("org.springframework.web.bind.annotation.RestController"));
        clazz.addAnnotation("@RestController");
        //添加@RequestMapping注解,并引入相应的类
        clazz.addImportedType(new FullyQualifiedJavaType("org.springframework.web.bind.annotation.RequestMapping"));
        clazz.addAnnotation("@RequestMapping(\"/"+firstCharToLowCase(modelName)+"\")");
        //添加@Api注解,并引入相应的类
        clazz.addImportedType(new FullyQualifiedJavaType("io.swagger.annotations.Api"));
        String controllerSimpleName = controllerName.substring(controllerName.lastIndexOf(".") + 1);
        clazz.addAnnotation("@Api(tags = \""+controllerSimpleName+"\", description = \""+controllerSimpleName+"\")");
 
        //引入controller的父类和model,并添加泛型
        if(stringHasValue(superController)) {
            clazz.addImportedType(superController);
            clazz.addImportedType(recordType);
            FullyQualifiedJavaType superInterfac = new FullyQualifiedJavaType(superController+"<"+modelName+">");
            clazz.addSuperInterface(superInterfac);
        }
 
        //引入Service
        FullyQualifiedJavaType service = new FullyQualifiedJavaType(serviceName);
        clazz.addImportedType(service);
 
        //添加Service成员变量
        String serviceFieldName = firstCharToLowCase(serviceName.substring(serviceName.lastIndexOf(".") + 1));
        Field daoField = new Field(serviceFieldName, new FullyQualifiedJavaType(serviceName));
        clazz.addImportedType(new FullyQualifiedJavaType(serviceName));
        clazz.addImportedType(new FullyQualifiedJavaType("org.springframework.beans.factory.annotation.Autowired"));
        //描述成员属性 的注解
        daoField.addAnnotation("@Autowired");
        //描述成员属性修饰符
        daoField.setVisibility(JavaVisibility.PRIVATE);
        clazz.addField(daoField);
 
 
        //描述 方法名
        Method method = new Method("getService");
        //方法注解
        method.addAnnotation("@Override");
        String simpleSuperServiceName = superServiceInterface.substring(superServiceInterface.lastIndexOf(".") + 1);
        FullyQualifiedJavaType methodReturnType = new FullyQualifiedJavaType(simpleSuperServiceName+"<"+modelName+">");
        //返回类型
        method.setReturnType(methodReturnType);
        //方法体,逻辑代码
        method.addBodyLine("return " + serviceFieldName + ";");
        //修饰符
        method.setVisibility(JavaVisibility.PUBLIC);
        clazz.addImportedType(superServiceInterface);
        clazz.addMethod(method);
 
 
        GeneratedJavaFile gjf2 = new GeneratedJavaFile(clazz, targetProject, context.getJavaFormatter());
        return gjf2;
    }
 
 
    private String firstCharToLowCase(String str) {
        char[] chars = new char[1];
        //String str="ABCDE1234";
        chars[0] = str.charAt(0);
        String temp = new String(chars);
        if(chars[0] >= 'A'  &&  chars[0] <= 'Z') {
            return str.replaceFirst(temp,temp.toLowerCase());
        }
        return str;
    }
}


2、添加 MainUI.fxml 功能


<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import javafx.collections.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>

<BorderPane prefHeight="613.0" prefWidth="918.0" stylesheets="@../style.css" xmlns="http://javafx.com/javafx/17.0.2-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.zzg.mybatis.generator.controller.MainUIController">
    <top>
        <VBox>
            <children>
                <ToolBar minHeight="70.0" prefHeight="81.0" prefWidth="918.0" BorderPane.alignment="CENTER">
                    <items>
                        <Label fx:id="connectionLabel" contentDisplay="TOP" text="数据库连接">
                            <cursor>
                                <Cursor fx:constant="HAND" />
                            </cursor>
                            <font>
                                <Font size="14.0" />
                            </font>
                            <padding>
                                <Insets left="10.0" right="10.0" />
                            </padding>
                        </Label>
                        <Label fx:id="configsLabel" contentDisplay="TOP" text="配置">
                            <padding>
                                <Insets right="10.0" />
                            </padding>
                        </Label>
                    </items>
                </ToolBar>
            </children>
        </VBox>
    </top>
    <center>
        <SplitPane dividerPositions="0.15">
            <items>
                <AnchorPane maxWidth="500.0" minWidth="100.0" prefHeight="818.0" prefWidth="200.0">
                    <children>
                        <TreeView fx:id="leftDBTree" layoutX="-14.0" maxWidth="134.0" prefHeight="503.0" prefWidth="134.0" AnchorPane.bottomAnchor="27.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
                        <TextField fx:id="filterTreeBox" layoutY="504.0" prefHeight="26.0" prefWidth="134.0" promptText="搜索" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" />
                    </children>
                </AnchorPane>
                <AnchorPane minWidth="400.0">
                    <children>
                        <VBox AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
                            <children>
                                <GridPane alignment="TOP_RIGHT" layoutX="5.0" layoutY="29.0" prefHeight="805.0" prefWidth="766.0" vgap="5.0" AnchorPane.leftAnchor="-5.0" AnchorPane.rightAnchor="10.0">
                                    <columnConstraints>
                                        <ColumnConstraints halignment="RIGHT" hgrow="SOMETIMES" maxWidth="157.0" minWidth="132.0" prefWidth="138.0" />
                                        <ColumnConstraints hgrow="SOMETIMES" maxWidth="688.0" minWidth="10.0" prefWidth="222.0" />
                                        <ColumnConstraints halignment="RIGHT" hgrow="SOMETIMES" maxWidth="688.0" minWidth="69.0" prefWidth="76.0" />
                                        <ColumnConstraints hgrow="SOMETIMES" maxWidth="688.0" minWidth="10.0" prefWidth="108.0" />
                                        <ColumnConstraints halignment="RIGHT" hgrow="SOMETIMES" maxWidth="688.0" minWidth="10.0" prefWidth="129.0" />
                                        <ColumnConstraints hgrow="SOMETIMES" maxWidth="688.0" minWidth="10.0" prefWidth="95.0" />
                                    </columnConstraints>
                                    <rowConstraints>
                                        <RowConstraints minHeight="10.0"  prefHeight="30.0"  />
                                        <RowConstraints minHeight="10.0"  prefHeight="30.0"  />
                                        <RowConstraints minHeight="10.0"  prefHeight="30.0"  />
                                        <RowConstraints minHeight="10.0"  prefHeight="30.0"  />
                                        <RowConstraints minHeight="10.0"  prefHeight="30.0"  />
                                        <RowConstraints minHeight="10.0"  prefHeight="30.0"  />
                                        <RowConstraints minHeight="10.0"  prefHeight="30.0"  />
                                        <RowConstraints minHeight="10.0"  prefHeight="30.0"  />
                                        <RowConstraints minHeight="10.0"  prefHeight="30.0"  />
                                        <RowConstraints minHeight="10.0"  prefHeight="30.0"  />
                                        <RowConstraints minHeight="10.0"  prefHeight="30.0"  />
                                        <RowConstraints minHeight="10.0"  prefHeight="200"   />
                                        <RowConstraints maxHeight="43.0"  minHeight="11.0"  prefHeight="43" />
                                    </rowConstraints>
                                    <children>
                                        <Label text="表名" />
                                        <TextField fx:id="tableNameField" disable="true" editable="false" prefHeight="27.0" prefWidth="156.0" promptText="person" GridPane.columnIndex="1">
                                            <GridPane.margin>
                                                <Insets left="5.0" right="5.0" />
                                            </GridPane.margin>
                                        </TextField>

                                        <Label text="Java实体类名" GridPane.rowIndex="1" />
                                        <HBox alignment="CENTER_LEFT" GridPane.columnIndex="1" GridPane.columnSpan="3" GridPane.rowIndex="1" GridPane.valignment="CENTER">
                                            <children>
                                                <TextField fx:id="domainObjectNameField" prefHeight="27.0" prefWidth="154.0" promptText="Person" GridPane.columnIndex="1" GridPane.rowIndex="2">
                                                    <GridPane.margin>
                                                        <Insets left="5.0" right="5.0" />
                                                    </GridPane.margin>
                                                    <HBox.margin>
                                                        <Insets right="5.0" />
                                                    </HBox.margin>
                                                </TextField>
                                                <Button mnemonicParsing="false" onAction="#openTableColumnCustomizationPage" text="定制列">
                                                    <styleClass>
                                                        <String fx:value="btn" />
                                                        <String fx:value="btn-default" />
                                                    </styleClass>
                                                </Button>
                                            </children>
                                            <GridPane.margin>
                                                <Insets left="5.0" />
                                            </GridPane.margin>
                                        </HBox>

                                        <Label text="主键(选填)" GridPane.rowIndex="2" />
                                        <HBox alignment="CENTER_LEFT" GridPane.columnIndex="1" GridPane.columnSpan="3" GridPane.rowIndex="2" GridPane.valignment="CENTER">
                                            <children>
                                                <TextField fx:id="generateKeysField" prefHeight="25.0" prefWidth="216.0" promptText="primary key, such as id" GridPane.columnIndex="1" GridPane.rowIndex="3">
                                                    <GridPane.margin>
                                                        <Insets left="5.0" right="5.0" />
                                                    </GridPane.margin>
                                                    <HBox.margin>
                                                        <Insets right="5.0" />
                                                    </HBox.margin>
                                                </TextField>
                                            </children>
                                            <GridPane.margin>
                                                <Insets left="5.0" />
                                            </GridPane.margin>
                                        </HBox>

                                        <Label text="项目所在目录" GridPane.rowIndex="3" />
                                        <HBox alignment="CENTER_LEFT" prefHeight="30.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.columnSpan="4" GridPane.rowIndex="3">
                                            <children>
                                                <TextField fx:id="projectFolderField" prefHeight="27.0" prefWidth="463.0" promptText="D:\workspace\example">
                                                    <HBox.margin>
                                                        <Insets left="5.0" right="5.0" />
                                                    </HBox.margin>
                                                </TextField>
                                                <Button mnemonicParsing="false" onAction="#chooseProjectFolder" text="选择">
                                                    <styleClass>
                                                        <String fx:value="btn" />
                                                        <String fx:value="btn-default" />
                                                    </styleClass>
                                                </Button>
                                            </children>
                                        </HBox>

                                        <Label text="Controller接口包名" GridPane.rowIndex="4" />
                                        <TextField fx:id="controllerTargetPackage" prefHeight="27.0" prefWidth="248.0" promptText="com.example.controller" text="com.example.controller" GridPane.columnIndex="1" GridPane.rowIndex="4">
                                            <HBox.margin>
                                                <Insets right="5.0" />
                                            </HBox.margin>
                                            <GridPane.margin>
                                                <Insets left="5.0" right="5.0" />
                                            </GridPane.margin>
                                        </TextField>
                                        <Label text="存放目录" GridPane.columnIndex="2" GridPane.rowIndex="4" />
                                        <TextField fx:id="controllerTargetProject" prefHeight="27.0" prefWidth="155.0" promptText="src/main/java" text="src/main/java" GridPane.columnIndex="3" GridPane.columnSpan="2" GridPane.rowIndex="4">
                                            <GridPane.margin>
                                                <Insets left="5.0" />
                                            </GridPane.margin>
                                        </TextField>

                                        <Label text="实体类名包名" GridPane.rowIndex="5" />
                                        <TextField fx:id="modelTargetPackage" prefHeight="27.0" prefWidth="152.0" promptText="com.example.model" text="com.example.model" GridPane.columnIndex="1" GridPane.rowIndex="5">
                                            <HBox.margin>
                                                <Insets right="5.0" />
                                            </HBox.margin>
                                            <GridPane.margin>
                                                <Insets left="5.0" right="5.0" />
                                            </GridPane.margin>
                                        </TextField>
                                        <Label text="存放目录" GridPane.columnIndex="2" GridPane.rowIndex="5" />
                                        <TextField fx:id="modelTargetProject" prefHeight="27.0" prefWidth="228.0" promptText="src/main/java" text="src/main/java" GridPane.columnIndex="3" GridPane.columnSpan="2" GridPane.rowIndex="5">
                                            <GridPane.margin>
                                                <Insets left="5.0" />
                                            </GridPane.margin>
                                        </TextField>

                                        <Label text="Service接口包名" GridPane.rowIndex="6" />
                                        <TextField fx:id="serviceTargetPackage" prefHeight="27.0" prefWidth="248.0" promptText="com.example.service" text="com.example.service" GridPane.columnIndex="1" GridPane.rowIndex="6">
                                            <HBox.margin>
                                                <Insets right="5.0" />
                                            </HBox.margin>
                                            <GridPane.margin>
                                                <Insets left="5.0" right="5.0" />
                                            </GridPane.margin>
                                        </TextField>
                                        <Label text="存放目录" GridPane.columnIndex="2" GridPane.rowIndex="6" />
                                        <TextField fx:id="serviceTargetProject" prefHeight="27.0" prefWidth="155.0" promptText="src/main/java" text="src/main/java" GridPane.columnIndex="3" GridPane.columnSpan="2" GridPane.rowIndex="6">
                                            <GridPane.margin>
                                                <Insets left="5.0" />
                                            </GridPane.margin>
                                        </TextField>

                                        <Label text="ServiceImpl接口包名" GridPane.rowIndex="7" />
                                        <TextField fx:id="serviceImplTargetPackage" prefHeight="27.0" prefWidth="248.0" promptText="com.example.service.impl" text="com.example.service.impl" GridPane.columnIndex="1" GridPane.rowIndex="7">
                                            <HBox.margin>
                                                <Insets right="5.0" />
                                            </HBox.margin>
                                            <GridPane.margin>
                                                <Insets left="5.0" right="5.0" />
                                            </GridPane.margin>
                                        </TextField>
                                        <Label text="存放目录" GridPane.columnIndex="2" GridPane.rowIndex="7" />
                                        <TextField fx:id="serviceImplTargetProject" prefHeight="27.0" prefWidth="155.0" promptText="src/main/java" text="src/main/java" GridPane.columnIndex="3" GridPane.columnSpan="2" GridPane.rowIndex="7">
                                            <GridPane.margin>
                                                <Insets left="5.0" />
                                            </GridPane.margin>
                                        </TextField>


                                        <Label text="Mapper接口包名" GridPane.rowIndex="8" />
                                        <TextField fx:id="daoTargetPackage" prefHeight="27.0" prefWidth="248.0" promptText="com.example.mapper" text="com.example.mapper" GridPane.columnIndex="1" GridPane.rowIndex="8">
                                            <HBox.margin>
                                                <Insets right="5.0" />
                                            </HBox.margin>
                                            <GridPane.margin>
                                                <Insets left="5.0" right="5.0" />
                                            </GridPane.margin>
                                        </TextField>
                                        <Label text="存放目录" GridPane.columnIndex="2" GridPane.rowIndex="8" />
                                        <TextField fx:id="daoTargetProject" prefHeight="27.0" prefWidth="155.0" promptText="src/main/java" text="src/main/java" GridPane.columnIndex="3" GridPane.columnSpan="2" GridPane.rowIndex="8">
                                            <GridPane.margin>
                                                <Insets left="5.0" />
                                            </GridPane.margin>
                                        </TextField>

                                        <Label text="自定义接口名称(选填)" GridPane.rowIndex="9">
                                            <padding>
                                                <Insets left="5.0" />
                                            </padding>
                                        </Label>
                                        <TextField fx:id="mapperName" prefHeight="27.0" prefWidth="532.0" promptText="PersonDAO" GridPane.columnIndex="1" GridPane.columnSpan="4" GridPane.rowIndex="9">
                                            <GridPane.margin>
                                                <Insets left="5.0" />
                                            </GridPane.margin>
                                        </TextField>

                                        <Label prefHeight="27.0" prefWidth="99.0" text="映射XML文件包名" GridPane.rowIndex="10" />
                                        <TextField fx:id="mapperTargetPackage" prefHeight="27.0" prefWidth="248.0" promptText="com.example" text="com.example" GridPane.columnIndex="1" GridPane.rowIndex="10">
                                            <HBox.margin>
                                                <Insets right="5.0" />
                                            </HBox.margin>
                                            <GridPane.margin>
                                                <Insets left="5.0" right="5.0" />
                                            </GridPane.margin>
                                        </TextField>
                                        <Label text="存放目录" GridPane.columnIndex="2" GridPane.rowIndex="10" />
                                        <TextField fx:id="mappingTargetProject" prefHeight="27.0" prefWidth="155.0" promptText="src/main/resources" text="src/main/resources" GridPane.columnIndex="3" GridPane.columnSpan="2" GridPane.rowIndex="10">
                                            <GridPane.margin>
                                                <Insets left="5.0" />
                                            </GridPane.margin>
                                        </TextField>

                                        <VBox prefHeight="53.0" prefWidth="536.0" spacing="10.0" GridPane.columnIndex="1" GridPane.columnSpan="4" GridPane.rowIndex="11">
                                            <children>
                                                <HBox alignment="CENTER_LEFT">
                                                    <children>
                                                        <Label text="生成文件的编码" />
                                                        <ChoiceBox fx:id="encodingChoice" prefHeight="23.0" prefWidth="71.0">
                                                            <items>
                                                                <FXCollections fx:factory="observableArrayList">
                                                                    <String fx:value="UTF-8" />
                                                                </FXCollections>
                                                            </items>
                                                        </ChoiceBox>
                                                    </children>
                                                </HBox>
                                                <HBox alignment="CENTER_LEFT" spacing="10.0" GridPane.columnIndex="1" GridPane.columnSpan="3">
<!--                                                     GridPane.rowIndex="8"-->
                                                    <children>
                                                        <CheckBox fx:id="useExample" minWidth="100.0" mnemonicParsing="false" text="使用Example" />
                                                        <CheckBox fx:id="offsetLimitCheckBox" disable="true" minWidth="100.0" mnemonicParsing="false" selected="true" text="分页插件(暂时只支持MySQL和PostgreSQL)" GridPane.columnIndex="1" />
<!--                                                         GridPane.rowIndex="8"-->
                                                    </children>
                                                </HBox>
                                                <HBox prefHeight="100.0" prefWidth="200.0" spacing="18.0">
                                                    <children>
                                                        <CheckBox fx:id="commentCheckBox" mnemonicParsing="false" selected="true" text="生成实体域注释(来自表注释)" />
                                                        <CheckBox fx:id="overrideXML" mnemonicParsing="false" selected="true" text="覆盖原XML" />
                                                    </children>
                                                </HBox>
                                                <HBox spacing="18.0">
                                                    <children>
                                                        <CheckBox fx:id="useLombokPlugin" mnemonicParsing="false" text="LombokPlugin" />
                                                        <CheckBox fx:id="needToStringHashcodeEquals" mnemonicParsing="false" selected="true" text="生成toString/hashCode/equals方法" />
                                                        <CheckBox fx:id="useSchemaPrefix" mnemonicParsing="false" text="使用Schema前缀" />
                                                    </children>
                                                </HBox>
                                                <HBox prefHeight="100.0" prefWidth="200.0" spacing="18.0">
                                                    <children>
                                                        <CheckBox fx:id="forUpdateCheckBox" mnemonicParsing="false" selected="false" text="select 增加ForUpdate" />
                                                        <CheckBox fx:id="annotationDAOCheckBox" mnemonicParsing="false" selected="true" text="DAO使用 @Repository 注解" />
                                                        <CheckBox fx:id="annotationMapperDAOCheckBox" mnemonicParsing="false" selected="false" text="DAO使用 @Mapper 注解" />
                                                    </children>
                                                </HBox>
                                                <HBox prefHeight="100.0" prefWidth="200.0">
                                                    <children>
                                                        <CheckBox fx:id="useDAOExtendStyle" mnemonicParsing="false" selected="true" text="DAO方法抽出到公共父接口">
                                                            <HBox.margin>
                                                                <Insets right="10.0" />
                                                            </HBox.margin>
                                                        </CheckBox>
                                                        <CheckBox fx:id="jsr310Support" mnemonicParsing="false" prefHeight="16.0" prefWidth="252.0" text="JSR310: Date and Time API" />
                                                    </children>
                                                </HBox>
                                                <HBox spacing="18.0">
                                                    <children>
                                                        <CheckBox fx:id="annotationCheckBox" mnemonicParsing="false" selected="false" text="生成JPA注解" />
                                                        <CheckBox fx:id="useActualColumnNamesCheckbox" mnemonicParsing="false" selected="false" text="使用实际的列名" />
                                                        <CheckBox fx:id="useTableNameAliasCheckbox" mnemonicParsing="false" selected="false" text="启用as别名查询" />
                                                    </children>
                                                </HBox>
                                                <HBox prefHeight="100.0" prefWidth="200.0" />
                                            </children>
                                            <padding>
                                                <Insets left="5.0" />
                                            </padding>
                                        </VBox>
                                        <HBox alignment="CENTER_LEFT" prefHeight="100.0" prefWidth="200.0" spacing="10.0" GridPane.columnIndex="1" GridPane.columnSpan="3" GridPane.rowIndex="12">
                                            <children>
                                                <Button mnemonicParsing="false" onAction="#generateCode" text="代码生成">
                                                    <styleClass>
                                                        <String fx:value="btn-success" />
                                                        <String fx:value="btn" />
                                                    </styleClass>
                                                </Button>
                                                <Button mnemonicParsing="false" onAction="#saveGeneratorConfig" text="保存配置">
                                                    <styleClass>
                                                        <String fx:value="btn" />
                                                        <String fx:value="btn-default" />
                                                    </styleClass>
                                                </Button>
                                                <Button mnemonicParsing="false" onAction="#openTargetFolder" text="打开生成文件夹">
                                                    <styleClass>
                                                        <String fx:value="btn" />
                                                        <String fx:value="btn-default" />
                                                    </styleClass>
                                                </Button>
                                            </children>
                                        </HBox>

                                    </children>
                                </GridPane>
                            </children>
                            <padding>
                                <Insets bottom="7.0" left="7.0" right="7.0" top="7.0" />
                            </padding>
                        </VBox>
                    </children>
                </AnchorPane>
            </items>
        </SplitPane>
    </center>
</BorderPane>


3、添加 MainUIController 功能


package com.zzg.mybatis.generator.controller;

import com.jcraft.jsch.Session;
import com.zzg.mybatis.generator.bridge.MybatisGeneratorBridge;
import com.zzg.mybatis.generator.model.DatabaseConfig;
import com.zzg.mybatis.generator.model.GeneratorConfig;
import com.zzg.mybatis.generator.model.UITableColumnVO;
import com.zzg.mybatis.generator.util.ConfigHelper;
import com.zzg.mybatis.generator.util.DbUtil;
import com.zzg.mybatis.generator.util.MyStringUtils;
import com.zzg.mybatis.generator.view.AlertUtil;
import com.zzg.mybatis.generator.view.UIProgressCallback;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.TextFieldTreeCell;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.KeyEvent;
import javafx.stage.DirectoryChooser;
import javafx.util.Callback;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.mybatis.generator.config.ColumnOverride;
import org.mybatis.generator.config.IgnoredColumn;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.awt.*;
import java.io.File;
import java.net.URL;
import java.sql.SQLRecoverableException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.ResourceBundle;

public class MainUIController extends BaseFXController {

    private static final Logger _LOG = LoggerFactory.getLogger(MainUIController.class);
    private static final String FOLDER_NO_EXIST = "部分目录不存在,是否创建";
    // tool bar buttons
    @FXML
    private Label connectionLabel;
    @FXML
    private Label configsLabel;
    @FXML
    private TextField modelTargetPackage;
    @FXML
    private TextField serviceTargetPackage;
    @FXML
    private TextField serviceImplTargetPackage;
    @FXML
    private TextField controllerTargetPackage;
    @FXML
    private TextField mapperTargetPackage;
    @FXML
    private TextField daoTargetPackage;
    @FXML
    private TextField tableNameField;
    @FXML
    private TextField domainObjectNameField;
    @FXML
    private TextField generateKeysField;	//主键ID
    @FXML
    private TextField modelTargetProject;
    @FXML
    private TextField serviceTargetProject;
    @FXML
    private TextField serviceImplTargetProject;
    @FXML
    private TextField controllerTargetProject;
    @FXML
    private TextField mappingTargetProject;
    @FXML
    private TextField daoTargetProject;
    @FXML
    private TextField mapperName;
    @FXML
    private TextField projectFolderField;
    @FXML
    private CheckBox offsetLimitCheckBox;
    @FXML
    private CheckBox commentCheckBox;
    @FXML
	private CheckBox overrideXML;
    @FXML
    private CheckBox needToStringHashcodeEquals;
    @FXML
    private CheckBox useLombokPlugin;
    @FXML
    private CheckBox forUpdateCheckBox;
    @FXML
    private CheckBox annotationDAOCheckBox;
    @FXML
    private CheckBox annotationMapperDAOCheckBox;
    @FXML
    private CheckBox useTableNameAliasCheckbox;
    @FXML
    private CheckBox annotationCheckBox;
    @FXML
    private CheckBox useActualColumnNamesCheckbox;
    @FXML
    private CheckBox useExample;
    @FXML
    private CheckBox useDAOExtendStyle;
    @FXML
    private CheckBox useSchemaPrefix;
    @FXML
    private CheckBox jsr310Support;
    @FXML
    private TreeView<String> leftDBTree;
    @FXML
    public TextField filterTreeBox;
    // Current selected databaseConfig
    private DatabaseConfig selectedDatabaseConfig;
    // Current selected tableName
    private String tableName;

    private List<IgnoredColumn> ignoredColumns;

    private List<ColumnOverride> columnOverrides;

    @FXML
    private ChoiceBox<String> encodingChoice;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        ImageView dbImage = new ImageView("icons/computer.png");
        dbImage.setFitHeight(40);
        dbImage.setFitWidth(40);
        connectionLabel.setGraphic(dbImage);
        connectionLabel.setOnMouseClicked(event -> {
            TabPaneController controller = (TabPaneController) loadFXMLPage("新建数据库连接", FXMLPage.NEW_CONNECTION, false);
            controller.setMainUIController(this);
            controller.showDialogStage();
        });
        ImageView configImage = new ImageView("icons/config-list.png");
        configImage.setFitHeight(40);
        configImage.setFitWidth(40);
        configsLabel.setGraphic(configImage);
        configsLabel.setOnMouseClicked(event -> {
            GeneratorConfigController controller = (GeneratorConfigController) loadFXMLPage("配置", FXMLPage.GENERATOR_CONFIG, false);
            controller.setMainUIController(this);
            controller.showDialogStage();
        });
		useExample.setOnMouseClicked(event -> {
			if (useExample.isSelected()) {
				offsetLimitCheckBox.setDisable(false);
			} else {
				offsetLimitCheckBox.setDisable(true);
			}
		});
		// selectedProperty().addListener 解决应用配置的时候未触发Clicked事件
        useLombokPlugin.selectedProperty().addListener((observable, oldValue, newValue) -> {
            needToStringHashcodeEquals.setDisable(newValue);
        });

        leftDBTree.setShowRoot(false);
        leftDBTree.setRoot(new TreeItem<>());
        Callback<TreeView<String>, TreeCell<String>> defaultCellFactory = TextFieldTreeCell.forTreeView();
        filterTreeBox.addEventHandler(KeyEvent.KEY_PRESSED, ev -> {
            if (ev.getCode() == KeyCode.ENTER) {
                ObservableList<TreeItem<String>> schemas = leftDBTree.getRoot().getChildren();
                schemas.filtered(TreeItem::isExpanded).forEach(this::displayTables);
                ev.consume();
            }
        });
        leftDBTree.setCellFactory((TreeView<String> tv) -> {
            TreeCell<String> cell = defaultCellFactory.call(tv);

            cell.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> {
                int level = leftDBTree.getTreeItemLevel(cell.getTreeItem());
                TreeCell<String> treeCell = (TreeCell<String>) event.getSource();
                TreeItem<String> treeItem = treeCell.getTreeItem();
                if (level == 1) {
                    final ContextMenu contextMenu = new ContextMenu();
                    MenuItem item1 = new MenuItem("关闭连接");
                    item1.setOnAction(event1 -> treeItem.getChildren().clear());
	                MenuItem item2 = new MenuItem("编辑连接");
	                item2.setOnAction(event1 -> {
		                DatabaseConfig selectedConfig = (DatabaseConfig) treeItem.getGraphic().getUserData();
                        TabPaneController controller = (TabPaneController) loadFXMLPage("编辑数据库连接", FXMLPage.NEW_CONNECTION, false);
		                controller.setMainUIController(this);
		                controller.setConfig(selectedConfig);
		                controller.showDialogStage();
	                });
                    MenuItem item3 = new MenuItem("删除连接");
                    item3.setOnAction(event1 -> {
                        DatabaseConfig selectedConfig = (DatabaseConfig) treeItem.getGraphic().getUserData();
                        try {
                            ConfigHelper.deleteDatabaseConfig(selectedConfig);
                            this.loadLeftDBTree();
                        } catch (Exception e) {
                            AlertUtil.showErrorAlert("Delete connection failed! Reason: " + e.getMessage());
                        }
                    });
                    contextMenu.getItems().addAll(item1, item2, item3);
                    cell.setContextMenu(contextMenu);
                }
                if (event.getClickCount() == 2) {
                    if(treeItem == null) {
                        return ;
                    }
                    treeItem.setExpanded(true);
                    if (level == 1) {
                        displayTables(treeItem);
                    } else if (level == 2) { // left DB tree level3
                        String tableName = treeCell.getTreeItem().getValue();
                        selectedDatabaseConfig = (DatabaseConfig) treeItem.getParent().getGraphic().getUserData();
                        this.tableName = tableName;
                        tableNameField.setText(tableName);
                        domainObjectNameField.setText(MyStringUtils.dbStringToCamelStyle(tableName));
                        mapperName.setText(domainObjectNameField.getText().concat("DAO"));
                    }
                }
            });
            return cell;
        });
        loadLeftDBTree();
		setTooltip();
		//默认选中第一个,否则如果忘记选择,没有对应错误提示
        encodingChoice.getSelectionModel().selectFirst();
	}

	private void displayTables(TreeItem<String> treeItem) {
        if(treeItem == null) {
            return ;
        }
        if (!treeItem.isExpanded()) {
            return;
        }
        DatabaseConfig selectedConfig = (DatabaseConfig) treeItem.getGraphic().getUserData();
        try {
            String filter = filterTreeBox.getText();
            List<String> tables = DbUtil.getTableNames(selectedConfig, filter);
            if (tables.size() > 0) {
                ObservableList<TreeItem<String>> children = treeItem.getChildren();
                children.clear();
                for (String tableName : tables) {
                    TreeItem<String> newTreeItem = new TreeItem<>();
                    ImageView imageView = new ImageView("icons/table.png");
                    imageView.setFitHeight(16);
                    imageView.setFitWidth(16);
                    newTreeItem.setGraphic(imageView);
                    newTreeItem.setValue(tableName);
                    children.add(newTreeItem);
                }
            }else if (StringUtils.isNotBlank(filter)){
                treeItem.getChildren().clear();
            }
            if (StringUtils.isNotBlank(filter)) {
                ImageView imageView = new ImageView("icons/filter.png");
                imageView.setFitHeight(16);
                imageView.setFitWidth(16);
                imageView.setUserData(treeItem.getGraphic().getUserData());
                treeItem.setGraphic(imageView);
            }else {
                ImageView dbImage = new ImageView("icons/computer.png");
                dbImage.setFitHeight(16);
                dbImage.setFitWidth(16);
                dbImage.setUserData(treeItem.getGraphic().getUserData());
                treeItem.setGraphic(dbImage);
            }
        } catch (SQLRecoverableException e) {
            _LOG.error(e.getMessage(), e);
            AlertUtil.showErrorAlert("连接超时");
        } catch (Exception e) {
            _LOG.error(e.getMessage(), e);
            AlertUtil.showErrorAlert(e.getMessage());
        }
    }

	private void setTooltip() {
		encodingChoice.setTooltip(new Tooltip("生成文件的编码,必选"));
		generateKeysField.setTooltip(new Tooltip("insert时可以返回主键ID"));
		offsetLimitCheckBox.setTooltip(new Tooltip("是否要生成分页查询代码"));
		commentCheckBox.setTooltip(new Tooltip("使用数据库的列注释作为实体类字段名的Java注释 "));
		useActualColumnNamesCheckbox.setTooltip(new Tooltip("是否使用数据库实际的列名作为实体类域的名称"));
		useTableNameAliasCheckbox.setTooltip(new Tooltip("在Mapper XML文件中表名使用别名,并且列全部使用as查询"));
		overrideXML.setTooltip(new Tooltip("重新生成时把原XML文件覆盖,否则是追加"));
        useDAOExtendStyle.setTooltip(new Tooltip("将通用接口方法放在公共接口中,DAO接口留空"));
        forUpdateCheckBox.setTooltip(new Tooltip("在Select语句中增加for update后缀"));
        useLombokPlugin.setTooltip(new Tooltip("实体类使用Lombok @Data简化代码"));
	}

    void loadLeftDBTree() {
        TreeItem rootTreeItem = leftDBTree.getRoot();
        rootTreeItem.getChildren().clear();
        try {
            List<DatabaseConfig> dbConfigs = ConfigHelper.loadDatabaseConfig();
            for (DatabaseConfig dbConfig : dbConfigs) {
                TreeItem<String> treeItem = new TreeItem<>();
                treeItem.setValue(dbConfig.getName());
                ImageView dbImage = new ImageView("icons/computer.png");
                dbImage.setFitHeight(16);
                dbImage.setFitWidth(16);
                dbImage.setUserData(dbConfig);
                treeItem.setGraphic(dbImage);
                rootTreeItem.getChildren().add(treeItem);
            }
        } catch (Exception e) {
            _LOG.error("connect db failed, reason", e);
            AlertUtil.showErrorAlert(e.getMessage() + "\n" + ExceptionUtils.getStackTrace(e));
        }
    }

    @FXML
    public void chooseProjectFolder() {
        DirectoryChooser directoryChooser = new DirectoryChooser();
        File selectedFolder = directoryChooser.showDialog(getPrimaryStage());
        if (selectedFolder != null) {
            projectFolderField.setText(selectedFolder.getAbsolutePath());
        }
    }

    @FXML
    public void generateCode() {
        if (tableName == null) {
            AlertUtil.showWarnAlert("请先在左侧选择数据库表");
            return;
        }
        String result = validateConfig();
		if (result != null) {
			AlertUtil.showErrorAlert(result);
			return;
		}
        GeneratorConfig generatorConfig = getGeneratorConfigFromUI();
        if (!checkDirs(generatorConfig)) {
            return;
        }

        MybatisGeneratorBridge bridge = new MybatisGeneratorBridge();
        bridge.setGeneratorConfig(generatorConfig);
        bridge.setDatabaseConfig(selectedDatabaseConfig);
        bridge.setIgnoredColumns(ignoredColumns);
        bridge.setColumnOverrides(columnOverrides);
        UIProgressCallback alert = new UIProgressCallback(Alert.AlertType.INFORMATION);
        bridge.setProgressCallback(alert);
        alert.show();
        PictureProcessStateController pictureProcessStateController = null;
        try {
            //Engage PortForwarding
            Session sshSession = DbUtil.getSSHSession(selectedDatabaseConfig);
            DbUtil.engagePortForwarding(sshSession, selectedDatabaseConfig);

            if (sshSession != null) {
                pictureProcessStateController = new PictureProcessStateController();
                pictureProcessStateController.setDialogStage(getDialogStage());
                pictureProcessStateController.startPlay();
            }

            bridge.generate();

            if (pictureProcessStateController != null) {
                Task task = new Task<Void>() {
                    @Override
                    protected Void call() throws Exception {
                        Thread.sleep(3000);
                        return null;
                    }
                };
                PictureProcessStateController finalPictureProcessStateController = pictureProcessStateController;
                task.setOnSucceeded(event -> {
                    finalPictureProcessStateController.close();
                });
                task.setOnFailed(event -> {
                    finalPictureProcessStateController.close();
                });
                new Thread(task).start();
            }
        } catch (Exception e) {
            e.printStackTrace();
            AlertUtil.showErrorAlert(e.getMessage());
            if (pictureProcessStateController != null) {
                pictureProcessStateController.close();
                pictureProcessStateController.playFailState(e.getMessage(), true);
            }
        }
    }

	private String validateConfig() {
		String projectFolder = projectFolderField.getText();
		if (StringUtils.isEmpty(projectFolder))  {
			return "项目目录不能为空";
		}
		if (StringUtils.isEmpty(domainObjectNameField.getText()))  {
			return "类名不能为空";
		}
		if (StringUtils.isAnyEmpty(modelTargetPackage.getText(),
                mapperTargetPackage.getText(),
                daoTargetPackage.getText(),
                serviceTargetPackage.getText(),
                serviceImplTargetPackage.getText(),
                controllerTargetPackage.getText())) {
			return "包名不能为空";
		}
		return null;
	}

	@FXML
    public void saveGeneratorConfig() {
        TextInputDialog dialog = new TextInputDialog("");
        dialog.setTitle("保存当前配置");
        dialog.setContentText("请输入配置名称");
        Optional<String> result = dialog.showAndWait();
        if (result.isPresent()) {
            String name = result.get();
            if (StringUtils.isEmpty(name)) {
                AlertUtil.showErrorAlert("名称不能为空");
                return;
            }
            _LOG.info("user choose name: {}", name);
            try {
                GeneratorConfig generatorConfig = getGeneratorConfigFromUI();
                generatorConfig.setName(name);
                ConfigHelper.deleteGeneratorConfig(name);
                ConfigHelper.saveGeneratorConfig(generatorConfig);
            } catch (Exception e) {
                _LOG.error("保存配置失败", e);
                AlertUtil.showErrorAlert("保存配置失败");
            }
        }
    }

    public GeneratorConfig getGeneratorConfigFromUI() {
        GeneratorConfig generatorConfig = new GeneratorConfig();
        generatorConfig.setProjectFolder(projectFolderField.getText());
        generatorConfig.setModelPackage(modelTargetPackage.getText());
        generatorConfig.setServicePackage(serviceTargetPackage.getText());
        generatorConfig.setServiceImplPackage(serviceImplTargetPackage.getText());
        generatorConfig.setControllerPackage(controllerTargetPackage.getText());
        generatorConfig.setGenerateKeys(generateKeysField.getText());
        generatorConfig.setModelPackageTargetFolder(modelTargetProject.getText());
        generatorConfig.setServicePackageTargetFolder(serviceTargetProject.getText());
        generatorConfig.setServiceImplPackageTargetFolder(serviceImplTargetProject.getText());
        generatorConfig.setControllerPackageTargetFolder(controllerTargetProject.getText());
        generatorConfig.setDaoPackage(daoTargetPackage.getText());
        generatorConfig.setDaoTargetFolder(daoTargetProject.getText());
        generatorConfig.setMapperName(mapperName.getText());
        generatorConfig.setMappingXMLPackage(mapperTargetPackage.getText());
        generatorConfig.setMappingXMLTargetFolder(mappingTargetProject.getText());
        generatorConfig.setTableName(tableNameField.getText());
        generatorConfig.setDomainObjectName(domainObjectNameField.getText());
        generatorConfig.setOffsetLimit(offsetLimitCheckBox.isSelected());
        generatorConfig.setComment(commentCheckBox.isSelected());
        generatorConfig.setOverrideXML(overrideXML.isSelected());
        generatorConfig.setNeedToStringHashcodeEquals(needToStringHashcodeEquals.isSelected());
        generatorConfig.setUseLombokPlugin(useLombokPlugin.isSelected());
        generatorConfig.setUseTableNameAlias(useTableNameAliasCheckbox.isSelected());
        generatorConfig.setNeedForUpdate(forUpdateCheckBox.isSelected());
        generatorConfig.setAnnotationDAO(annotationDAOCheckBox.isSelected());
        generatorConfig.setAnnotationMapperDAO(annotationMapperDAOCheckBox.isSelected());
        generatorConfig.setAnnotation(annotationCheckBox.isSelected());
        generatorConfig.setUseActualColumnNames(useActualColumnNamesCheckbox.isSelected());
        generatorConfig.setEncoding(encodingChoice.getValue());
        generatorConfig.setUseExample(useExample.isSelected());
        generatorConfig.setUseDAOExtendStyle(useDAOExtendStyle.isSelected());
        generatorConfig.setUseSchemaPrefix(useSchemaPrefix.isSelected());
        generatorConfig.setJsr310Support(jsr310Support.isSelected());
        return generatorConfig;
    }

    public void setGeneratorConfigIntoUI(GeneratorConfig generatorConfig) {
        projectFolderField.setText(generatorConfig.getProjectFolder());
        generateKeysField.setText(generatorConfig.getGenerateKeys());
        modelTargetPackage.setText(generatorConfig.getModelPackage());
        modelTargetProject.setText(generatorConfig.getModelPackageTargetFolder());
        serviceTargetPackage.setText(generatorConfig.getServicePackage());
        serviceTargetProject.setText(generatorConfig.getServicePackageTargetFolder());
        serviceImplTargetPackage.setText(generatorConfig.getServiceImplPackage());
        serviceImplTargetProject.setText(generatorConfig.getServiceImplPackageTargetFolder());
        controllerTargetPackage.setText(generatorConfig.getControllerPackage());
        controllerTargetProject.setText(generatorConfig.getControllerPackageTargetFolder());
        daoTargetPackage.setText(generatorConfig.getDaoPackage());
		daoTargetProject.setText(generatorConfig.getDaoTargetFolder());
		mapperTargetPackage.setText(generatorConfig.getMappingXMLPackage());
        mappingTargetProject.setText(generatorConfig.getMappingXMLTargetFolder());
        if (StringUtils.isBlank(tableNameField.getText())) {
            tableNameField.setText(generatorConfig.getTableName());
            mapperName.setText(generatorConfig.getMapperName());
            domainObjectNameField.setText(generatorConfig.getDomainObjectName());
        }
        offsetLimitCheckBox.setSelected(generatorConfig.isOffsetLimit());
        commentCheckBox.setSelected(generatorConfig.isComment());
        overrideXML.setSelected(generatorConfig.isOverrideXML());
        needToStringHashcodeEquals.setSelected(generatorConfig.isNeedToStringHashcodeEquals());
        useLombokPlugin.setSelected(generatorConfig.isUseLombokPlugin());
        useTableNameAliasCheckbox.setSelected(generatorConfig.getUseTableNameAlias());
        forUpdateCheckBox.setSelected(generatorConfig.isNeedForUpdate());
        annotationDAOCheckBox.setSelected(generatorConfig.isAnnotationDAO());
        annotationCheckBox.setSelected(generatorConfig.isAnnotation());
        useActualColumnNamesCheckbox.setSelected(generatorConfig.isUseActualColumnNames());
        encodingChoice.setValue(generatorConfig.getEncoding());
        useExample.setSelected(generatorConfig.isUseExample());
        useDAOExtendStyle.setSelected(generatorConfig.isUseDAOExtendStyle());
        useSchemaPrefix.setSelected(generatorConfig.isUseSchemaPrefix());
        jsr310Support.setSelected(generatorConfig.isJsr310Support());
    }

    @FXML
    public void openTableColumnCustomizationPage() {
        if (tableName == null) {
            AlertUtil.showWarnAlert("请先在左侧选择数据库表");
            return;
        }
        SelectTableColumnController controller = (SelectTableColumnController) loadFXMLPage("定制列", FXMLPage.SELECT_TABLE_COLUMN, true);
        controller.setMainUIController(this);
        try {
            // If select same schema and another table, update table data
            if (!tableName.equals(controller.getTableName())) {
                List<UITableColumnVO> tableColumns = DbUtil.getTableColumns(selectedDatabaseConfig, tableName);
                controller.setColumnList(FXCollections.observableList(tableColumns));
                controller.setTableName(tableName);
            }
            controller.showDialogStage();
        } catch (Exception e) {
            _LOG.error(e.getMessage(), e);
            AlertUtil.showErrorAlert(e.getMessage());
        }
    }

    public void setIgnoredColumns(List<IgnoredColumn> ignoredColumns) {
        this.ignoredColumns = ignoredColumns;
    }

    public void setColumnOverrides(List<ColumnOverride> columnOverrides) {
        this.columnOverrides = columnOverrides;
    }

    /**
     * 检查并创建不存在的文件夹
     *
     * @return
     */
    private boolean checkDirs(GeneratorConfig config) {
		List<String> dirs = new ArrayList<>();
		dirs.add(config.getProjectFolder());
		dirs.add(config.getProjectFolder().concat("/").concat(config.getModelPackageTargetFolder()));
		dirs.add(config.getProjectFolder().concat("/").concat(config.getDaoTargetFolder()));
		dirs.add(config.getProjectFolder().concat("/").concat(config.getMappingXMLTargetFolder()));
		boolean haveNotExistFolder = false;
		for (String dir : dirs) {
			File file = new File(dir);
			if (!file.exists()) {
				haveNotExistFolder = true;
			}
		}
		if (haveNotExistFolder) {
			Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
			alert.setContentText(FOLDER_NO_EXIST);
			Optional<ButtonType> optional = alert.showAndWait();
			if (optional.isPresent()) {
				if (ButtonType.OK == optional.get()) {
					try {
						for (String dir : dirs) {
							FileUtils.forceMkdir(new File(dir));
						}
						return true;
					} catch (Exception e) {
						AlertUtil.showErrorAlert("创建目录失败,请检查目录是否是文件而非目录");
					}
				} else {
					return false;
				}
			}
		}
        return true;
    }

    @FXML
    public void openTargetFolder() {
        GeneratorConfig generatorConfig = getGeneratorConfigFromUI();
        String projectFolder = generatorConfig.getProjectFolder();
        try {
            Desktop.getDesktop().browse(new File(projectFolder).toURI());
        }catch (Exception e) {
            AlertUtil.showErrorAlert("打开目录失败,请检查目录是否填写正确" + e.getMessage());
        }

    }
}



4、添加 GeneratorConfig功能


package com.zzg.mybatis.generator.model;


public class GeneratorConfig {

	/**
	 * 本配置的名称
	 */
	private String name;

	private String connectorJarPath;

	private String projectFolder;

	private String modelPackage;

	private String modelPackageTargetFolder;

	private String servicePackage;

	private String servicePackageTargetFolder;

	private String serviceImplPackage;

	private String serviceImplPackageTargetFolder;

	private String controllerPackage;

	private String controllerPackageTargetFolder;

	private String daoPackage;

	private String daoTargetFolder;

	private String mapperName;

	private String mappingXMLPackage;

	private String mappingXMLTargetFolder;

	private String tableName;

	private String domainObjectName;

	private boolean offsetLimit;

	private boolean comment;

	private boolean overrideXML;

	private boolean needToStringHashcodeEquals;

	private boolean useLombokPlugin;

	private boolean needForUpdate;

	private boolean annotationDAO;

	private boolean annotationMapperDAO;

	private boolean annotation;

	private boolean useActualColumnNames;

	private boolean useExample;

	private String generateKeys;

	private String encoding;

	private boolean useTableNameAlias;

	private boolean useDAOExtendStyle;

    private boolean useSchemaPrefix;

    private boolean jsr310Support;

    public boolean isJsr310Support() {
        return jsr310Support;
    }

    public void setJsr310Support(boolean jsr310Support) {
        this.jsr310Support = jsr310Support;
    }

    public boolean isUseSchemaPrefix() {
        return useSchemaPrefix;
    }

    public void setUseSchemaPrefix(boolean useSchemaPrefix) {
        this.useSchemaPrefix = useSchemaPrefix;
    }

	public boolean isUseExample() {
		return useExample;
	}

	public void setUseExample(boolean useExample) {
		this.useExample = useExample;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getTableName() {
		return tableName;
	}

	public void setTableName(String tableName) {
		this.tableName = tableName;
	}

	public String getDomainObjectName() {
		return domainObjectName;
	}

	public void setDomainObjectName(String domainObjectName) {
		this.domainObjectName = domainObjectName;
	}

	public String getConnectorJarPath() {
		return connectorJarPath;
	}

	public void setConnectorJarPath(String connectorJarPath) {
		this.connectorJarPath = connectorJarPath;
	}

	public String getProjectFolder() {
		return projectFolder;
	}

	public void setProjectFolder(String projectFolder) {
		this.projectFolder = projectFolder;
	}

	public String getModelPackage() {
		return modelPackage;
	}

	public void setModelPackage(String modelPackage) {
		this.modelPackage = modelPackage;
	}

	public String getModelPackageTargetFolder() {
		return modelPackageTargetFolder;
	}

	public void setModelPackageTargetFolder(String modelPackageTargetFolder) {
		this.modelPackageTargetFolder = modelPackageTargetFolder;
	}

	public String getServicePackage() {
		return servicePackage;
	}

	public void setServicePackage(String servicePackage) {
		this.servicePackage = servicePackage;
	}

	public String getServicePackageTargetFolder() {
		return servicePackageTargetFolder;
	}

	public void setServicePackageTargetFolder(String servicePackageTargetFolder) {
		this.servicePackageTargetFolder = servicePackageTargetFolder;
	}

	public String getServiceImplPackage() {
		return serviceImplPackage;
	}

	public void setServiceImplPackage(String serviceImplPackage) {
		this.serviceImplPackage = serviceImplPackage;
	}

	public String getServiceImplPackageTargetFolder() {
		return serviceImplPackageTargetFolder;
	}

	public void setServiceImplPackageTargetFolder(String serviceImplPackageTargetFolder) {
		this.serviceImplPackageTargetFolder = serviceImplPackageTargetFolder;
	}

	public String getControllerPackage() {
		return controllerPackage;
	}

	public void setControllerPackage(String controllerPackage) {
		this.controllerPackage = controllerPackage;
	}

	public String getControllerPackageTargetFolder() {
		return controllerPackageTargetFolder;
	}

	public void setControllerPackageTargetFolder(String controllerPackageTargetFolder) {
		this.controllerPackageTargetFolder = controllerPackageTargetFolder;
	}

	public String getDaoPackage() {
		return daoPackage;
	}

	public void setDaoPackage(String daoPackage) {
		this.daoPackage = daoPackage;
	}

	public String getDaoTargetFolder() {
		return daoTargetFolder;
	}

	public void setDaoTargetFolder(String daoTargetFolder) {
		this.daoTargetFolder = daoTargetFolder;
	}

	public String getMappingXMLPackage() {
		return mappingXMLPackage;
	}

	public void setMappingXMLPackage(String mappingXMLPackage) {
		this.mappingXMLPackage = mappingXMLPackage;
	}

	public String getMappingXMLTargetFolder() {
		return mappingXMLTargetFolder;
	}

	public void setMappingXMLTargetFolder(String mappingXMLTargetFolder) {
		this.mappingXMLTargetFolder = mappingXMLTargetFolder;
	}

	public boolean isOffsetLimit() {
		return offsetLimit;
	}

	public void setOffsetLimit(boolean offsetLimit) {
		this.offsetLimit = offsetLimit;
	}

	public boolean isComment() {
		return comment;
	}

	public void setComment(boolean comment) {
		this.comment = comment;
	}

    public boolean isNeedToStringHashcodeEquals() {
        return needToStringHashcodeEquals;
    }

    public void setNeedToStringHashcodeEquals(boolean needToStringHashcodeEquals) {
        this.needToStringHashcodeEquals = needToStringHashcodeEquals;
    }

	public boolean isUseLombokPlugin() {
		return useLombokPlugin;
	}

	public void setUseLombokPlugin(boolean useLombokPlugin) {
		this.useLombokPlugin = useLombokPlugin;
	}

	public boolean isNeedForUpdate() {
		return needForUpdate;
	}

	public void setNeedForUpdate(boolean needForUpdate) {
		this.needForUpdate = needForUpdate;
	}

	public boolean isAnnotationDAO() {
		return annotationDAO;
	}

	public void setAnnotationDAO(boolean annotationDAO) {
		this.annotationDAO = annotationDAO;
	}

	public boolean isAnnotationMapperDAO() {
		return annotationMapperDAO;
	}

	public void setAnnotationMapperDAO(boolean annotationMapperDAO) {
		this.annotationMapperDAO = annotationMapperDAO;
	}

	public boolean isAnnotation() {
		return annotation;
	}

	public void setAnnotation(boolean annotation) {
		this.annotation = annotation;
	}

	public boolean isUseActualColumnNames() {
		return useActualColumnNames;
	}

	public void setUseActualColumnNames(boolean useActualColumnNames) {
		this.useActualColumnNames = useActualColumnNames;
	}

	public String getMapperName() {
		return mapperName;
	}

	public void setMapperName(String mapperName) {
		this.mapperName = mapperName;
	}

	public String getGenerateKeys() {
		return generateKeys;
	}

	public void setGenerateKeys(String generateKeys) {
		this.generateKeys = generateKeys;
	}

    public String getEncoding() {
        return encoding;
    }

    public void setEncoding(String encoding) {
        this.encoding = encoding;
    }

	public boolean getUseTableNameAlias() {
		return useTableNameAlias;
	}

	public void setUseTableNameAlias(boolean useTableNameAlias) {
		this.useTableNameAlias = useTableNameAlias;
	}

	public boolean isUseTableNameAlias() {
		return useTableNameAlias;
	}

	public boolean isOverrideXML() {
		return overrideXML;
	}

	public void setOverrideXML(boolean overrideXML) {
		this.overrideXML = overrideXML;
	}

	public void setUseDAOExtendStyle(boolean useDAOExtendStyle) {
		this.useDAOExtendStyle = useDAOExtendStyle;
	}

	public boolean isUseDAOExtendStyle() {
		return useDAOExtendStyle;
	}
}



5、添加 PluginConfiguration 配置。

在这里插入图片描述

package com.zzg.mybatis.generator.bridge;

import com.jcraft.jsch.Session;
import com.zzg.mybatis.generator.controller.PictureProcessStateController;
import com.zzg.mybatis.generator.model.DatabaseConfig;
import com.zzg.mybatis.generator.model.DbType;
import com.zzg.mybatis.generator.model.GeneratorConfig;
import com.zzg.mybatis.generator.plugins.DbRemarksCommentGenerator;
import com.zzg.mybatis.generator.plugins.ServiceAndControllerGeneratorPlugin;
import com.zzg.mybatis.generator.util.ConfigHelper;
import com.zzg.mybatis.generator.util.DbUtil;
import org.apache.commons.lang3.StringUtils;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.api.ProgressCallback;
import org.mybatis.generator.api.ShellCallback;
import org.mybatis.generator.config.*;
import org.mybatis.generator.internal.DefaultShellCallback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;


public class MybatisGeneratorBridge {

	private static final Logger _LOG = LoggerFactory.getLogger(MybatisGeneratorBridge.class);

    private GeneratorConfig generatorConfig;

    private DatabaseConfig selectedDatabaseConfig;

    private ProgressCallback progressCallback;

    private List<IgnoredColumn> ignoredColumns;

    private List<ColumnOverride> columnOverrides;

    public MybatisGeneratorBridge() {
    }

    public void setGeneratorConfig(GeneratorConfig generatorConfig) {
        this.generatorConfig = generatorConfig;
    }

    public void setDatabaseConfig(DatabaseConfig databaseConfig) {
        this.selectedDatabaseConfig = databaseConfig;
    }

    public void generate() throws Exception {
        Configuration configuration = new Configuration();
        Context context = new Context(ModelType.CONDITIONAL);
        configuration.addContext(context);
		
        context.addProperty("javaFileEncoding", "UTF-8");
        
		String dbType = selectedDatabaseConfig.getDbType();
		String connectorLibPath = ConfigHelper.findConnectorLibPath(dbType);
	    _LOG.info("connectorLibPath: {}", connectorLibPath);
	    configuration.addClasspathEntry(connectorLibPath);
        // Table configuration
        TableConfiguration tableConfig = new TableConfiguration(context);
        tableConfig.setTableName(generatorConfig.getTableName());
        tableConfig.setDomainObjectName(generatorConfig.getDomainObjectName());
        if(!generatorConfig.isUseExample()) {
            tableConfig.setUpdateByExampleStatementEnabled(false);
            tableConfig.setCountByExampleStatementEnabled(false);
            tableConfig.setDeleteByExampleStatementEnabled(false);
            tableConfig.setSelectByExampleStatementEnabled(false);
        }

		context.addProperty("autoDelimitKeywords", "true");
		if (DbType.MySQL.name().equals(dbType) || DbType.MySQL_8.name().equals(dbType)) {
			tableConfig.setSchema(selectedDatabaseConfig.getSchema());
			// 由于beginningDelimiter和endingDelimiter的默认值为双引号("),在Mysql中不能这么写,所以还要将这两个默认值改为`
			context.addProperty("beginningDelimiter", "`");
			context.addProperty("endingDelimiter", "`");
		} else {
            tableConfig.setCatalog(selectedDatabaseConfig.getSchema());
	    }
        if (generatorConfig.isUseSchemaPrefix()) {
            if (DbType.MySQL.name().equals(dbType) || DbType.MySQL_8.name().equals(dbType)) {
                tableConfig.setSchema(selectedDatabaseConfig.getSchema());
            } else if (DbType.Oracle.name().equals(dbType)) {
                //Oracle的schema为用户名,如果连接用户拥有dba等高级权限,若不设schema,会导致把其他用户下同名的表也生成一遍导致中代码重复
                tableConfig.setSchema(selectedDatabaseConfig.getUsername());
            } else {
                tableConfig.setCatalog(selectedDatabaseConfig.getSchema());
            }
        }
        // 针对 postgresql 单独配置
		if (DbType.PostgreSQL.name().equals(dbType)) {
            tableConfig.setDelimitIdentifiers(true);
        }

        //添加GeneratedKey主键生成
		if (StringUtils.isNotEmpty(generatorConfig.getGenerateKeys())) {
            String dbType2 = dbType;
            if (DbType.MySQL.name().equals(dbType2) || DbType.MySQL_8.name().equals(dbType)) {
                dbType2 = "JDBC";
                //dbType为JDBC,且配置中开启useGeneratedKeys时,Mybatis会使用Jdbc3KeyGenerator,
                //使用该KeyGenerator的好处就是直接在一次INSERT 语句内,通过resultSet获取得到 生成的主键值,
                //并很好的支持设置了读写分离代理的数据库
                //例如阿里云RDS + 读写分离代理
                //无需指定主库
                //当使用SelectKey时,Mybatis会使用SelectKeyGenerator,INSERT之后,多发送一次查询语句,获得主键值
                //在上述读写分离被代理的情况下,会得不到正确的主键
            }
			tableConfig.setGeneratedKey(new GeneratedKey(generatorConfig.getGenerateKeys(), dbType2, true, null));
		}

        if (generatorConfig.getMapperName() != null) {
            tableConfig.setMapperName(generatorConfig.getMapperName());
        }
        // add ignore columns
        if (ignoredColumns != null) {
            ignoredColumns.forEach(tableConfig::addIgnoredColumn);
        }
        if (columnOverrides != null) {
            columnOverrides.forEach(tableConfig::addColumnOverride);
        }
        if (generatorConfig.isUseActualColumnNames()) {
			tableConfig.addProperty("useActualColumnNames", "true");
        }

		if(generatorConfig.isUseTableNameAlias()){
            tableConfig.setAlias(generatorConfig.getTableName());
        }

        JDBCConnectionConfiguration jdbcConfig = new JDBCConnectionConfiguration();
        if (DbType.MySQL.name().equals(dbType) || DbType.MySQL_8.name().equals(dbType)) {
	        jdbcConfig.addProperty("nullCatalogMeansCurrent", "true");
	        // useInformationSchema可以拿到表注释,从而生成类注释可以使用表的注释
	        jdbcConfig.addProperty("useInformationSchema", "true");
        }
        jdbcConfig.setDriverClass(DbType.valueOf(dbType).getDriverClass());
        jdbcConfig.setConnectionURL(DbUtil.getConnectionUrlWithSchema(selectedDatabaseConfig));
        jdbcConfig.setUserId(selectedDatabaseConfig.getUsername());
        jdbcConfig.setPassword(selectedDatabaseConfig.getPassword());
        if(DbType.Oracle.name().equals(dbType)){
            jdbcConfig.getProperties().setProperty("remarksReporting", "true");
        }

        // java model
        JavaModelGeneratorConfiguration modelConfig = new JavaModelGeneratorConfiguration();
        modelConfig.setTargetPackage(generatorConfig.getModelPackage());
        modelConfig.setTargetProject(generatorConfig.getProjectFolder() + "/" + generatorConfig.getModelPackageTargetFolder());
        // Mapper configuration
        SqlMapGeneratorConfiguration mapperConfig = new SqlMapGeneratorConfiguration();
        mapperConfig.setTargetPackage(generatorConfig.getMappingXMLPackage());
        mapperConfig.setTargetProject(generatorConfig.getProjectFolder() + "/" + generatorConfig.getMappingXMLTargetFolder());
        // DAO
        JavaClientGeneratorConfiguration daoConfig = new JavaClientGeneratorConfiguration();
        daoConfig.setConfigurationType("XMLMAPPER");
        daoConfig.setTargetPackage(generatorConfig.getDaoPackage());
        daoConfig.setTargetProject(generatorConfig.getProjectFolder() + "/" + generatorConfig.getDaoTargetFolder());


        // java service、service impl 、controller
        PluginConfiguration serviceAndControllerConfig = new PluginConfiguration();
        serviceAndControllerConfig.addProperty("type", "com.zzg.mybatis.generator.plugins.ServiceAndControllerGeneratorPlugin");
        //serviceAndControllerConfig.addProperty("targetProject", generatorConfig.getProjectFolder()+"/src/main/java");
        serviceAndControllerConfig.addProperty("serviceTargetProject", generatorConfig.getProjectFolder() + "/" + generatorConfig.getServicePackageTargetFolder());
        serviceAndControllerConfig.addProperty("serviceImplTargetProject", generatorConfig.getProjectFolder() + "/" + generatorConfig.getServiceImplPackageTargetFolder());
        serviceAndControllerConfig.addProperty("controllerTargetProject", generatorConfig.getProjectFolder() + "/" + generatorConfig.getControllerPackageTargetFolder());
        serviceAndControllerConfig.addProperty("servicePackage", "com.example.service");
        serviceAndControllerConfig.addProperty("serviceImplPackage", "com.example.service.impl");
        serviceAndControllerConfig.addProperty("controllerPackage", "com.example.controller");
        serviceAndControllerConfig.addProperty("serviceSuffix", "Service");
        serviceAndControllerConfig.addProperty("superServiceInterface", "org.aurochsframework.boot.commons.service.GeneralService");
        serviceAndControllerConfig.addProperty("superServiceImpl", "org.aurochsframework.boot.commons.service.AbstractGeneralService");
        serviceAndControllerConfig.addProperty("superController", "org.aurochsframework.boot.commons.controller.GeneralCrudController");
        serviceAndControllerConfig.setConfigurationType("com.zzg.mybatis.generator.plugins.ServiceAndControllerGeneratorPlugin");
        context.addPluginConfiguration(serviceAndControllerConfig);


        context.setId("myid");
        context.addTableConfiguration(tableConfig);
        context.setJdbcConnectionConfiguration(jdbcConfig);
        context.setJavaModelGeneratorConfiguration(modelConfig);
        context.setSqlMapGeneratorConfiguration(mapperConfig);
        context.setJavaClientGeneratorConfiguration(daoConfig);

        // Comment
        CommentGeneratorConfiguration commentConfig = new CommentGeneratorConfiguration();
        commentConfig.setConfigurationType(DbRemarksCommentGenerator.class.getName());
        if (generatorConfig.isComment()) {
            commentConfig.addProperty("columnRemarks", "true");
        }
        if (generatorConfig.isAnnotation()) {
            commentConfig.addProperty("annotations", "true");
        }
        context.setCommentGeneratorConfiguration(commentConfig);
        // set java file encoding
        context.addProperty(PropertyRegistry.CONTEXT_JAVA_FILE_ENCODING, generatorConfig.getEncoding());

        //实体添加序列化
        PluginConfiguration serializablePluginConfiguration = new PluginConfiguration();
        serializablePluginConfiguration.addProperty("type", "org.mybatis.generator.plugins.SerializablePlugin");
        serializablePluginConfiguration.setConfigurationType("org.mybatis.generator.plugins.SerializablePlugin");
        context.addPluginConfiguration(serializablePluginConfiguration);

        // Lombok 插件
        if (generatorConfig.isUseLombokPlugin()) {
            PluginConfiguration pluginConfiguration = new PluginConfiguration();
            pluginConfiguration.addProperty("type", "com.softwareloop.mybatis.generator.plugins.LombokPlugin");
            pluginConfiguration.setConfigurationType("com.softwareloop.mybatis.generator.plugins.LombokPlugin");
            context.addPluginConfiguration(pluginConfiguration);
        }
        // toString, hashCode, equals插件
        else if (generatorConfig.isNeedToStringHashcodeEquals()) {
            PluginConfiguration pluginConfiguration1 = new PluginConfiguration();
            pluginConfiguration1.addProperty("type", "org.mybatis.generator.plugins.EqualsHashCodePlugin");
            pluginConfiguration1.setConfigurationType("org.mybatis.generator.plugins.EqualsHashCodePlugin");
            context.addPluginConfiguration(pluginConfiguration1);
            PluginConfiguration pluginConfiguration2 = new PluginConfiguration();
            pluginConfiguration2.addProperty("type", "org.mybatis.generator.plugins.ToStringPlugin");
            pluginConfiguration2.setConfigurationType("org.mybatis.generator.plugins.ToStringPlugin");
            context.addPluginConfiguration(pluginConfiguration2);
        }
        // limit/offset插件
        if (generatorConfig.isOffsetLimit()) {
            if (DbType.MySQL.name().equals(dbType) || DbType.MySQL_8.name().equals(dbType)
		            || DbType.PostgreSQL.name().equals(dbType)) {
                PluginConfiguration pluginConfiguration = new PluginConfiguration();
                pluginConfiguration.addProperty("type", "com.zzg.mybatis.generator.plugins.MySQLLimitPlugin");
                pluginConfiguration.setConfigurationType("com.zzg.mybatis.generator.plugins.MySQLLimitPlugin");
                context.addPluginConfiguration(pluginConfiguration);
            }
        }
        //for JSR310
        if (generatorConfig.isJsr310Support()) {
            JavaTypeResolverConfiguration javaTypeResolverConfiguration = new JavaTypeResolverConfiguration();
            javaTypeResolverConfiguration.setConfigurationType("com.zzg.mybatis.generator.plugins.JavaTypeResolverJsr310Impl");
            context.setJavaTypeResolverConfiguration(javaTypeResolverConfiguration);
        }
        //forUpdate 插件
        if(generatorConfig.isNeedForUpdate()) {
            if (DbType.MySQL.name().equals(dbType)
                    || DbType.PostgreSQL.name().equals(dbType) || DbType.Oracle.name().equals(dbType)) {
                PluginConfiguration pluginConfiguration = new PluginConfiguration();
                pluginConfiguration.addProperty("type", "com.zzg.mybatis.generator.plugins.MySQLForUpdatePlugin");
                pluginConfiguration.setConfigurationType("com.zzg.mybatis.generator.plugins.MySQLForUpdatePlugin");
                context.addPluginConfiguration(pluginConfiguration);
            }
        }
        //repository 插件
        if(generatorConfig.isAnnotationDAO()) {
            if (DbType.MySQL.name().equals(dbType) || DbType.MySQL_8.name().equals(dbType)
                    || DbType.PostgreSQL.name().equals(dbType) || DbType.Oracle.name().equals(dbType)) {
                PluginConfiguration pluginConfiguration = new PluginConfiguration();
                pluginConfiguration.addProperty("type", "com.zzg.mybatis.generator.plugins.RepositoryPlugin");
                pluginConfiguration.setConfigurationType("com.zzg.mybatis.generator.plugins.RepositoryPlugin");
                context.addPluginConfiguration(pluginConfiguration);
            }
        }
        //mapper 插件
        if(generatorConfig.isAnnotationMapperDAO()) {
            if (DbType.MySQL.name().equals(dbType) || DbType.MySQL_8.name().equals(dbType)
                    || DbType.PostgreSQL.name().equals(dbType) || DbType.Oracle.name().equals(dbType)) {
                PluginConfiguration pluginConfiguration = new PluginConfiguration();
                pluginConfiguration.addProperty("type", "com.zzg.mybatis.generator.plugins.MapperPlugin");
                pluginConfiguration.setConfigurationType("com.zzg.mybatis.generator.plugins.MapperPlugin");
                context.addPluginConfiguration(pluginConfiguration);
            }
        }

        if (generatorConfig.isUseDAOExtendStyle()) {
            if (DbType.MySQL.name().equals(dbType) || DbType.MySQL_8.name().equals(dbType)
                    || DbType.PostgreSQL.name().equals(dbType) || DbType.Oracle.name().equals(dbType)) {
                PluginConfiguration pluginConfiguration = new PluginConfiguration();
				pluginConfiguration.addProperty("useExample", String.valueOf(generatorConfig.isUseExample()));
				pluginConfiguration.addProperty("type", "com.zzg.mybatis.generator.plugins.CommonDAOInterfacePlugin");
                pluginConfiguration.setConfigurationType("com.zzg.mybatis.generator.plugins.CommonDAOInterfacePlugin");
                context.addPluginConfiguration(pluginConfiguration);
            }
        }

        context.setTargetRuntime("MyBatis3");

        List<String> warnings = new ArrayList<>();
        Set<String> fullyqualifiedTables = new HashSet<>();
        Set<String> contexts = new HashSet<>();
        ShellCallback shellCallback = new DefaultShellCallback(true); // override=true
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(configuration, shellCallback, warnings);
        // if overrideXML selected, delete oldXML ang generate new one
		if (generatorConfig.isOverrideXML()) {
			String mappingXMLFilePath = getMappingXMLFilePath(generatorConfig);
			File mappingXMLFile = new File(mappingXMLFilePath);
			if (mappingXMLFile.exists()) {
				mappingXMLFile.delete();
			}
		}
        myBatisGenerator.generate(progressCallback, contexts, fullyqualifiedTables);
    }

    private String getMappingXMLFilePath(GeneratorConfig generatorConfig) {
		StringBuilder sb = new StringBuilder();
		sb.append(generatorConfig.getProjectFolder()).append("/");
		sb.append(generatorConfig.getMappingXMLTargetFolder()).append("/");
		String mappingXMLPackage = generatorConfig.getMappingXMLPackage();
		if (StringUtils.isNotEmpty(mappingXMLPackage)) {
			sb.append(mappingXMLPackage.replace(".", "/")).append("/");
		}
		if (StringUtils.isNotEmpty(generatorConfig.getMapperName())) {
			sb.append(generatorConfig.getMapperName()).append(".xml");
		} else {
			sb.append(generatorConfig.getDomainObjectName()).append("Mapper.xml");
		}

		return sb.toString();
	}

	public void setProgressCallback(ProgressCallback progressCallback) {
        this.progressCallback = progressCallback;
    }

    public void setIgnoredColumns(List<IgnoredColumn> ignoredColumns) {
        this.ignoredColumns = ignoredColumns;
    }

    public void setColumnOverrides(List<ColumnOverride> columnOverrides) {
        this.columnOverrides = columnOverrides;
    }
}

6、运行与测试结果

在这里插入图片描述
在这里插入图片描述

全文到处便完结了

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐