一、什么是逆向工程
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中的 & 要改成转义字符 & 这里传上来页面自动给转成了 &。
还有就是不同的数据库中不能含有相同的表,例如数据库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接口生成的方法介绍:
- long countByExample(TUserExample example):按条件计数
- int deleteByExample(TUserExample example):按条件删除
- int deleteByPrimaryKey(Integer id):按主键删除
- int insert(TUser record):插入数据(返回值为ID)
- int insertSelective(TUser record):插入数据,只插入值不为null的字段,内部动态sql判断
- List selectByExample(TUserExample example):按条件查询,传入null表示查询所有
- TUser selectByPrimaryKey(Integer id):按主键查询
- int updateByExampleSelective(@Param(“record”) TUser record, @Param(“example”) TUserExample example):按条件更新值不为null的字段
- int updateByExample(@Param(“record”) TUser record, @Param(“example”) TUserExample example):按条件更新
- int updateByPrimaryKeySelective(TUser record):按主键更新值不为null的字段
- 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、运行与测试结果
全文到处便完结了