十、代码生成工具
虽然MyBatis是一个简单易学的框架,但是配置XML文件也是一件相当繁琐的一个过程,而且会出现很多不容易定位的错误。当在工作中需要生成大量对象的时候,有太多的重复劳动,简直是生无可恋。所以,官方开发了 MyBatis Generator。它只需要很少量的简单配置,就可以完成大量的表到Java对象的生成工作,拥有零出错和速度快的优点,让开发人员解放出来更专注于业务逻辑的开发。
MyBatis-Generator 是MyBatis提供的一个代码生成工具,简称MBG,可以帮助我们生成数据库表对应的持久化对象(也称作 Model、PO)、操作数据库的接口(dao)、简单 SQL 的 mapper(XML 形式或注解形式)。
其官方网址为:mybatis.org/generator/
10.1 引入
既然需要使用 MyBatis-Generator,那么在项目中就一定使用了 MyBatis 和某一种数据库,并且这些依赖应该已经在 Maven 中配置好了。例如 pom 文件中的配置:
一种是:
<!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
<dependency>
<groupid>org.mybatis.spring.boot</groupid>
<artifactid>mybatis-spring-boot-starter</artifactid>
<version>xx.xx.xx</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupid>mysql</groupid>
<artifactid>mysql-connector-java</artifactid>
<version>xx.xx.xx</version>
</dependency>
<!-- mybatis 代码自动生成器 -->
<!-- https://mvnrepository.com/artifact/org.mybatis.generator/mybatis-generator-core -->
<dependency>
<groupid>org.mybatis.generator</groupid>
<artifactid>mybatis-generator-core</artifactid>
<version>xx.xx.xx</version>
</dependency>
另外一种方式是:
<!--插件-->
<plugins>
<!-- https://mvnrepository.com/artifact/org.mybatis.generator/mybatis-generator-maven-plugin -->
<plugin>
<groupid>org.mybatis.generator</groupid>
<artifactid>mybatis-generator-maven-plugin</artifactid>
<version>xx.xx.xx</version>
<!--插件设置-->
<configuration>
<!-- 在控制台打印执行日志 -->
<verbose>true</verbose>
<!-- 重复生成时会覆盖之前的文件-->
<overwrite>true</overwrite>
<!--自动生成配置 如果名字是generatorConfig.xml可以省略配置-->
<!--<configurationFile>src/main/resources/generatorConfig.xml</configurationFile>-->
</configuration>
</plugin>
</plugins>
10.2 配置文件
MyBatis-Generator需要一个xml配置文件,来详细配置生成代码的各种细节。例如,在项目的resources目录下新建一个mybatis-generator-config.xml配置文件。
<generatorconfiguration>
<!-- 0个或1个 -->
<properties />
<!-- 0个或1个 -->
<classpathentry />
<!-- 1个或多个 -->
<context>
<!-- jdbc连接 -->
<jdbcconnection> ... </jdbcconnection>
<!-- schema为数据库名,tableName为对应的数据库表名 -->
<table> ... </table>
<!-- 注释 -->
<commentgenerator> ... </commentgenerator>
<!-- 类型转换 -->
<javatyperesolver> ... </javatyperesolver>
<!-- 生成实体类配置 -->
<javamodelgenerator> ... </javamodelgenerator>
<!-- 生成Mapper.xml文件配置 -->
<sqlmapgenerator> ... </sqlmapgenerator>
<!-- 生成Mapper.java 接口-->
<javaclientgenerator> ... </javaclientgenerator>
</context>
</generatorconfiguration>
10.2.1 properties
properties 用于加载配置项或者配置文件,在整个配置文件中就可以使用 ${propertyKey} 的方式来引用配置项,对于后面需要配置的 jdbc 和targetProject属性会很有用。该元素有两个属性,均用来指定外部配置文件的地址,这两个属性只能使用其中一个来指定,同时出现会报错。
| 属性 | 说明 |
|---|---|
| resource | 指定classpath下的属性文件。 使用类似 com/myproject/generatorConfig.properties这样的属性值 |
| url | 指定文件系统上的特定位置。 例如 file:///C:/myfolder/generatorConfig.properties |
10.2.2 classPathEntry
使用 classPathEntry 元素,可以在加载需要的额外的依赖包,其中,location 属性指明需要加载的 jar/zip 包的全路径。
<!-- 指定数据库驱动的jdbc驱动jar包的位置 -->
<classpathentry location="./mysql-connector-java-5.1.40.jar" />
10.2.3 context 元素
在 generationConfiguration 的子元素中,context 是核心元素,用于配置生成一组对象的环境。元素 context 有 4 个属性可供配置:
| 属性 | 说明 |
|---|---|
| id | 必填,上下文 id,用于在生成错误时提示; 保证多个 context 的 id 不重复就行。 |
| defaultModelType | 用于指定生成对象的样式 |
| targetRuntime | 用于指定生成的代码的运行时环境,这个配置会影响生成的 dao 和 mapper.xml 的内容 MyBatis3:默认的值,生成基于MyBatis3.x以上版本的内容,包括XXXBySample; MyBatis3Simple:类似MyBatis3,只是不生成XXXBySample; 还有其它可配置的值,详情见 官网 |
| introspectedColumnImpl | 类全限定名,用于扩展MBG |
元素 context 中,有多个子元素需要配置。同样的,context的子元素必须按照下面给出的次数和顺序进行配置:
10.2.3.1 property
用于为代码生成指定属性,或为其它元素指定属性。可以配置零个或多个,常见的property配置如下:
<!--
自动识别数据库关键字,默认为false。一般保留默认值,遇到数据库关键字时,按照table元素中columnOverride属性的配置进行覆盖;
如果设置为 true, 则需按照 SqlReservedWords 中定义的关键字列表,对关键字进行定界(分隔);
定界符(分隔符)参见 beginningDelimiter 和 endingDelimiter 的设置-->
<property name="autoDelimitKeywords" value="true" />
<!-- 生成的Java文件的编码 -->
<property name="javaFileEncoding" value="UTF-8" />
<!-- 格式化java代码 -->
<property name="javaFormatter" value="org.mybatis.generator.api.dom.DefaultJavaFormatter" />
<!-- 格式化XML代码 -->
<property name="xmlFormatter" value="org.mybatis.generator.api.dom.DefaultXmlFormatter" />
<!-- 指明数据库的用于标记数据库对象名的符号,比如ORACLE就是双引号,MYSQL默认是`反引号; -->
<property name="beginningDelimiter" value="`" />
<property name="endingDelimiter" value="`" />
10.2.3.2 plugin
配置插件,可以有零个或多个,常见的 plugin 配置有:
<!-- 使生成的 Model 实现 Serializable 接口 -->
<plugin type="org.mybatis.generator.plugins.SerializablePlugin" />
<!-- 为生成的 Model 覆写 toString() 方法 -->
<plugin type="org.mybatis.generator.plugins.ToStringPlugin" />
<!-- 为生成的 Model 覆写 equals() 和 hashCode() 方法 -->
<plugin type="org.mybatis.generator.plugins.EqualsHashCodePlugin" />
<!-- 生成mysql带有分页的sql的插件 这个可以自己写,-->
<plugin type="generator.MysqlPaginationPlugin" />
10.2.3.3 commentGenerator
可以配置0个或1个,用来配置生成的注释,默认是生成注释的,并且会在注释中添加时间等信息。但生成的注释信息没有任何价值,因而一般情况下都会屏蔽注释信息。其中type属性,以用来指定自己的注释实现类,继承DefaultCommentGenerator重写一些方法。
<commentgenerator>
<!-- 生成的注释中是否不包含时间信息,默认为false -->
<property name="suppressDate" value="false" />
<!-- 生成的注释中,时间的显示格式 -->
<property name="dateFormat" value="yyyy-MM-dd" />
<!-- 是否去除自动生成的注释 -->
<property name="suppressAllComments" value="true" />
<!-- 是否添加数据库内的注释 -->
<property name="addRemarkComments" value="true" />
</commentgenerator>
10.2.3.4 jdbcConnection
用于指定数据库连接信息,该元素必选,并且只能有一个,具体如下:
<!--
driverClass:访问数据库的JDBC驱动程序的完全限定类名
connectionURL:访问数据库的JDBC连接URL
userId:访问数据库的用户ID
password:访问数据库的密码
-->
<jdbcconnection driverclass="${spring.datasource.driverClassName}" connectionurl="${spring.datasource.url}" userid="${spring.datasource.username}" password="${spring.datasource.password}">
<!-- 针对oracle数据库 -->
<property name="remarksReporting" value="true"></property>
<!-- 针对mysql数据库 -->
<property name="useInformationSchema" value="true"></property>
</jdbcconnection>
其中,${propertyKey} 里面是引用的外部配置文件中的propertyValue,当然也可以写死,那么就不用在, 中引入此文件了。
10.2.3.5 javaTypeResolver
可以配置 0 或 1 个,用来配置JDBC到Java中的类型转换规则,如果不进行配置,则使用默认的转换规则:
- 如果精度>0或者长度>18,就会使用 java.math.BigDecimal
- 如果精度=0并且10<=长度<=18,就会使用 java.lang.Long
- 如果精度=0并且5<=长度<=9,就会使用 java.lang.Integer
- 如果精度=0并且长度<5,就会使用 java.lang.Short
10.2.3.6 javaModelGenerator
Java模型生成器,有且仅能配置一个,负责key类(context元素的defaultModelType属性)、JavaBean实体类、查询类的生成。
<!--生成entity类存放位置-->
<javamodelgenerator targetpackage="" targetproject="">
</javamodelgenerator>
javaModelGenerator 有两个属性:
| 属性 | 说明 |
|---|---|
| targetPackage | 生成实体类存放的包名,一般就是放在该包下 |
| targetProject | 指定目标项目路径(一个已存在的目录)。 可以是绝对路径或相对路径(如targetProject="src/main/java"),生成的内容会放到指定目录中 |
在 javaModelGenerator 元素中还可以配置多个 property 子元素,如下:
| 属性 | 默认值 | 说明 |
|---|---|---|
| enableSubPackages | false | 如果true,MBG会根据catalog和schema来生成子包。如果 false就会直接用targetPackage属性。 |
| immutable | false | 该属性用来配置实体类属性是否可变。 如果设置为 true,那么会使用构造方法入参,并且不会生成setter方法。如果为 false,实体类属性就可以改变。 |
| trimStrings | false | 是否对数据库查询结果进行trim操作 |
<javamodelgenerator targetpackage="org.dllwh.mybatis.model" targetproject="src\main\java">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javamodelgenerator>
10.2.3.7 sqlMapGenerator
可以配置0或1个,生成SQL Map的xml文件生成器。在MyBatis3之后,可以使用mapper.xml文件 + Mapper接口,或者只使用Mapper接口 + Annotation;所以,如果javaClientGenerator元素中配置了需要生成xml的话,这个元素就必须配置。该元素有targetPackage 和 targetProject 两个属性
<sqlmapgenerator targetpackage="org.dllwh.dao.impl" targetproject="src/main/java">
<!-- 针对数据库的一个配置,是否把 schema 作为字包名 -->
<property name="enableSubPackages" value="false" />
</sqlmapgenerator>
| 属性 | 说明 |
|---|---|
| targetPackage | 生成mapper存放的包名,一般就是放在该包下 |
| targetProject | 指定目标项目路径(一个已存在的目录)。 可以是绝对路径或相对路径(如targetProject="src/main/java"),生成的内容会放到指定目录中 |
配置示例:
<sqlmapgenerator targetpackage="org.dllwh.mybatis.xml" targetproject="src\main\resources">
<property name="enableSubPackages" value="true" />
</sqlmapgenerator>
10.2.3.8 javaClientGenerator
用于配置关于Mapper接口的生成,配置0或1个。注意,如果没有配置该元素,那么默认不会生成Mapper接口。其有 3 个属性:
| 属性 | 说明 |
|---|---|
| type | 该属性用于选择一个预定义的客户端代码(可以理解为Mapper接口)生成器,用户可以自定义实现 ANNOTATEDMAPPER:使用Mapper接口+Annotation的方式创建,不会生成对应的XML; MIXEDMAPPER:使用混合配置,会生成Mapper接口,并适当添加合适的Annotation,但是XML会生成在XML中; XMLMAPPER:会生成Mapper接口,接口完全依赖XML; |
| targetPackage | 生成interface 文件存放的包名,一般就是放在该包下 |
| targetProject | 指定目标项目路径(一个已存在的目录)。 可以是绝对路径或相对路径(如targetProject="src/main/java"),生成的内容会放到指定目录中 |
配置示例:
<javaclientgenerator type="XMLMAPPER" targetpackage="org.dllwh.dao" targetproject="src/main/java">
</javaclientgenerator>
10.2.3.9 table
指定数据库表,要生成哪些表,就写哪些表,要和数据库中对应,不能写错!一个 table 元素对应一张数据库表,如果想同时为多张表生成代码,需要配置多个 table 元素;或者可以将 tableName 设置为 % 来为全部表生成代码。
| 属性 | 是否必须 | 说明 |
|---|---|---|
| tableName | ✓ | 指定要生成的表名,可以使用SQL通配符匹配多个表。注意:大小写敏感问题 |
| schema | × | 为数据库名,oracle需要配置,mysql不需要配置 |
| catalog | × | 数据库的 catalog |
| domainObjectName | × | 生成实体对象的类名。如果没有指定,会自动根据表名来生成名称 |
| enableInsert | × | 指定是否生成insert语句,默认true |
| enableSelectByPrimaryKey | × | 指定是否生成按照主键查询对象的语句(就是getById或get),默认true |
| enableSelectByExample | × | 指定是否生成动态查询语句,默认true |
| enableUpdateByPrimaryKey | × | 指定是否生成按照主键update对象的语句,默认true |
| enableDeleteByPrimaryKey | × | 指定是否生成按照主键delete对象的语句,默认true |
| enableDeleteByExample | × | 指定是否生成动态delete语句,默认true |
| enableCountByExample | × | 指定是否生成动态查询总条数语句(用于分页的总条数查询),默认true |
| enableUpdateByExample | × | 指定是否生成动态修改语句(只修改对象中不为空的属性),默认true |
| delimitAllColumns | × | 设置是否所有生成的SQL中的列名都使用标识符引起来 |
此外,table 元素中还可以配置多个 property 和 columnOverride 等子元素。示例代码如下:
<!--
指定是否只生成domain类,如果设置为true,只生成domain类。
如果还配置了sqlMapGenerator,那么在 mapper.xml 文件中,只生成 resultMap 元素
-->
<property name="modelOnly" value="false" />
<!-- 可以为生成的类会继承这个类 -->
<!-- <property name="rootClass" value=""/> -->
<!-- 可以为生成的类添加一个父接口 -->
<!-- <property name="rootInterface" value=""/> -->
<!-- 如果设置为true,生成的model类会直接使用column本身的名字,而不会再使用驼峰命名方法 -->
<property name="useActualColumnNames" value="false" />
<!-- 生成主键的方法,如果设置了该元素,会在生成的<insert>元素中生成一条正确的<selectKey>元素 -->
<!-- <generatedKey column="id" sqlStatement="MySql" identity="true"/>-->
<generatedkey column="id" sqlStatement="SELECT LAST_INSERT_ID()" />
<!-- 用来修改表中某个列的属性,MBG 会根据修改后的配置来生成 domain 的属性;
column:要重新设置的列名;一个 table 元素中可以定义多个 columnOverride 元素哈 -->
<columnoverride column="show_status" javatype="" jdbctype="">
<!-- 使用 property 属性来指定列要生成的属性名称 -->
<property name="property" value="showStatus" />
<!-- javaType 用于指定生成的 domain 的属性类型,使用类型的全限定名-->
<property name="javaType" value="java.lang.Integer" />
<!-- jdbcType用于指定该列的JDBC类型
<property name="jdbcType" value=""/>
-->
</columnoverride>
10.3 配置示例
下面是一个简单的MyBatis-Generator 配置示例
<!-- 配置文件头 -->
<!--?xml version="1.0" encoding="UTF-8"?-->
<!-- 所有的配置均在根元素 generatorConfiguration 下 -->
<!--配置参考 http://mybatis.org/generator/configreference/xmlconfig.html-->
<generatorconfiguration>
<!-- 属性配置文件 -->
<properties resource="mybatis-generator.properties" />
<context id="MySqlContext" targetruntime="MyBatis3">
<property name="autoDelimitKeywords" value="true" />
<!-- 生成的 Java 文件的编码 -->
<property name="javaFileEncoding" value="UTF-8" />
<!-- 格式化 Java 代码 -->
<property name="javaFormatter" value="org.mybatis.generator.api.dom.DefaultJavaFormatter" />
<!-- 格式化 XML 代码 -->
<property name="xmlFormatter" value="org.mybatis.generator.api.dom.DefaultXmlFormatter" />
<property name="beginningDelimiter" value="`" />
<property name="endingDelimiter" value="`" />
<plugin type="org.mybatis.generator.plugins.SerializablePlugin" />
<commentgenerator>
<property name="suppressDate" value="true" />
<property name="dateFormat" value="yyyy-MM-dd" />
<!-- 是否去除自动生成的注释 true:是 : false:否 -->
<property name="suppressAllComments" value="true" />
<property name="addRemarkComments" value="true" />
</commentgenerator>
<!-- 配置数据库连接 -->
<jdbcconnection driverClass="${spring.datasource.driverClassName}" connectionURL="${spring.datasource.url}" userId="${spring.datasource.username}" password="${spring.datasource.password}" />
<!-- 生成PO类 -->
<javamodelgenerator targetpackage="${model.target.package}" targetproject="src/main/java">
<!-- 是否允许子包 -->
<property name="enableSubPackages" value="false" />
<!-- 是否对modal添加构造函数 -->
<property name="constructorBased" value="true" />
<!-- 是否清理从数据库中查询出的字符串左右两边的空白字符 -->
<property name="trimStrings" value="true" />
<!-- 建立modal对象是否不可改变 即生成的modal对象不会有setter方法,只有构造方法 -->
<property name="immutable" value="false" />
</javamodelgenerator>
<!-- 生成 Mapper 接口的位置 -->
<sqlmapgenerator targetpackage="${mapper.target.package}" targetproject="src/main/java">
<!-- 针对数据库的一个配置,是否把 schema 作为字包名 -->
<property name="enableSubPackages" value="false" />
</sqlmapgenerator>
<!-- 生成 Mapper XML 的位置 -->
<javaclientgenerator type="XMLMAPPER" targetpackage="${dao.target.package}" targetproject="src/main/java">
<!-- 针对 oracle 数据库的一个配置,是否把 schema 作为字包名 -->
<property name="enableSubPackages" value="false" />
</javaclientgenerator>
<!-- 设置数据库的表名和实体类名 -->
<property name="modelOnly" value="false" /><property name="useActualColumnNames" value="false" /><table tablename="sys_menu" enableinsert="true" enableselectbyprimarykey="true" enableselectbyexample="false" enableupdatebyprimarykey="true" enabledeletebyprimarykey="true" enabledeletebyexample="false" enablecountbyexample="false" enableupdatebyexample="false">
<!-- 如果设置为true,生成的model类会直接使用column本身的名字,而不会再使用驼峰命名方法 -->
</table>
</context>
</generatorconfiguration>
其中,mybatis-generator.properties 如下:
#Mybatis Generator configuration
# 自定义属性 - 创建人
custom.property.author=独泪了无痕
# 自定义属性 - 创建邮箱
custom.property.email=duleilewuhen@sina.com
# 类根地址
target.prefix=org.dllwh
# 生成实体类地址
model.target.package=org.dllwh.model
# 生成dao接口(mapper)地址
dao.target.package=org.dllwh.dao
# 生成mapper(xml)地址
mapper.target.package=org.dllwh.dao.impl
# 数据库信息
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/spring?useUnicode=true&characterEncoding=UTF-8&useSSL=true
spring.datasource.username=spring
spring.datasource.password=spring
10.4 测试类
使用 Java 代码编程运行,需要按照方式一在Maven的pom文件中引入依赖。然后在项目中新建一个 Java 类,代码类似下面:
import java.io.File;
import java.util.ArrayList;
import java.util.List;
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;
public class GeneratorSqlmap {
public void defaultCommentGenerator() throws Exception {
// MyBatis-Generator 执行过程中的警告信息
List<string> warnings = new ArrayList<string>();
// 当生成的代码重复时,覆盖原代码
boolean overwrite = true;
// 指向逆向工程配置文件
String genCfg = "/generatorConfig.xml";
// 读取 MyBatis-Generator 配置文件
File configFile = new File(GeneratorSqlmap.class.getResource(genCfg).getFile());
// 初始化配置解析器
ConfigurationParser cp = new ConfigurationParser(warnings);
// 调用配置解析器创建配置对象
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
// 创建一个MyBatisGenerator对象。MyBatisGenerator类是真正用来执行生成动作的类
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
// 执行生成代码
myBatisGenerator.generate(null);
// 输出警告信息
warnings.forEach(warning -> System.out.println(warning));
}
public static void main(String[] args) throws Exception {
try {
GeneratorSqlmap generatorSqlmap = new GeneratorSqlmap();
generatorSqlmap.defaultCommentGenerator();
} catch (Exception e) {
e.printStackTrace();
}
}
}
10.5 自定义扩展
10.5.1 自定义生成注释
commentGenerator 生成的是由 org.mybatis.generator.api.CommentGenerator 来控制的,这是一个接口,MyBatis Generator 的默认实现类是 org.mybatis.generator.internal.DefaultCommentGenerator。当你在 generatorConfig.xml 中配置了 commentGenerator 标签,那么默认状态下,生成注释的工作,将由 DefaultCommentGenerator 来完成。 运行 MyBatis-Generator后,查看生成的代码,怎么说呢,数据库注释倒是拿到了,但是生成的一堆其他信息,看着实在是太扎眼了。查看源码,发现这些内容已经写死在DefaultCommentGenerator中了,没有办法自定义。
自己动手丰衣足食,我们为啥不自己写个类实现CommentGenerator接口,然后自定义自己想要的注释呢。观察CommentGenerator接口,发现里面的方法非常多,不仅包含了生成 Java 实体注释对应的方法,还包括了生成XML中注释的方法。所以我们先定义我们自己的注释类CustomSQLCommentGenerator,继承DefaultCommentGenerator,重写我们需要的方法:
-
在项目中创建实体类CustomSQLCommentGenerator继承 DefaultCommentGenerato
-
配置 generatorConfig.xml设置我们自己的注释生成器:
<!--?xml version="1.0" encoding="UTF-8"?--> <!-- 所有的配置均在根元素 generatorConfiguration 下 --> <generatorconfiguration> <properties resource="mybatis-generator.properties" /> <context id="MySqlContext" targetruntime="MyBatis3"> <!-- 自定义comment生成器地址 --> <commentgenerator type="xx.xx.CustomSQLCommentGenerator"> </commentgenerator> </context> </generatorconfiguration> -
再次运行主类 MyBatis-Generator,成功的生成了类注释和字段注释~
-
还有一点儿需要补充的,上面的示例是继承了 DefaultCommentGenerator 实现的,也可以通过实现CommentGenerator 接口实现。
package org.dllwh.template.database.mybatis.custom; import static org.mybatis.generator.internal.util.StringUtility.*; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import java.util.Properties; import org.mybatis.generator.api.*; import org.mybatis.generator.api.dom.java.*; import org.mybatis.generator.api.dom.xml.XmlElement; import org.mybatis.generator.config.MergeConstants; public class CustomSQLCommentGenerator implements CommentGenerator { /** 属性,即配置在 commentGenerator 标签之内的 Property 标签 */ private Properties properties; /** 生成的注释中是否不包含时间信息,默认为false */ private boolean suppressDate; /** 是否去除自动生成的注释 */ private boolean suppressAllComments; /** 是否添加数据库内的注释 */ private boolean addRemarkComments; /** 生成的注释中,时间的显示格式 */ private SimpleDateFormat dateFormat; /** 自定义属性 - 创建人 */ private String author; /** 自定义属性 - 创建邮箱 */ private String email; /** 自定义属性 - 版本 */ private String version; /** 自定义属性 - 是否添加get注释 */ private boolean addGetComments; /** 自定义属性 - 是否添加set注释 */ private boolean addSetComments; public CustomSQLCommentGenerator() { super(); properties = new Properties(); suppressDate = false; suppressAllComments = false; addRemarkComments = true; } /** * 从该配置中的任何属性添加此实例的属性CommentGenerator配置,这个方法将在任何其他方法之前被调用。 */ @Override public void addConfigurationProperties(Properties properties) { // 获取自定义的 properties this.properties.putAll(properties); author = properties.getProperty("author", ""); email = properties.getProperty("email", ""); version = properties.getProperty("version", "V 1.0.1"); addGetComments = isTrue(properties.getProperty("addGetComments")) ? true : false; addSetComments = isTrue(properties.getProperty("addSetComments")) ? true : false; String dateFormatString = properties.getProperty("dateFormat", "yyyy-MM-dd HH:mm:ss"); if (stringHasValue(dateFormatString)) { dateFormat = new SimpleDateFormat(dateFormatString); } } /** * 生成xx.java文件(model)属性的注释,注释为空就不给属性添加。 */ @Override public void addFieldComment(Field field, IntrospectedTable introspectedTable, IntrospectedColumn introspectedColumn) { if (suppressAllComments) { return; } // 获取列注释 String remarks = introspectedColumn.getRemarks(); // 开启注释,并且数据库中comment有值 if (addRemarkComments && stringHasValue(remarks)) { // 通过换行符分割 String[] remarkLines = remarks.split(System.getProperty("line.separator")); int length = remarkLines.length; // 如果有多行,就换行显示 if (length > 1) { // 注释开始的地方 field.addJavaDocLine("/**"); for (int i = 0; i < length; i++) { field.addJavaDocLine(" * " + remarkLines[i]); } // 注释结束 field.addJavaDocLine(" */"); } else { field.addJavaDocLine("/** " + remarks + " */"); } } } /** * 实体类的静态字段,为空就不给属性添加。 */ @Override public void addFieldComment(Field field, IntrospectedTable introspectedTable) { if (suppressAllComments) { return; } } /** * 创建的数据表对应的类添加的注释 */ @Override public void addModelClassComment(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) { if (suppressAllComments || !addRemarkComments) { return; } topLevelClass.addJavaDocLine("/**"); topLevelClass.addJavaDocLine(" * 把今天最好的表现当作明天最新的起点..~"); topLevelClass.addJavaDocLine(" * "); topLevelClass.addJavaDocLine(" * Today the best performance as tomorrow newest starter!"); topLevelClass.addJavaDocLine(" * "); topLevelClass.addJavaDocLine(" * @类描述 : TODO(这里用一句话描述这个类的作用)"); // 数据库表名 String tableName = introspectedTable.getFullyQualifiedTableNameAtRuntime(); // 获取表注释 String tableRemarks = introspectedTable.getRemarks(); topLevelClass.addJavaDocLine(" * "); topLevelClass.addJavaDocLine(" * @数据表 : " + tableName); if (stringHasValue(tableRemarks)) { topLevelClass.addJavaDocLine(" * "); topLevelClass.addJavaDocLine(" * @数据表注释 : "); String[] remarkLines = tableRemarks.split(System.getProperty("line.separator")); for (String remarkLine : remarkLines) { topLevelClass.addJavaDocLine(" * " + remarkLine); } } topLevelClass.addJavaDocLine(" * "); if (stringHasValue(email) && stringHasValue(author)) { topLevelClass.addJavaDocLine(" * @author : <a href="\"mailto:"" + email "\"> " + author + "</a>"); } else if (stringHasValue(author) && !stringHasValue(email)) { topLevelClass.addJavaDocLine(" * @author : " + author); } else if (stringHasValue(email) && !stringHasValue(author)) { topLevelClass.addJavaDocLine(" * @email : " + email); } topLevelClass.addJavaDocLine(" * @创建时间 : " + getDateString()); topLevelClass.addJavaDocLine(" * @版本 : " + version); topLevelClass.addJavaDocLine(" * @since : " + System.getProperty("java.version")); topLevelClass.addJavaDocLine(" * @see <a href="\"\"">TODO(连接内容简介)</a>"); topLevelClass.addJavaDocLine(" */"); } /** * Java类的类注释 */ @Override public void addClassComment(InnerClass innerClass, IntrospectedTable introspectedTable) { if (suppressAllComments) { return; } } /** * Java类的类注释 */ @Override public void addClassComment(InnerClass innerClass, IntrospectedTable introspectedTable, boolean markAsDoNotDelete) { if (suppressAllComments) { return; } } /** * 为枚举添加注释 */ @Override public void addEnumComment(InnerEnum innerEnum, IntrospectedTable introspectedTable) { if (suppressAllComments) { return; } StringBuilder sb = new StringBuilder(); innerEnum.addJavaDocLine("/**"); sb.append(introspectedTable.getFullyQualifiedTable()); innerEnum.addJavaDocLine(sb.toString()); addJavadocTag(innerEnum, false); innerEnum.addJavaDocLine(" */"); } /** * 数据库对应实体类的Getter方法注解 */ @Override public void addGetterComment(Method method, IntrospectedTable introspectedTable, IntrospectedColumn introspectedColumn) { if (suppressAllComments || !addGetComments) { return; } method.addJavaDocLine("/**"); StringBuilder sb = new StringBuilder(); sb.append(" * "); if (stringHasValue(introspectedColumn.getRemarks())) { sb.append(introspectedColumn.getRemarks()); method.addJavaDocLine(sb.toString()); method.addJavaDocLine(" *"); } sb.setLength(0); sb.append(" * @return "); sb.append(introspectedColumn.getActualColumnName()); if (stringHasValue(introspectedColumn.getRemarks())) { sb.append(" - "); sb.append(introspectedColumn.getRemarks()); } method.addJavaDocLine(sb.toString()); method.addJavaDocLine(" */"); return; } /** * 数据库对应实体类的Setter方法注解 */ @Override public void addSetterComment(Method method, IntrospectedTable introspectedTable, IntrospectedColumn introspectedColumn) { if (suppressAllComments || !addSetComments) { return; } method.addJavaDocLine("/**"); StringBuilder sb = new StringBuilder(); if (stringHasValue(introspectedColumn.getRemarks())) { sb.append(" * "); sb.append(introspectedColumn.getRemarks()); method.addJavaDocLine(sb.toString()); method.addJavaDocLine(" *"); } Parameter parm = method.getParameters().get(0); sb.setLength(0); sb.append(" * @param "); sb.append(parm.getName()); if (stringHasValue(introspectedColumn.getRemarks())) { sb.append(" "); sb.append(introspectedColumn.getRemarks()); } method.addJavaDocLine(sb.toString()); method.addJavaDocLine(" */"); } /** * 普通方法的注释,这里主要是XXXMapper.java里面的接口方法的注释 */ @Override public void addGeneralMethodComment(Method method, IntrospectedTable introspectedTable) { if (suppressAllComments) { return; } method.addJavaDocLine("/**"); method.addJavaDocLine(" * " + method.getName()); method.addJavaDocLine(" * "); List<parameter> parameters = method.getParameters(); parameters.forEach(parameter -> method.addJavaDocLine(" * @param " + parameter.getName())); method.addJavaDocLine(" * "); // 如果有返回类型,添加@return String returnType = "void"; if (method.getReturnType() != null && !returnType.equals(method.getReturnType().toString())) { method.addJavaDocLine(" * @return "); } // addJavadocTag(method, false); method.addJavaDocLine(" */"); } /** * 给Java文件加注释,这个注释是在文件的顶部,也就是package上面。 */ @Override public void addJavaFileComment(CompilationUnit compilationUnit) { compilationUnit.addFileCommentLine("/*"); compilationUnit.addFileCommentLine("*"); compilationUnit.addFileCommentLine("* " + compilationUnit.getType().getShortName() + ".java"); compilationUnit.addFileCommentLine("* Copyright(C) 2017-2020 xxx公司"); compilationUnit.addFileCommentLine("* ALL rights reserved."); compilationUnit.addFileCommentLine("* @date " + getDateString() + ""); compilationUnit.addFileCommentLine("*/"); } /** * 实体类对应的mapper.xml注释,mapper类不加注释,如有需要参考 DefaultCommentGenerator */ @Override public void addComment(XmlElement xmlElement) { if (suppressAllComments) { return; } } /** * 为调用此方法作为根元素的第一个子节点添加注释。 */ @Override public void addRootComment(XmlElement rootElement) { } /** * @方法描述 : 返回格式化的日期字符串以包含在Javadoc标记中和XML注释。 如果您不想要日期,则可以返回null在这些文档元素中。 * @return 格式化后的日期 */ protected String getDateString() { if (suppressDate) { return null; } else if (dateFormat != null) { return dateFormat.format(new Date()); } else { return new Date().toString(); } } /** * @方法描述: 此方法为其添加了自定义javadoc标签。 * * @param javaElement * @param markAsDoNotDelete */ protected void addJavadocTag(JavaElement javaElement, boolean markAsDoNotDelete) { javaElement.addJavaDocLine(" *"); StringBuilder sb = new StringBuilder(); sb.append(" * "); sb.append(MergeConstants.NEW_ELEMENT_TAG); if (markAsDoNotDelete) { sb.append(" do_not_delete_during_merge"); } String s = getDateString(); if (s != null) { sb.append(' '); sb.append(s); } javaElement.addJavaDocLine(sb.toString()); } }
10.5.2 修改Mapper文件名
import static org.mybatis.generator.internal.util.StringUtility.stringHasValue;
import static org.mybatis.generator.internal.util.messages.Messages.getString;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.PluginAdapter;
public class RenameJavaMapperPlugins extends PluginAdapter {
private String searchString;
private String replaceString;
private Pattern pattern;
public boolean validate(List<string> warnings) {
searchString = properties.getProperty("searchString");
replaceString = properties.getProperty("replaceString");
boolean valid = stringHasValue(searchString) && stringHasValue(replaceString);
if (valid) {
pattern = Pattern.compile(searchString);
} else {
if (!stringHasValue(searchString)) {
warnings.add(getString("ValidationError.18", "RenameExampleClassPlugin", "searchString"));
}
if (!stringHasValue(replaceString)) {
warnings.add(getString("ValidationError.18", "RenameExampleClassPlugin", "replaceString"));
}
}
return valid;
}
@Override
public void initialized(IntrospectedTable introspectedTable) {
String oldType = introspectedTable.getMyBatis3JavaMapperType();
Matcher matcher = pattern.matcher(oldType);
oldType = matcher.replaceAll(replaceString);
introspectedTable.setMyBatis3JavaMapperType(oldType);
}
}
10.5.3 自定义的lombok配置
// 在类上添加注解
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class CustomLombokPluginAviationInfo {
}
// 在时间字段上自动添加注解
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date endTime;
import static org.mybatis.generator.internal.util.StringUtility.stringHasValue;
import static org.mybatis.generator.internal.util.messages.Messages.getString;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Properties;
import org.apache.commons.lang3.StringUtils;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.Plugin;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.java.Field;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.api.dom.java.Interface;
import org.mybatis.generator.api.dom.java.Method;
import org.mybatis.generator.api.dom.java.TopLevelClass;
public class CustomLombokPlugin extends PluginAdapter {
private final Collection<annotations> annotations;
/** 继承的父类 */
private String supperClass;
/** 过滤的属性字段 */
private String ignoreFields;
/** 自定义属性 - 创建人 */
private String author;
/** 自定义属性 - 创建邮箱 */
private String email;
/** 自定义属性 - 版本 */
private String version;
public CustomLombokPlugin() {
annotations = new LinkedHashSet<>(Annotations.values().length);
}
@Override
public boolean validate(List<string> warnings) {
boolean valid = true;
supperClass = properties.getProperty("supperClass");
ignoreFields = properties.getProperty("ignoreFields");
author = properties.getProperty("author", "");
email = properties.getProperty("email", "");
version = properties.getProperty("version", "V 1.0.1");
try {
Class.forName(supperClass);
} catch (ClassNotFoundException e) {
warnings.add(getString("ValidationError.18", "CustomLombokPlugin", "supperClass"));
}
return valid;
}
/**
* 获取表
*/
@Override
public boolean modelBaseRecordClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
addAnnotations(topLevelClass);
return true;
}
@Override
public boolean modelPrimaryKeyClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
addAnnotations(topLevelClass);
return true;
}
@Override
public boolean modelRecordWithBLOBsClassGenerated(TopLevelClass topLevelClass,
IntrospectedTable introspectedTable) {
addAnnotations(topLevelClass);
return true;
}
/**
* 设置get方法(使用lombok不需要,直接返回false)
*/
@Override
public boolean modelGetterMethodGenerated(Method method, TopLevelClass topLevelClass,
IntrospectedColumn introspectedColumn, IntrospectedTable introspectedTable, ModelClassType modelClassType) {
return false;
}
/**
* 设置set方法(使用lombok不需要,直接返回false)
*/
@Override
public boolean modelSetterMethodGenerated(Method method, TopLevelClass topLevelClass,
IntrospectedColumn introspectedColumn, IntrospectedTable introspectedTable, ModelClassType modelClassType) {
return false;
}
/**
* 设置lombok注解 <br>
*/
private void addAnnotations(TopLevelClass topLevelClass) {
for (Annotations annotation : annotations) {
// 添加domain的import
topLevelClass.addImportedType(annotation.javaType);
// 添加domain的注解
topLevelClass.addAnnotation(annotation.asAnnotation());
}
/**
* 设置父类
*/
if (supperClass != null) {
FullyQualifiedJavaType fullyQualifiedJavaType = new FullyQualifiedJavaType(supperClass);
topLevelClass.addImportedType("lombok.EqualsAndHashCode");
topLevelClass.addImportedType(fullyQualifiedJavaType.getFullyQualifiedName());
topLevelClass.addAnnotation("@EqualsAndHashCode(callSuper = false)");
topLevelClass.setSuperClass(fullyQualifiedJavaType.getShortName());
}
/**
* 将需要忽略生成的属性过滤掉
*/
List<field> fields = topLevelClass.getFields();
if (null != ignoreFields && !"".equals(ignoreFields)) {
String[] field = ignoreFields.split(",");
for (String ignoreField : field) {
for (int i = 0; i < fields.size(); i++) {
Field tableField = fields.get(i);
if (ignoreField.equalsIgnoreCase(tableField.getName())) {
fields.remove(tableField);
i--;
}
}
}
}
}
/**
* entity类设置
*
* @param properties
*/
@Override
public void setProperties(Properties properties) {
super.setProperties(properties);
// @Data is default annotation
annotations.add(Annotations.DATA);
annotations.add(Annotations.ALL_ARGS_CONSTRUCTOR);
annotations.add(Annotations.NO_ARGS_CONSTRUCTOR);
annotations.add(Annotations.BUILDER);
for (String annotationName : properties.stringPropertyNames()) {
if (annotationName.contains(".")) {
continue;
}
String value = properties.getProperty(annotationName);
if (!Boolean.parseBoolean(value)) {
// The annotation is disabled, skip it
continue;
}
Annotations annotation = Annotations.getValueOf(annotationName);
if (annotation == null) {
continue;
}
String optionsPrefix = annotationName + ".";
for (String propertyName : properties.stringPropertyNames()) {
if (!propertyName.startsWith(optionsPrefix)) {
// A property not related to this annotation
continue;
}
String propertyValue = properties.getProperty(propertyName);
annotation.appendOptions(propertyName, propertyValue);
annotations.add(annotation);
annotations.addAll(Annotations.getDependencies(annotation));
}
}
}
/**
* mapper类设置注解以及Mapper文件的注释
*/
@Override
public boolean clientGenerated(Interface interfaze, TopLevelClass topLevelClass,
IntrospectedTable introspectedTable) {
interfaze.addJavaDocLine("/**");
interfaze.addJavaDocLine(" * 把今天最好的表现当作明天最新的起点..~");
interfaze.addJavaDocLine(" * ");
interfaze.addJavaDocLine(" * Today the best performance as tomorrow newest starter!");
interfaze.addJavaDocLine(" * ");
interfaze.addJavaDocLine(" * @类描述 : TODO(这里用一句话描述这个类的作用)");
// 获取表注释
String tableRemarks = introspectedTable.getRemarks();
if (stringHasValue(tableRemarks)) {
interfaze.addJavaDocLine(" * ");
interfaze.addJavaDocLine(" * @数据表注释 : ");
String[] remarkLines = tableRemarks.split(System.getProperty("line.separator"));
for (String remarkLine : remarkLines) {
interfaze.addJavaDocLine(" * " + remarkLine);
}
}
if (stringHasValue(tableRemarks)) {
interfaze.addJavaDocLine(" * ");
interfaze.addJavaDocLine(" * @数据表注释 : ");
String[] remarkLines = tableRemarks.split(System.getProperty("line.separator"));
for (String remarkLine : remarkLines) {
interfaze.addJavaDocLine(" * " + remarkLine);
}
}
interfaze.addJavaDocLine(" * ");
if (stringHasValue(email) && stringHasValue(author)) {
interfaze.addJavaDocLine(" * @author : <a href="\"mailto:"" + email "\"> " + author + "</a>");
} else if (stringHasValue(author) && StringUtils.isBlank(email)) {
interfaze.addJavaDocLine(" * @author : " + author);
}
// interfaze.addJavaDocLine(" * @创建时间 : " + getDateString());
interfaze.addJavaDocLine(" * @版本 : " + version);
interfaze.addJavaDocLine(" * @since : " + System.getProperty("java.version"));
interfaze.addJavaDocLine(" * @see <a href="\"\"">TODO(连接内容简介)</a>");
interfaze.addJavaDocLine(" */");
interfaze.addImportedType(new FullyQualifiedJavaType("org.apache.ibatis.annotations.Mapper"));
interfaze.addAnnotation("@Mapper");
return true;
}
/**
* entity字段设置
*/
@Override
public boolean modelFieldGenerated(Field field, TopLevelClass topLevelClass, IntrospectedColumn introspectedColumn,
IntrospectedTable introspectedTable, Plugin.ModelClassType modelClassType) {
if (field.getType().getShortNameWithoutTypeArguments().equals("Date")) {
field.getAnnotations().add(Annotations.DATE_TIME_FORMAT.asAnnotation());
field.getAnnotations().add(Annotations.JSON_FORMAT.asAnnotation());
topLevelClass.addImportedType(Annotations.DATE_TIME_FORMAT.javaType);
topLevelClass.addImportedType(Annotations.JSON_FORMAT.javaType);
}
return true;
}
enum Annotations {
DATA("data", "@Data", "lombok.Data"), BUILDER("builder", "@Builder", "lombok.Builder"),
ALL_ARGS_CONSTRUCTOR("allArgsConstructor", "@AllArgsConstructor", "lombok.AllArgsConstructor"),
NO_ARGS_CONSTRUCTOR("noArgsConstructor", "@NoArgsConstructor", "lombok.NoArgsConstructor"),
ACCESSORS("accessors", "@Accessors", "lombok.experimental.Accessors"),
TO_STRING("toString", "@ToString", "lombok.ToString"),
DATE_TIME_FORMAT("dateTimeFormat", "@DateTimeFormat(pattern = \"yyyy-MM-dd HH:mm:ss\")",
"org.springframework.format.annotation.DateTimeFormat"),
JSON_FORMAT("jsonFormat", "@JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\")",
"com.fasterxml.jackson.annotation.JsonFormat");
private final String paramName;
private final String name;
private final FullyQualifiedJavaType javaType;
private final List<string> options;
Annotations(String paramName, String name, String className) {
this.paramName = paramName;
this.name = name;
this.javaType = new FullyQualifiedJavaType(className);
this.options = new ArrayList<string>();
}
public static Annotations getValueOf(String paramName) {
for (Annotations annotation : Annotations.values()) {
if (String.CASE_INSENSITIVE_ORDER.compare(paramName, annotation.paramName) == 0) {
return annotation;
}
}
return null;
}
public static Collection<annotations> getDependencies(Annotations annotation) {
if (annotation == ALL_ARGS_CONSTRUCTOR) {
return Collections.singleton(NO_ARGS_CONSTRUCTOR);
} else {
return Collections.emptyList();
}
}
// A trivial quoting.
// Because Lombok annotation options type is almost String or boolean.
private static String quote(String value) {
if (Boolean.TRUE.toString().equals(value) || Boolean.FALSE.toString().equals(value))
// case of boolean, not passed as an array.
{
return value;
}
return value.replaceAll("[\\w]+", "\"$0\"");
}
public void appendOptions(String key, String value) {
String keyPart = key.substring(key.indexOf(".") + 1);
String valuePart = value.contains(",") ? String.format("{%s}", value) : value;
this.options.add(String.format("%s=%s", keyPart, quote(valuePart)));
}
public String asAnnotation() {
if (options.isEmpty()) {
return name;
}
StringBuilder sb = new StringBuilder();
sb.append(name);
sb.append("(");
boolean first = true;
for (String option : options) {
if (first) {
first = false;
} else {
sb.append(", ");
}
sb.append(option);
}
sb.append(")");
return sb.toString();
}
}
}
10.5.4 自定义的生成配置
<!--?xml version="1.0" encoding="UTF-8"?-->
<!-- 所有的配置均在根元素 generatorConfiguration 下 -->
<generatorconfiguration>
<properties resource="mybatis-generator.properties" />
<context id="MySqlContext" targetruntime="MyBatis3">
<property name="autoDelimitKeywords" value="true" />
<!-- 生成的 Java 文件的编码 -->
<property name="javaFileEncoding" value="UTF-8" />
<!-- 格式化 Java 代码 -->
<property name="javaFormatter" value="org.mybatis.generator.api.dom.DefaultJavaFormatter" />
<!-- 格式化 XML 代码 -->
<property name="xmlFormatter" value="org.mybatis.generator.api.dom.DefaultXmlFormatter" />
<property name="beginningDelimiter" value="`" />
<property name="endingDelimiter" value="`" />
<plugin type="org.mybatis.generator.plugins.SerializablePlugin" />
<!-- 自定义lomBok插件 -->
<plugin type="org.dllwh.template.database.mybatis.custom.CustomLombokPlugin">
<property name="defaultSerialVersionUID" value="true" />
<property name="supperClass" value="" />
<property name="ignoreFields" value="" />
<property name="author" value="${custom.property.author}" />
<property name="email" value="${custom.property.email}" />
<property name="version" value="V 1.0.1" />
</plugin>
<!-- 自定义JavaMapper文件名 -->
<plugin type="org.dllwh.template.database.mybatis.custom.RenameJavaMapperPlugins">
<property name="searchString" value="Mapper$" />
<property name="replaceString" value="Dao" />
</plugin>
<!-- 自定义comment生成器地址 -->
<commentgenerator type="org.dllwh.template.database.mybatis.custom.CustomSQLCommentGenerator">
<property name="author" value="${custom.property.author}" />
<property name="email" value="${custom.property.email}" />
<property name="version" value="V 1.0.1" />
<property name="addGetComments" value="false" />
<property name="addSetComments" value="false" />
</commentgenerator>
<!-- 配置数据库连接 -->
<jdbcconnection driverClass="${spring.datasource.driverClassName}" connectionURL="${spring.datasource.url}" userId="${spring.datasource.username}" password="${spring.datasource.password}" />
<!-- 生成实体的位置 -->
<javamodelgenerator targetpackage="${model.target.package}" targetproject="src/main/java">
<property name="trimStrings" value="true" />
</javamodelgenerator>
<!-- 生成 Mapper 接口的位置 -->
<sqlmapgenerator targetpackage="${mapper.target.package}" targetproject="src/main/java">
</sqlmapgenerator>
<!-- 生成 Mapper XML 的位置 -->
<javaclientgenerator type="XMLMAPPER" targetpackage="${dao.target.package}" targetproject="src/main/java">
</javaclientgenerator>
<!-- 设置数据库的表名和实体类名 -->
<property name="modelOnly" value="false" /><property name="useActualColumnNames" value="false" /><columnoverride column="excep_detail" javaType="java.lang.String" jdbcType="text" /><columnoverride column="parameter" javaType="java.lang.String" jdbcType="text" /><columnoverride column="result" javaType="java.lang.String" jdbcType="text" /><table tablename="sys__operate_log" enableinsert="true" enableselectbyprimarykey="true" enableselectbyexample="false" enableupdatebyprimarykey="true" enabledeletebyprimarykey="true" enabledeletebyexample="false" enablecountbyexample="false" enableupdatebyexample="false">
<!-- 如果设置为true,生成的model类会直接使用column本身的名字,而不会再使用驼峰命名方法 -->
<!-- -->
</table>
</context>
</generatorconfiguration>
```</annotations></string></string></field></string></annotations></string></parameter></string></string>