四、代码生成器

4.1 通用 Mapper 专用代码生成器

使用该插件可以很方便的生成实体类、Mapper接口以及对应的XML文件。

本篇文档就是讲述如何在 MBG 中使用该插件。

首先对MBG不太了解的可以先阅读下面的文档

Mybatis Geneator 详解 http://blog.csdn.net/isea533/article/details/42102297

4.1.1 简单介绍

通用 Mapper 专用代码生成器生成的 Model 会在原有基础上增加 @Table,@Id,@Column 等注解,方便自动会数据库字段进行映射。

运行MBG有多种方法,这里只介绍两种比较常见的方法。并且有关的内容会针对这样的运行方式进行配置。

4.1.2 使用Java编码方式运行MBG

在 generatr 项目测试代码中包含这个例子。

https://github.com/abel533/Mapper/blob/master/generator/src/test/java/tk/mybatis/mapper/generator/Generator.java

使用这种方式,需要引入 MBG 的依赖,同时项目中应该已经有通用 Mapper 的依赖了。

  1. <!-- https://mvnrepository.com/artifact/org.mybatis.generator/mybatis-generator-core -->
  2. <dependency>
  3. <groupId>org.mybatis.generator</groupId>
  4. <artifactId>mybatis-generator-core</artifactId>
  5. <version>1.3.6</version>
  6. </dependency>
  7. <!-- 通用 Mapper -->
  8. <!-- https://mvnrepository.com/artifact/tk.mybatis/mapper -->
  9. <dependency>
  10. <groupId>tk.mybatis</groupId>
  11. <artifactId>mapper</artifactId>
  12. <version>4.0.0</version>
  13. </dependency>
  14. <!-- 如果你只需要用到通用 Mapper 中的插件,可以只引入 mapper-generator -->
  15. <!-- 注意,这个包不需要和上面的 mapper 同时引入,mapper 中包含 generator -->
  16. <!-- https://mvnrepository.com/artifact/tk.mybatis/mapper-generator -->
  17. <dependency>
  18. <groupId>tk.mybatis</groupId>
  19. <artifactId>mapper-generator</artifactId>
  20. <version>1.0.0</version>
  21. </dependency>

Java代码很容易,和测试中的一样:

  1. public static void main(String[] args) throws Exception {
  2. List<String> warnings = new ArrayList<String>();
  3. boolean overwrite = true;
  4. ConfigurationParser cp = new ConfigurationParser(warnings);
  5. Configuration config =
  6. cp.parseConfiguration(getResourceAsStream("generatorConfig.xml"));
  7. DefaultShellCallback callback = new DefaultShellCallback(overwrite);
  8. MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
  9. myBatisGenerator.generate(null);
  10. for (String warning : warnings) {
  11. System.out.println(warning);
  12. }
  13. }

注意,测试中还有 startDB 等方法,这是因为测试中使用的 hsqldb 内存数据库。

这段代码容易,最主要的一个内容是"generatorConfig.xml",我们应该如何配置该类。

下面是一个generatorConfig.xml的例子:

  1. <!DOCTYPE generatorConfiguration
  2. PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
  3. "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
  4. <!--suppress MybatisGenerateCustomPluginInspection -->
  5. <generatorConfiguration>
  6. <context id="Mysql" targetRuntime="MyBatis3Simple" defaultModelType="flat">
  7. <property name="javaFileEncoding" value="UTF-8"/>
  8. <property name="useMapperCommentGenerator" value="false"/>
  9. <plugin type="tk.mybatis.mapper.generator.MapperPlugin">
  10. <property name="mappers" value="tk.mybatis.mapper.common.Mapper"/>
  11. <property name="caseSensitive" value="true"/>
  12. <property name="forceAnnotation" value="true"/>
  13. <property name="beginningDelimiter" value="`"/>
  14. <property name="endingDelimiter" value="`"/>
  15. </plugin>
  16. <jdbcConnection driverClass="org.hsqldb.jdbcDriver"
  17. connectionURL="jdbc:hsqldb:mem:generator"
  18. userId="sa"
  19. password="">
  20. </jdbcConnection>
  21. <!--MyBatis 生成器只需要生成 Model-->
  22. <javaModelGenerator targetPackage="test.model"
  23. targetProject="generator/src/test/java"/>
  24. <table tableName="user_info">
  25. <generatedKey column="id" sqlStatement="JDBC"/>
  26. </table>
  27. <table tableName="country">
  28. <generatedKey column="id" sqlStatement="JDBC"/>
  29. </table>
  30. </context>
  31. </generatorConfiguration>

和一般的配置相比,这里只是多了一个插件的配置:

  1. <plugin type="tk.mybatis.mapper.generator.MapperPlugin">
  2. <property name="mappers" value="tk.mybatis.mapper.common.Mapper"/>
  3. <property name="caseSensitive" value="true"/>
  4. <property name="forceAnnotation" value="true"/>
  5. <property name="beginningDelimiter" value="`"/>
  6. <property name="endingDelimiter" value="`"/>
  7. </plugin>

这里最关键的参数就是 mappers,配置后生成的 Mapper 接口都会自动继承上改接口,如果你定义了一个自己的基础接口,例如:

  1. package xxx.base;
  2. import tk.mybatis.mapper.common.Mapper;
  3. import tk.mybatis.mapper.common.MySqlMapper;
  4. /**
  5. * 继承自己的MyMapper
  6. */
  7. public interface MyMapper<T> extends Mapper<T>, MySqlMapper<T> {
  8. //TODO
  9. //FIXME 特别注意,该接口不能被扫描到,否则会出错
  10. }

在配置插件时,可以配置为:

  1. <property name="mappers" value="xxx.base.MyMapper"/>

其他参数的含义:

  • caseSensitive 是否区分大小写,默认值 false。如果数据库区分大小写,这里就需要配置为 true,这样当表名为 USER 时,会生成 @Table(name = "USER") 注解,否则使用小写 user 时会找不到表。
  • forceAnnotation 是否强制生成注解,默认 false,如果设置为 true,不管数据库名和字段名是否一致,都会生成注解(包含 @Table@Column)。
  • beginningDelimiter 和 endingDelimiter 开始和结束分隔符,对于有关键字的情况下适用。
  • useMapperCommentGenerator 是否使用通用 Mapper 提供的注释工具,默认 true 使用,这样在生成代码时会包含字段的注释(目前只有 mysql 和 oracle 支持),设置 false 后会用默认的,或者你可以配置自己的注释插件。
  • generateColumnConsts 在生成的 model中,增加字段名的常量,便于使用 Example 拼接查询条件的时候使用。
  • lombok 增加 model 代码生成时,可以直接生成 lombok 的 @Getter@Setter@ToString@Accessors(chain = true) 四类注解, 使用者在插件配置项中增加 <property name="lombok" value="Getter,Setter,ToString,Accessors"/> 即可生成对应包含注解的 model 类。

在上面<table 的配置中是针对 MySql 这种自增数据库的,如果使用 ORACLE 序列方式,可以参考下面的配置:

  1. <table tableName="country">
  2. <generatedKey column="id"
  3. sqlStatement="select SEQ_{1}.nextval from dual"
  4. identity="false" type="pre"/>
  5. </table>

SQL 中的 {1} 代表的是对应表的大写形式,{0} 是小写形式,这个配置生成的代码会像下面这样:

  1. public class Country {
  2. @Id
  3. @GeneratedValue(strategy = GenerationType.IDENTITY,
  4. generator = "select SEQ_COUNTRY.nextval from dual")
  5. private Integer id;
  6. // 省略其他
  7. }

这段配置介绍完了,之后运行前面的JAVA方法,就会生成对应的文件。该文件的样式在最后贴个例子。

4.1.3 使用 Maven 执行MBG

这里有一个完整的例子,MyBatis-Spring,下面讲解的内容出自这个例子。

Maven 中的插件配置如下:

  1. <plugins>
  2. <plugin>
  3. <artifactId>maven-compiler-plugin</artifactId>
  4. <configuration>
  5. <source>${jdk.version}</source>
  6. <target>${jdk.version}</target>
  7. </configuration>
  8. </plugin>
  9. <plugin>
  10. <groupId>org.mybatis.generator</groupId>
  11. <artifactId>mybatis-generator-maven-plugin</artifactId>
  12. <version>1.3.6</version>
  13. <configuration>
  14. <configurationFile>
  15. ${basedir}/src/main/resources/generator/generatorConfig.xml
  16. </configurationFile>
  17. <overwrite>true</overwrite>
  18. <verbose>true</verbose>
  19. </configuration>
  20. <dependencies>
  21. <dependency>
  22. <groupId>mysql</groupId>
  23. <artifactId>mysql-connector-java</artifactId>
  24. <version>5.1.29</version>
  25. </dependency>
  26. <dependency>
  27. <groupId>tk.mybatis</groupId>
  28. <artifactId>mapper</artifactId>
  29. <version>4.0.0</version>
  30. </dependency>
  31. </dependencies>
  32. </plugin>
  33. </plugins>

在插件中配置了配置文件的路径,覆盖和输出详细日志三个参数。

除此之外需要特别注意的是 <dependencies>,MBG 配置中用到的所有外部代码都必须通过依赖方式配置在这里,否则运行时会提示找不到对应的类而报错。这里有两个必须的依赖,一个是 JDBC 驱动,另一个是 Mapper 的插件。

下面看配置文件generatorConfig.xml

  1. <!DOCTYPE generatorConfiguration
  2. PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
  3. "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
  4. <generatorConfiguration>
  5. <properties resource="config.properties"/>
  6. <context id="Mysql" targetRuntime="MyBatis3Simple" defaultModelType="flat">
  7. <property name="beginningDelimiter" value="`"/>
  8. <property name="endingDelimiter" value="`"/>
  9. <plugin type="tk.mybatis.mapper.generator.MapperPlugin">
  10. <property name="mappers" value="tk.mybatis.mapper.common.Mapper"/>
  11. <property name="caseSensitive" value="true"/>
  12. </plugin>
  13. <jdbcConnection driverClass="${jdbc.driverClass}"
  14. connectionURL="${jdbc.url}"
  15. userId="${jdbc.user}"
  16. password="${jdbc.password}">
  17. </jdbcConnection>
  18. <javaModelGenerator targetPackage="com.isea533.mybatis.model"
  19. targetProject="src/main/java"/>
  20. <sqlMapGenerator targetPackage="mapper"
  21. targetProject="src/main/resources"/>
  22. <javaClientGenerator targetPackage="com.isea533.mybatis.mapper"
  23. targetProject="src/main/java"
  24. type="XMLMAPPER"/>
  25. <table tableName="user_info">
  26. <generatedKey column="id" sqlStatement="JDBC"/>
  27. </table>
  28. </context>
  29. </generatorConfiguration>

这里和之前相差不多,只是通过 <properties> 引入了外部属性文件,在 <jdbcConnection> 配置时,使用的属性文件中的参数。

运行

在 pom.xml 这一级目录的命令行窗口执行 mvn mybatis-generator:generate即可(前提是配置了mvn)。

生成的代码

下面是自动生成的代码的例子,这些例子可以在Mybatis-Spring这里找到。

一、实体类Country

  1. package com.isea533.mybatis.model;
  2. import javax.persistence.*;
  3. @Table(name = "country")
  4. public class Country {
  5. /**
  6. * 主键
  7. */
  8. @Id
  9. @Column(name = "Id")
  10. @GeneratedValue(generator = "JDBC")
  11. private Integer id;
  12. /**
  13. * 名称
  14. */
  15. private String countryname;
  16. /**
  17. * 代码
  18. */
  19. private String countrycode;
  20. /**
  21. * 获取主键
  22. *
  23. * @return Id - 主键
  24. */
  25. public Integer getId() {
  26. return id;
  27. }
  28. /**
  29. * 设置主键
  30. *
  31. * @param id 主键
  32. */
  33. public void setId(Integer id) {
  34. this.id = id;
  35. }
  36. /**
  37. * 获取名称
  38. *
  39. * @return countryname - 名称
  40. */
  41. public String getCountryname() {
  42. return countryname;
  43. }
  44. /**
  45. * 设置名称
  46. *
  47. * @param countryname 名称
  48. */
  49. public void setCountryname(String countryname) {
  50. this.countryname = countryname;
  51. }
  52. /**
  53. * 获取代码
  54. *
  55. * @return countrycode - 代码
  56. */
  57. public String getCountrycode() {
  58. return countrycode;
  59. }
  60. /**
  61. * 设置代码
  62. *
  63. * @param countrycode 代码
  64. */
  65. public void setCountrycode(String countrycode) {
  66. this.countrycode = countrycode;
  67. }
  68. }

可以看到这里生成的注释是有意义的内容,注释来源于数据库表字段的注释。

二、Mapper接口CountryMapper

  1. package com.isea533.mybatis.mapper;
  2. import com.isea533.mybatis.model.Country;
  3. import tk.mybatis.mapper.common.Mapper;
  4. public interface CountryMapper extends Mapper<Country> {
  5. }

接口自动继承配置的通用Mapper接口,自动包含泛型实体。

三、Mapper.xml文件CountryMapper.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3. <mapper namespace="com.isea533.mybatis.mapper.CountryMapper">
  4. <resultMap id="BaseResultMap" type="com.isea533.mybatis.model.Country">
  5. <!--
  6. WARNING - @mbg.generated
  7. -->
  8. <id column="Id" jdbcType="INTEGER" property="id" />
  9. <result column="countryname" jdbcType="VARCHAR" property="countryname" />
  10. <result column="countrycode" jdbcType="VARCHAR" property="countrycode" />
  11. </resultMap>
  12. </mapper>

xml文件只包含了实体的resultMap映射配置。

4.2 代码生成器文档

代码生成器是基于 MBG 插件的,所以需要配合 MBG 使用。

一个简单的 MBG 配置如下:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE generatorConfiguration
  3. PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
  4. "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
  5. <generatorConfiguration>
  6. <context id="Mysql" targetRuntime="MyBatis3Simple" defaultModelType="flat">
  7. <property name="javaFileEncoding" value="UTF-8"/>
  8. <!--配置是否使用通用 Mapper 自带的注释扩展,默认 true-->
  9. <!--<property name="useMapperCommentGenerator" value="false"/>-->
  10. <!--通用 Mapper 插件,可以生成带注解的实体类-->
  11. <plugin type="tk.mybatis.mapper.generator.MapperPlugin">
  12. <property name="mappers" value="tk.mybatis.mapper.common.Mapper,tk.mybatis.mapper.hsqldb.HsqldbMapper"/>
  13. <property name="caseSensitive" value="true"/>
  14. <property name="forceAnnotation" value="true"/>
  15. <property name="beginningDelimiter" value="`"/>
  16. <property name="endingDelimiter" value="`"/>
  17. </plugin>
  18. <!--通用代码生成器插件-->
  19. <plugin type="tk.mybatis.mapper.generator.TemplateFilePlugin">
  20. <property name="targetProject" value="src/test/java"/>
  21. <property name="targetPackage" value="test.mapper"/>
  22. <property name="templatePath" value="generator/mapper.ftl"/>
  23. <property name="mapperSuffix" value="Dao"/>
  24. <property name="fileName" value="${tableClass.shortClassName}${mapperSuffix}.java"/>
  25. </plugin>
  26. <jdbcConnection driverClass="com.mysql.jdbc.Driver"
  27. connectionURL="jdbc:mysql://localhost:3306/test"
  28. userId="root"
  29. password="">
  30. </jdbcConnection>
  31. <!--MyBatis 生成器只需要生成 Model-->
  32. <javaModelGenerator targetPackage="test.model" targetProject="./src/test/java"/>
  33. <table tableName="user%">
  34. <generatedKey column="id" sqlStatement="JDBC"/>
  35. </table>
  36. </context>
  37. </generatorConfiguration>

在这个配置中,我们只关注 tk.mybatis.mapper.generator.TemplateFilePlugin

4.2.1 基于模板的插件 TemplateFilePlugin

这个插件中除了几个必备的属性外,还可以增加任意的属性,属性完全是为了给模板提供数据。

先看一个基本完整的配置:

  1. <!--测试输出单个文件,每个表都会生成一个对应的文件-->
  2. <plugin type="tk.mybatis.mapper.generator.TemplateFilePlugin">
  3. <property name="singleMode" value="false"/>
  4. <property name="targetProject" value="src/test/resources"/>
  5. <property name="targetPackage" value=""/>
  6. <property name="templatePath" value="generator/test-one.ftl"/>
  7. <property name="fileName" value="${tableClass.shortClassName}Test.txt"/>
  8. <!--默认值是下面这个,可以不配置-->
  9. <property name="templateFormatter" value="tk.mybatis.mapper.generator.formatter.FreemarkerTemplateFormatter"/>
  10. </plugin>

下面介绍必备的属性。

1. targetProject

用于指定目标项目,一般是 src/main/java 或者 src/main/resource 这样的目录。
还可以是 src/test/java 或者 src/test/resource 这样的目录。

在多模块项目中,还能通过相对路径指定为其他的目录,例如:

  1. <property name="targetProject" value="../myproject-api/src/main/java"/>

这个属性值有一个要求,就是目录必须存在,否则不会生成代码!

2. targetPackage

用于指定包的部分,虽然是这个名字,实际上就是路径。

这个属性指定的路径如果不存在,就会自动创建。

这个属性的值可以为空。

例如 mapper/admin 用于生成 mapper/admin/ 目录,或者 tk.mybatis.mapper 生成包(本质上还是目录)。

这个属性还有一个特殊的地方,它还支持使用模板,就和下面的 fileName 一样,举个简单的使用场景。

你可能在生成前端代码的时候,希望将表对应的 JSP 生成在自己的一个目录中,此时可以配置为: <property name="targetPackage" value="WEB-INF/jsp/${tableClass.lowerCaseName}/"/> 模板中可以用到的属性,这里都能用,其他属性后面会介绍。

通过这个路径也能看出来,配置一个插件只能根据模板在一个指定位置(targetProject 和 targetPackage 决定的目录)生成一个文件。

3. templatePath

指定模板路径,可以是任意能够通过 ClassLoader 能够获取的位置,文件类型没有限制。

例如示例中的 generator/test-one.ftl

这个属性必须指定,否则不会生成代码!

4. fileName

这个属性用于指定生成文件的名字,这个值支持使用模板,例如上面的 ${tableClass.shortClassName}Test.txt,具体可用的属性会在后面介绍。

这个属性必须指定,否则不会生成代码!

5. templateFormatter

这个属性可选,默认使用基于 FreeMarker 的实现!

默认情况下,你需要添加下面的依赖:

  1. <dependency>
  2. <groupId>org.freemarker</groupId>
  3. <artifactId>freemarker</artifactId>
  4. <version>2.3.23</version>
  5. </dependency>

默认的实现类为:tk.mybatis.mapper.generator.formatter.FreemarkerTemplateFormatter

这个类实现了两个接口 TemplateFormatter, ListTemplateFormatter

这俩接口分别对应下面 singleMode 参数值的 truefalse

也就是一个表生成一个文件,或者多个表生成一个文件。

对于一般情况下,都是第一种情况。但是在配置文件中,可能会用到多个表的信息。

如果你想使用其他模板引擎,可以自己实现上面的接口。

6. singleMode

上面已经提过,默认为 true

一个表生成一个文件时,可用属性可以参考 generator/test-one.ftl,表的属性在 tableClass 中。

多个表生成一个文件时,可用属性可以参考 generator/test-all.ftl,所有表的属性在 tableClassSet 中,通过遍历可以获取单个的信息。

7. 其他你需要的属性

模板中需要的特殊信息都可以通过 <property> 方法设置,在模板中直接使用这里定义的属性名来使用,后面例子的中的 mapperSuffix 就是这种属性。

4.2.2 TemplateFilePlugin 配置示例

因为模板需要根据业务进行设计,所以这里只提供了两个简单的 mapper 目标和两个完整属性的示例模板。

因为一个模板只能生成一类的文件,所以如果要生成多个不同的文件,就需要配置多个插件。

这种设计很灵活,因为自由度很高,所以代价就是配置的多。 但是正常情况下,根据业务设计的一套模板基本是固定的,不会有太多变化,所以用起来并不麻烦。

例如下面的示例:

  1. <!--通用代码生成器插件-->
  2. <!--mapper接口-->
  3. <plugin type="tk.mybatis.mapper.generator.TemplateFilePlugin">
  4. <property name="targetProject" value="src/test/java"/>
  5. <property name="targetPackage" value="test.mapper"/>
  6. <property name="templatePath" value="generator/mapper.ftl"/>
  7. <property name="mapperSuffix" value="Dao"/>
  8. <property name="fileName" value="${tableClass.shortClassName}${mapperSuffix}.java"/>
  9. </plugin>
  10. <!--mapper.xml-->
  11. <plugin type="tk.mybatis.mapper.generator.TemplateFilePlugin">
  12. <property name="targetProject" value="src/test/resources"/>
  13. <property name="targetPackage" value="mappers"/>
  14. <property name="mapperPackage" value="test.mapper"/>
  15. <property name="templatePath" value="generator/mapperXml.ftl"/>
  16. <property name="mapperSuffix" value="Dao"/>
  17. <property name="fileName" value="${tableClass.shortClassName}${mapperSuffix}.xml"/>
  18. </plugin>
  19. <!--测试输出单个文件,每个表都会生成一个对应的文件-->
  20. <plugin type="tk.mybatis.mapper.generator.TemplateFilePlugin">
  21. <property name="targetProject" value="src/test/resources"/>
  22. <property name="targetPackage" value=""/>
  23. <property name="templatePath" value="generator/test-one.ftl"/>
  24. <property name="fileName" value="${tableClass.shortClassName}Test.txt"/>
  25. <!--默认值是下面这个,可以不配置-->
  26. <property name="templateFormatter"
  27. value="tk.mybatis.mapper.generator.formatter.FreemarkerTemplateFormatter"/>
  28. </plugin>
  29. <!--测试输出整个文件,所有表都可用,一次只生成一个文件,用于聚合所有表使用-->
  30. <plugin type="tk.mybatis.mapper.generator.TemplateFilePlugin">
  31. <property name="singleMode" value="false"/>
  32. <property name="targetProject" value="src/test/resources"/>
  33. <property name="targetPackage" value=""/>
  34. <property name="templatePath" value="generator/test-all.ftl"/>
  35. <property name="fileName" value="TestAll.txt"/>
  36. </plugin>

前两个会生成 Dao 后缀的 Mapper 接口和 XML,其中有个针对性的参数 mapperSuffix 用于配置后缀,
还有个 mapperPackage 在生成 XML 时获取接口的包名(因为和这里的 targetPackage 可以不同)。

后两个插件用于演示所有可用的属性,而且是两种不同的模式。

在表和实体上可用的所有属性如下:

  1. 特殊:targetPackage值在 ${package} 中。
  2. <!-- 详细日期用法参考:http://freemarker.apache.org/docs/ref_builtins_date.html -->
  3. 当前时间:
  4. <#assign dateTime = .now>
  5. 日期:${dateTime?date}
  6. 时间:${dateTime?time}
  7. 格式化:${dateTime?string["yyyy-MM-dd HH:mm:ss"]}
  8. 所有配置的属性信息:
  9. <#list props?keys as key>
  10. ${key} - ${props[key]}
  11. </#list>
  12. 实体和表的信息:
  13. 表名:${tableClass.tableName}
  14. 变量名:${tableClass.variableName}
  15. 小写名:${tableClass.lowerCaseName}
  16. 类名:${tableClass.shortClassName}
  17. 全名:${tableClass.fullClassName}
  18. 包名:${tableClass.packageName}
  19. 列的信息:
  20. =====================================
  21. <#if tableClass.pkFields??>
  22. 主键:
  23. <#list tableClass.pkFields as field>
  24. -------------------------------------
  25. 列名:${field.columnName}
  26. 列类型:${field.jdbcType}
  27. 字段名:${field.fieldName}
  28. 注释:${field.remarks}
  29. 类型包名:${field.typePackage}
  30. 类型短名:${field.shortTypeName}
  31. 类型全名:${field.fullTypeName}
  32. 是否主键:${field.identity?c}
  33. 是否可空:${field.nullable?c}
  34. 是否为BLOB列:${field.blobColumn?c}
  35. 是否为String列:${field.stringColumn?c}
  36. 是否为字符串列:${field.jdbcCharacterColumn?c}
  37. 是否为日期列:${field.jdbcDateColumn?c}
  38. 是否为时间列:${field.jdbcTimeColumn?c}
  39. 是否为序列列:${field.sequenceColumn?c}
  40. 列长度:${field.length?c}
  41. 列精度:${field.scale}
  42. </#list>
  43. </#if>
  44. <#if tableClass.baseFields??>
  45. 基础列:
  46. <#list tableClass.baseFields as field>
  47. -------------------------------------
  48. 列名:${field.columnName}
  49. 列类型:${field.jdbcType}
  50. 字段名:${field.fieldName}
  51. 注释:${field.remarks}
  52. 类型包名:${field.typePackage}
  53. 类型短名:${field.shortTypeName}
  54. 类型全名:${field.fullTypeName}
  55. 是否主键:${field.identity?c}
  56. 是否可空:${field.nullable?c}
  57. 是否为BLOB列:${field.blobColumn?c}
  58. 是否为String列:${field.stringColumn?c}
  59. 是否为字符串列:${field.jdbcCharacterColumn?c}
  60. 是否为日期列:${field.jdbcDateColumn?c}
  61. 是否为时间列:${field.jdbcTimeColumn?c}
  62. 是否为序列列:${field.sequenceColumn?c}
  63. 列长度:${field.length?c}
  64. 列精度:${field.scale}
  65. </#list>
  66. </#if>
  67. <#if tableClass.blobFields??>
  68. Blob列:
  69. <#list tableClass.blobFields as field>
  70. -------------------------------------
  71. 列名:${field.columnName}
  72. 列类型:${field.jdbcType}
  73. 字段名:${field.fieldName}
  74. 注释:${field.remarks}
  75. 类型包名:${field.typePackage}
  76. 类型短名:${field.shortTypeName}
  77. 类型全名:${field.fullTypeName}
  78. 是否主键:${field.identity?c}
  79. 是否可空:${field.nullable?c}
  80. 是否为BLOB列:${field.blobColumn?c}
  81. 是否为String列:${field.stringColumn?c}
  82. 是否为字符串列:${field.jdbcCharacterColumn?c}
  83. 是否为日期列:${field.jdbcDateColumn?c}
  84. 是否为时间列:${field.jdbcTimeColumn?c}
  85. 是否为序列列:${field.sequenceColumn?c}
  86. 列长度:${field.length?c}
  87. 列精度:${field.scale}
  88. </#list>
  89. </#if>
  90. =====================================
  91. 全部列(包含了pk,base,blob 字段,可用的属性和上面的一样):
  92. <#if tableClass.allFields??>
  93. 列名 - 字段名
  94. <#list tableClass.allFields as field>
  95. ${field.columnName} - ${field.fieldName}
  96. </#list>
  97. </#if>

4.2.3 测试执行

上面示例就是本项目的测试代码,在 src/test/resources/generator/generatorConfig.xml 中。

还提供了一种 Java 编码方式运行的类,src/test/java/ 中的 tk.mybatis.mapper.generator.Generator,配置上面 xml 中的数据库信息就可以生成。

测试生成的部分结果如下。

实体:

  1. @Table(name = "`user_info`")
  2. public class UserInfo {
  3. @Id
  4. @Column(name = "`Id`")
  5. @GeneratedValue(generator = "JDBC")
  6. private Integer id;

Dao:

  1. package test.mapper;
  2. import test.model.UserInfo;
  3. /**
  4. * 通用 Mapper 代码生成器
  5. *
  6. * @author mapper-generator
  7. */
  8. public interface UserInfoDao extends tk.mybatis.mapper.common.Mapper<UserInfo> {
  9. }

XML:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE mapper
  3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  5. <mapper namespace="test.mapper.UserInfoDao">
  6. </mapper>

test-one.ftl 生成的信息如下:

  1. 目标package:
  2. 当前时间:
  3. 2017-11-6
  4. 22:00:45
  5. 2017-11-06 22:00:45
  6. 所有配置的属性信息:
  7. targetPackage -
  8. templateFormatter - tk.mybatis.mapper.generator.formatter.FreemarkerTemplateFormatter
  9. templatePath - generator/test-one.ftl
  10. targetProject - src/test/resources
  11. fileName - ${tableClass.shortClassName}Test.txt
  12. 实体和表的信息:
  13. 表名:user_info
  14. 变量名:userInfo
  15. 小写名:userinfo
  16. 类名:UserInfo
  17. 全名:test.model.UserInfo
  18. 包名:test.model
  19. 列的信息:
  20. =====================================
  21. 主键:
  22. -------------------------------------
  23. 列名:Id
  24. 列类型:INTEGER
  25. 字段名:id
  26. 注释:
  27. 类型包名:java.lang
  28. 类型短名:Integer
  29. 类型全名:java.lang.Integer
  30. 是否主键:true
  31. 是否可空:false
  32. 是否为BLOB列:false
  33. 是否为String列:false
  34. 是否为字符串列:false
  35. 是否为日期列:false
  36. 是否为时间列:false
  37. 是否为序列列:false
  38. 列长度:10
  39. 列精度:0
  40. 基础列:
  41. -------------------------------------
  42. 列名:username
  43. 列类型:VARCHAR
  44. 字段名:username
  45. 注释:用户名
  46. 类型包名:java.lang
  47. 类型短名:String
  48. 类型全名:java.lang.String
  49. 是否主键:false
  50. 是否可空:false
  51. 是否为BLOB列:false
  52. 是否为String列:true
  53. 是否为字符串列:true
  54. 是否为日期列:false
  55. 是否为时间列:false
  56. 是否为序列列:false
  57. 列长度:32
  58. 列精度:0
  59. -------------------------------------
  60. 列名:password
  61. 列类型:VARCHAR
  62. 字段名:password
  63. 注释:密码
  64. 类型包名:java.lang
  65. 类型短名:String
  66. 类型全名:java.lang.String
  67. 是否主键:false
  68. 是否可空:true
  69. 是否为BLOB列:false
  70. 是否为String列:true
  71. 是否为字符串列:true
  72. 是否为日期列:false
  73. 是否为时间列:false
  74. 是否为序列列:false
  75. 列长度:32
  76. 列精度:0
  77. -------------------------------------
  78. 列名:usertype
  79. 列类型:VARCHAR
  80. 字段名:usertype
  81. 注释:用户类型
  82. 类型包名:java.lang
  83. 类型短名:String
  84. 类型全名:java.lang.String
  85. 是否主键:false
  86. 是否可空:true
  87. 是否为BLOB列:false
  88. 是否为String列:true
  89. 是否为字符串列:true
  90. 是否为日期列:false
  91. 是否为时间列:false
  92. 是否为序列列:false
  93. 列长度:2
  94. 列精度:0
  95. -------------------------------------
  96. 列名:enabled
  97. 列类型:INTEGER
  98. 字段名:enabled
  99. 注释:是否可用
  100. 类型包名:java.lang
  101. 类型短名:Integer
  102. 类型全名:java.lang.Integer
  103. 是否主键:false
  104. 是否可空:true
  105. 是否为BLOB列:false
  106. 是否为String列:false
  107. 是否为字符串列:false
  108. 是否为日期列:false
  109. 是否为时间列:false
  110. 是否为序列列:false
  111. 列长度:10
  112. 列精度:0
  113. -------------------------------------
  114. 列名:realname
  115. 列类型:VARCHAR
  116. 字段名:realname
  117. 注释:真实姓名
  118. 类型包名:java.lang
  119. 类型短名:String
  120. 类型全名:java.lang.String
  121. 是否主键:false
  122. 是否可空:true
  123. 是否为BLOB列:false
  124. 是否为String列:true
  125. 是否为字符串列:true
  126. 是否为日期列:false
  127. 是否为时间列:false
  128. 是否为序列列:false
  129. 列长度:32
  130. 列精度:0
  131. -------------------------------------
  132. 列名:qq
  133. 列类型:VARCHAR
  134. 字段名:qq
  135. 注释:QQ
  136. 类型包名:java.lang
  137. 类型短名:String
  138. 类型全名:java.lang.String
  139. 是否主键:false
  140. 是否可空:true
  141. 是否为BLOB列:false
  142. 是否为String列:true
  143. 是否为字符串列:true
  144. 是否为日期列:false
  145. 是否为时间列:false
  146. 是否为序列列:false
  147. 列长度:14
  148. 列精度:0
  149. -------------------------------------
  150. 列名:email
  151. 列类型:VARCHAR
  152. 字段名:email
  153. 注释:
  154. 类型包名:java.lang
  155. 类型短名:String
  156. 类型全名:java.lang.String
  157. 是否主键:false
  158. 是否可空:true
  159. 是否为BLOB列:false
  160. 是否为String列:true
  161. 是否为字符串列:true
  162. 是否为日期列:false
  163. 是否为时间列:false
  164. 是否为序列列:false
  165. 列长度:100
  166. 列精度:0
  167. -------------------------------------
  168. 列名:tel
  169. 列类型:VARCHAR
  170. 字段名:tel
  171. 注释:联系电话
  172. 类型包名:java.lang
  173. 类型短名:String
  174. 类型全名:java.lang.String
  175. 是否主键:false
  176. 是否可空:true
  177. 是否为BLOB列:false
  178. 是否为String列:true
  179. 是否为字符串列:true
  180. 是否为日期列:false
  181. 是否为时间列:false
  182. 是否为序列列:false
  183. 列长度:255
  184. 列精度:0
  185. Blob列:
  186. =====================================
  187. 全部列:
  188. 列名 - 字段名
  189. Id - id
  190. username - username
  191. password - password
  192. usertype - usertype
  193. enabled - enabled
  194. realname - realname
  195. qq - qq
  196. email - email
  197. tel - tel

4.2.4 最后

基础的代码生成器是很简单的,和 Java 拼字符串输出很像,这里只是使用了模板。

几乎所有人都在 JSP 中用过的 EL 就是一种模板,可能你会 <c:forEach 这种,但是联想不到这里的代码生成器而已。

后续会在 https://github.com/abel533/Mybatis-Spring 项目中提供一套模板做为示例。

五、扩展通用接口

项目中提供了大量现成的方法,这些方法可以作为扩展时的参考。

例如 selectAll 方法。

首先定义接口:

  1. @RegisterMapper
  2. public interface SelectAllMapper<T> {
  3. /**
  4. * 查询全部结果
  5. *
  6. * @return
  7. */
  8. @SelectProvider(type = MySelectProvider.class, method = "dynamicSQL")
  9. List<T> selectAll();
  10. }

其中 MySelectProvider 是你要实现的一个类,该类需要继承 MapperTemplate

@RegisterMapper 注解可以避免 mappers 参数配置,通用 Mapper 检测到该接口被继承时,会自动注册。

  1. import org.apache.ibatis.mapping.MappedStatement;
  2. import tk.mybatis.mapper.mapperhelper.MapperHelper;
  3. import tk.mybatis.mapper.mapperhelper.MapperTemplate;
  4. import tk.mybatis.mapper.mapperhelper.SqlHelper;
  5. public class MySelectProvider extends MapperTemplate {
  6. public BaseSelectProvider(Class<?> mapperClass, MapperHelper mapperHelper) {
  7. super(mapperClass, mapperHelper);
  8. }
  9. /**
  10. * 查询全部结果
  11. *
  12. * @param ms
  13. * @return
  14. */
  15. public String selectAll(MappedStatement ms) {
  16. final Class<?> entityClass = getEntityClass(ms);
  17. //修改返回值类型为实体类型
  18. setResultType(ms, entityClass);
  19. StringBuilder sql = new StringBuilder();
  20. sql.append(SqlHelper.selectAllColumns(entityClass));
  21. sql.append(SqlHelper.fromTable(entityClass, tableName(entityClass)));
  22. sql.append(SqlHelper.orderByDefault(entityClass));
  23. return sql.toString();
  24. }
  25. }

其中 selectAll 方法名要和接口中定义的方法名一致。其次就是该方法的参数为 MappedStatement 类型。

在 selectAll 方法中,首先是获取了当前接口的实体类型:

  1. final Class<?> entityClass = getEntityClass(ms);

因为接口返回值类型为 List<T>,MyBatis 会认为返回值类型为 List<Object>,这和我们想要的实体类型不一样,所以下一行代码就是设置返回值类型:

  1. setResultType(ms, entityClass);

注意,只有返回 T 或者 List 时需要设置,返回 int 类型时不需要设置。

接下来就是纯粹的拼接 XML 形式的 SQL 了。

  1. //select col1,col2...
  2. sql.append(SqlHelper.selectAllColumns(entityClass));
  3. //from tablename - 支持动态表名
  4. sql.append(SqlHelper.fromTable(entityClass, tableName(entityClass)));
  5. //order by xxx
  6. sql.append(SqlHelper.orderByDefault(entityClass));

这是其中最简单的一个方法实现。当你想要实现某种方法时,可以从已有的例子中找一个最接近的方法,在此基础上进行修改。