Mybatis Generator
MyBatis Generator
基础使用
MyBatis Generator 可以直接通过命令行调用:
java -jar mybatis-generator-core-x.x.x.jar -configfile generatorConfig.xml
或者添加 Maven 插件:
<project ...>
...
<build>
...
<plugins>
...
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.7</version>
<configurationFile>src/main/resources/generatorConfig.xml</configurationFile>
</plugin>
...
</plugins>
...
</build>
...
</project>
然后在项目目录下执行 mvn 命令:
$ mvn mybatis-generator:generate
配置详解
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"></properties>
<!--指定特定数据库的jdbc驱动jar包的位置 -->
<classPathEntry location="${jdbc.driverLocation}"/>
<!--一或多个-->
<context id="default" targetRuntime="MyBatis3">
...
</context>
</generatorConfiguration>
<context>
元素用于指定生成一组对象的环境。例如指定要连接的数据库,要生成对象的类型和要处理的数据库中的表。运行 MBG 的时候还可以指定要运行的 <context>
。该元素只有一个必选属性 id,用来唯一确定一个 <context>
元素,该 id 属性可以在运行 MBG 的使用。<context>
还包含了 defaultModelType 属性,用来指定 MBG 生成实体类的规则:
- conditional: 默认设置,这个模型和下面的 hierarchical 类似,除了如果那个单独的类将只包含一个字段,将不会生成一个单独的类。因此,如果一个表的主键只有一个字段,那么不会为该字段生成单独的实体类,会将该字段合并到基本实体类中。
- flat: 该模型为每一张表只生成一个实体类,这个实体类包含表中的所有字段。
- hierarchical: 如果表有主键,那么该模型会产生一个单独的主键实体类,如果表还有 BLOB 字段,则会为表生成一个包含所有 BLOB 字段的单独的实体类,然后为所有其他的字段生成一个单独的实体类。MBG 会在所有生成的实体类之间维护一个继承关系。
上下文配置
MBG 配置中的其他几个元素,基本上都是 <context>
的子元素,这些子元素(有严格的配置顺序)包括:
<property>
(0 个或多个)
property 属性能够用于处理数据库表中的特殊字符,譬如 SQL 关键字的处理等。
<plugin>
(0 个或多个)
plugin 元素用来定义一个插件。插件用于扩展或修改通过 MyBatis Generator (MBG)代码生成器生成的代码。
<commentGenerator>
(0 个或 1 个)
commentGenerator 旨在创建 class 时,对注释进行控制。一般情况下由于 MBG 生成的注释信息没有任何价值,而且有时间戳的情况下每次生成的注释都不一样,使用版本控制的时候每次都会提交,因而一般情况下我们都会屏蔽注释信息,可以如下配置:
<commentGenerator>
<property name="suppressAllComments" value="true"/>
<property name="suppressDate" value="true"/>
</commentGenerator>
<jdbcConnection>
(1 个)
jdbcConnection 用于指定数据库连接信息,该元素必选,并且只能有一个。配置该元素只需要注意如果 JDBC 驱动不在 classpath 下,就需要通过 <classPathEntry>
元素引入 jar 包,这里推荐将 jar 包放到 classpath 下。
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/test"
userId="root"
password="">
</jdbcConnection>
<!--或者使用外部依赖-->
<jdbcConnection driverClass="${jdbc.driverClass}" connectionURL="${jdbc.connectionURL}" userId="${jdbc.userId}" password="${jdbc.password}">
</jdbcConnection>
实体类解析配置
<javaTypeResolver>
(0 个或 1 个)
这个元素的配置用来指定 JDBC 类型和 Java 类型如何转换。该元素提供了一个可选的属性 type,和<commentGenerator>
比较类型,提供了默认的实现 DEFAULT,一般情况下使用默认即可,需要特殊处理的情况可以通过其他元素配置来解决,不建议修改该属性。可以配置的属性为 forceBigDecimals,该属性可以控制是否强制 DECIMAL 和 NUMERIC 类型的字段转换为 Java 类型的 java.math.BigDecimal,默认值为 false,一般不需要配置。
<javaTypeResolver >
<!-- 如果精度>0或者长度>18,就会使用java.math.BigDecimal -->
<!-- 如果精度=0并且10<=长度<=18,就会使用java.lang.Long -->
<!-- 如果精度=0并且5<=长度<=9,就会使用java.lang.Integer -->
<!-- 如果精度=0并且长度<5,就会使用java.lang.Short -->
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<javaModelGenerator>
(1 个)
Model 模型生成器,用来生成含有主键 key 的类,记录类 以及查询 Example 类;targetPackage 指定生成的 model 生成所在的包名,targetProject 指定在该项目下所在的路径。
<javaModelGenerator targetPackage="wx.model.po" targetProject="src/main/java">
<!-- 是否对model添加 构造函数 -->
<property name="constructorBased" value="true"/>
<!-- 是否允许子包,即targetPackage.schemaName.tableName -->
<property name="enableSubPackages" value="false"/>
<!-- 建立的 Model 对象是否不可改变,即生成的Model对象不会有 setter方法,只有构造方法 -->
<property name="immutable" value="true"/>
<!-- 给 Model 添加一个父类 -->
<property name="rootClass" value="wx.model.Hello"/>
<!-- 是否对类 CHAR 类型的列的数据进行 trim 操作 -->
<property name="trimStrings" value="true"/>
</javaModelGenerator>
<sqlMapGenerator>
(0 个或 1 个)
Mapper 映射文件生成所在的目录 为每一个数据库的表生成对应的 SqlMap 文件:
<sqlMapGenerator targetPackage="wx.map.domain" targetProject="src/main/java">
<property name="enableSubPackages" value="false"/>
</sqlMapGenerator>
<javaClientGenerator>
(0 个或 1 个)
客户端代码,生成易于使用的针对 Model 对象和 XML 配置文件的代码:
<!--
type="ANNOTATEDMAPPER",生成Java Model 和基于注解的Mapper对象
type="MIXEDMAPPER",生成基于注解的Java Model 和相应的Mapper对象
type="XMLMAPPER",生成SQLMap XML文件和独立的Mapper接口
-->
<javaClientGenerator targetPackage="wx.dao" targetProject="src/main/java" type="MIXEDMAPPER">
<property name="enableSubPackages" value=""/>
<!--
定义Maper.java 源代码中的ByExample() 方法的可视性,可选的值有:
public;
private;
protected;
default
注意:如果 targetRuntime="MyBatis3",此参数被忽略
-->
<property name="exampleMethodVisibility" value=""/>
<!--
方法名计数器
Important note: this property is ignored if the target runtime is MyBatis3.
-->
<property name="methodNameCalculator" value=""/>
<!--
为生成的接口添加父接口
-->
<property name="rootInterface" value=""/>
</javaClientGenerator>
<table>
(1 个或多个)
<table tableName="lession" schema="mybatis" domainObjectName="ObjName">
<!-- optional
自动生成的键值(identity,或者序列值),如果指定此元素,MBG将会生成<selectKey>元素,然后将此元素插入到SQL Map的<insert> 元素之中,sqlStatement 的语句将会返回新的值;如果是一个自增主键的话,你可以使用预定义的语句, 或者添加自定义的SQL语句. 预定义的值如下: MySql: SELECT LAST_INSERT_ID()
-->
<generatedKey column="id" sqlStatement="Mysql" identity="" type=""/>
<!-- optional.
列的命名规则:
MBG使用 <columnRenamingRule> 元素在计算列名的对应名称之前,先对列名进行重命名,
作用:一般需要对BUSI_CLIENT_NO 前的BUSI_进行过滤
支持正在表达式
searchString 表示要被换掉的字符串
replaceString 则是要换成的字符串,默认情况下为空字符串,可选
-->
<columnRenamingRule searchString="" replaceString=""/>
<!-- optional.告诉 MBG 忽略某一列
column,需要忽略的列
delimitedColumnName:true ,匹配column的值和数据库列的名称 大小写完全匹配,false 忽略大小写匹配
是否限定表的列名,即固定表列在Model中的名称
-->
<ignoreColumn column="PLAN_ID" delimitedColumnName="true" />
<!--optional.覆盖MBG对 Model 的生成规则
column: 数据库的列名
javaType: 对应的Java数据类型的完全限定名
在必要的时候可以覆盖由JavaTypeResolver计算得到的java数据类型. For some databases, this is necessary to handle "odd" database types (e.g. MySql's unsigned bigint type should be mapped to java.lang.Object).
jdbcType:该列的JDBC数据类型(INTEGER, DECIMAL, NUMERIC, VARCHAR, etc.),该列可以覆盖由JavaTypeResolver计算得到的Jdbc类型,对某些数据库而言,对于处理特定的JDBC 驱动癖好 很有必要(e.g. DB2's LONGVARCHAR type should be mapped to VARCHAR for iBATIS).
typeHandler:
-->
<columnOverride column="" javaType="" jdbcType="" typeHandler="" delimitedColumnName="" />
</table>
Lombok
如果我们希望在生成的实体类中支持 Lombok,那可以参考 mybatis-generator-lombok-plugin 等项目。在 pom.xml 中可以添加该插件:
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>${mybatis.generator.version}</version>
<configuration>
<overwrite>true</overwrite>
</configuration>
<dependencies>
<dependency>
<groupId>com.softwareloop</groupId>
<artifactId>mybatis-generator-lombok-plugin</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
</plugin>
然后在 Generator 的配置中添加该插件:
<?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>
<context id="example"
targetRuntime="MyBatis3Simple"
defaultModelType="flat">
<!-- include the plugin -->
<plugin type="com.softwareloop.mybatis.generator.plugins.LombokPlugin">
<!-- enable annotations -->
<property name="builder" value="true"/>
<!-- annotation's option(boolean) -->
<property name="builder.fluent" value="true"/>
<!-- annotation's option(String) -->
<property name="builder.builderMethodName" value="myBuilder"/>
<property name="accessors" value="true"/>
<!-- annotation's option(array of String) -->
<property name="accessors.prefix" value="m_, _"/>
<!-- disable annotations -->
<property name="allArgsConstructor" value="false"/>
</plugin>
<!-- other configurations -->
</context>
</generatorConfiguration>
模板使用
对于 MyBatis Generator 生成的代码模板,不建议修改生成后的模板,这样在数据库发生变化时候可以直接重新生成,根据错误提示修改对应代码。MyBatis Generator 为我们自动生成了 Model, Mapper 与 Example 文件,其中 Example 能够被用于构建搜索条件,譬如:
// 模糊搜索用户名
String name = "明";
UserExample ex = new UserExample();
ex.createCriteria().andNameLike('%'+name+'%');
List<User> userList = userDao.selectByExample(ex);
// 通过某个字段排序
String orderByClause = "id DESC";
UserExample ex = new UserExample();
ex.setOrderByClause(orderByClause);
List<User> userList = userDao.selectByExample(ex);
// 条件搜索,不确定条件的个数
UserExample ex = new UserExample();
Criteria criteria = ex.createCriteria();
if(StringUtils.isNotBlank(user.getAddress())){
criteria.andAddressEqualTo(user.getAddress());
}
if(StringUtils.isNotBlank(user.getName())){
criteria.andNameEqualTo(user.getName());
}
List<User> userList = userDao.selectByExample(ex);
// 分页搜索列表
pager.setPageNum(1);
pager.setPageSize(5);
UserExample ex = new UserExample();
ex.setPage(pager);
List<User> userList = userDao.selectByExample(ex);
Mapper 类
Mapper 也就是数据库访问类 DAO:
package org.dev.repo.dao;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import org.dev.repo.dto.OrderDTO;
import org.dev.repo.dto.OrderDTOExample;
public interface OrderDTOMapper {
//通过条件计数
int countByExample(OrderDTOExample example);
//通过条件删除
int deleteByExample(OrderDTOExample example);
//通过主键删除
int deleteByPrimaryKey(Long id);
//插入DTO
int insert(OrderDTO record);
//dto非空字段插入
int insertSelective(OrderDTO record);
//条件查询,返回结果包括text等大字段
List<OrderDTO> selectByExampleWithBLOBs(OrderDTOExample example);
//条件查询,返回结果不包括text等大字段
List<OrderDTO> selectByExample(OrderDTOExample example);
//通过主键查询
OrderDTO selectByPrimaryKey(Long id);
//条件更新,只更新dto中非空字段的值
int updateByExampleSelective(
@Param("record") OrderDTO record,
@Param("example") OrderDTOExample example
);
//条件更新全部字段,并包含text等大字段
int updateByExampleWithBLOBs(
@Param("record") OrderDTO record,
@Param("example") OrderDTOExample example
);
//条件更新dto全部字段
int updateByExample(
@Param("record") OrderDTO record,
@Param("example") OrderDTOExample example
);
//通过主键更新,dto非空字段
int updateByPrimaryKeySelective(OrderDTO record);
//通过主键更新,dto全部字段,包含text等大字段
int updateByPrimaryKeyWithBLOBs(OrderDTO record);
//通过主键更新,dto全部字段,不包含text等大字段
int updateByPrimaryKey(OrderDTO record);
}
-
最常用的动态查询方法 selectByExample(OrderDTOExample example),简单通过构造不同的 example 对象就可以实现.
-
后缀为 Selective 的插入和更新方法,只处理 dto 对象的非空字段,例如经常我们只想更新某些记录的部分字段,那么 updateByExampleSelective 方法最合适不过.
-
后缀为 WithBLOBs 的方法是对大字段的处理方法,与常规处理方法分离。
使用 Example 进行动态查询也是颇为便捷的:
LocalDate ld=LocalDate.of(2017,1,1);
Instant instant = Instant.from(ld);
Date date = Date.from(instant);
OrderDTOExample example=new OrderDTOExample();
//设置排序字段
example.setOrderByClause(" order by create_time desc ");
//创建查询条件对象
example.createCriteria().andCustomerIdEqualTo(123456L).
andCreateTimeGreaterThan(date).
andPaymentAmtGreaterThan(300L);
//执行查询
return orderMapper.selectByExample(example);
Example
Example 类中有三个内部类 Criteria,Criterion,GeneratedCriteria:
-
Criteria 是查询条件类,继承 GeneratedCriteria 类,,我们构造查询条件就是使用它的方法,它由 Example 对象的 createCriteria()方法来创建。
-
Criterion 是存储字段的条件,例如 id=5 这个条件就是存储在这个类对象的属性中。
-
GeneratedCriteria 是 Criteria 父类,Example 对象映射表的每个字段的 12 个方法都属于这个类,它保存一组 Criterion 对象。
动态查询的 sqlmapper.xml 片段如下:
<select id="selectByExample" parameterType="org.dev.repo.dto.OrderDTOExample" resultMap="BaseResultMap">
select
<!--对应Example中的distinct属性 -->
<if test="distinct">
distinct
</if>
<!-- sql片段,order表字段 -->
<include refid="Base_Column_List" />
from order
<if test="_parameter != null">
<!--这里是重点,sql片段,查询条件-->
<include refid="Example_Where_Clause" />
</if>
<!--对应Example中的orderByClause属性 -->
<if test="orderByClause != null">
order by ${orderByClause}
</if>
</select>
sqlmapper.xml 是如何构造动态查询条件:
<sql id="Example_Where_Clause">
<where>
<!-- 遍历Example中的oredCriteria属性中的元素Criteria,多个Criteria组成or条件 -->
<foreach collection="oredCriteria" item="criteria" separator="or">
<!-- 如果Example.Criteria对象存在一个或多个Criterion对象那么进行遍历,多个Criterion构成and条件 -->
<if test="criteria.valid">
<trim prefix="(" prefixOverrides="and" suffix=")">
<foreach collection="criteria.criteria" item="criterion">
<choose>
<!-- 无值条件,例如name is null -->
<when test="criterion.noValue">
and ${criterion.condition}
</when>
<!-- 单值条件,例如 name='张三' -->
<when test="criterion.singleValue">
and ${criterion.condition} #{criterion.value}
</when>
<!-- 双值条件,对应between -->
<when test="criterion.betweenValue">
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
</when>
<!-- 多值条件,对应in -->
<when test="criterion.listValue">
and ${criterion.condition}
<foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
#{listItem}
</foreach>
</when>
</choose>
</foreach>
</trim>
</if>
</foreach>
</where>
</sql>
通过以上 sqlmapper.xml 我们可以看出 Example 对象及它的内部对象属性在运行时被 mybatis 框架循环遍历解析成动态 sql 语句,与我们手写 sql 语句并无差别。