前提 最近在做创业项目的时候因为有比较多的新需求,需要频繁基于DDL
生成Mybatis
适合的实体、Mapper
接口和映射文件。其中,代码生成器是MyBatis Generator(MBG)
,用到了Mybatis-Generator-Core
相关依赖,这里通过一篇文章详细地分析这个代码生成器的使用方式。本文编写的时候使用的Mybatis-Generator
版本为1.4.0
,其他版本没有进行过调研。
引入插件 Mybatis-Generator
的运行方式有很多种:
基于mybatis-generator-core-x.x.x.jar
和其XML
配置文件,通过命令行运行。
通过Ant
的Task
结合其XML
配置文件运行。
通过Maven
插件运行。
通过Java
代码和其XML
配置文件运行。
通过Java
代码和编程式配置运行。
通过Eclipse Feature
运行。
这里只介绍通过Maven
插件运行和通过Java
代码和其XML
配置文件运行这两种方式,两种方式有个特点:都要提前编写好XML
配置文件。个人感觉XML
配置文件相对直观,后文会花大量篇幅去说明XML
配置文件中的配置项及其作用。这里先注意一点:默认的配置文件为ClassPath:generatorConfig.xml
。
通过编码和配置文件运行 通过编码方式去运行插件先需要引入mybatis-generator-core
依赖,编写本文的时候最新的版本为:
<dependency > <groupId > org.mybatis.generator</groupId > <artifactId > mybatis-generator-core</artifactId > <version > 1.4.0</version > </dependency >
假设编写好的XML
配置文件是ClassPath
下的generator-configuration.xml
,那么使用代码生成器的编码方式大致如下:
List<String> warnings = new ArrayList<>(); boolean overwrite = true ;File configFile = new File("ClassPath路径/generator-configuration.xml" ); ConfigurationParser cp = new ConfigurationParser(warnings); Configuration config = cp.parseConfiguration(configFile); DefaultShellCallback callback = new DefaultShellCallback(overwrite); MyBatisGenerator generator = new MyBatisGenerator(config, callback, warnings); generator.generate(null );
通过Maven插件运行 如果使用Maven
插件,那么不需要 引入mybatis-generator-core
依赖,只需要引入一个Maven
的插件mybatis-generator-maven-plugin
:
<plugins > <plugin > <groupId > org.mybatis.generator</groupId > <artifactId > mybatis-generator-maven-plugin</artifactId > <version > 1.4.0</version > <executions > <execution > <id > Generate MyBatis Artifacts</id > <goals > <goal > generate</goal > </goals > </execution > </executions > <configuration > <verbose > true</verbose > <overwrite > true</overwrite > <configurationFile > ${basedir}/src/main/resources/generator-configuration.xml</configurationFile > </configuration > </plugin > </plugins >
mybatis-generator-maven-plugin
的更详细配置和可选参数可以参考:Running With Maven 。插件配置完毕之后,使用下面的命令即可运行:
mvn mybatis-generator:generate
XML配置文件详解 XML
配置文件才是Mybatis-Generator
的核心,它用于控制代码生成的所有行为。所有非标签独有的公共配置的Key
可以在mybatis-generator-core
的PropertyRegistry
类中找到。下面是一个相对完整的配置文件的模板:
<?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 ="db.properties" /> <classPathEntry location ="/Program Files/IBM/SQLLIB/java/db2java.zip" /> <context id ="DB2Tables" targetRuntime ="MyBatis3" > <jdbcConnection driverClass ="COM.ibm.db2.jdbc.app.DB2Driver" connectionURL ="jdbc:db2:TEST" userId ="db2admin" password ="db2admin" > </jdbcConnection > <plugin type ="org.mybatis.generator.plugins.SerializablePlugin" /> <commentGenerator > <property name ="suppressDate" value ="true" /> <property name ="suppressAllComments" value ="true" /> </commentGenerator > <javaTypeResolver > <property name ="forceBigDecimals" value ="false" /> </javaTypeResolver > <javaModelGenerator targetPackage ="test.model" targetProject ="\MBGTestProject\src" > <property name ="enableSubPackages" value ="true" /> <property name ="trimStrings" value ="true" /> </javaModelGenerator > <sqlMapGenerator targetPackage ="test.xml" targetProject ="\MBGTestProject\src" > <property name ="enableSubPackages" value ="true" /> </sqlMapGenerator > <javaClientGenerator type ="XMLMAPPER" targetPackage ="test.dao" targetProject ="\MBGTestProject\src" > <property name ="enableSubPackages" value ="true" /> </javaClientGenerator > <table schema ="DB2ADMIN" tableName ="ALLTYPES" domainObjectName ="Customer" > <property name ="useActualColumnNames" value ="true" /> <generatedKey column ="ID" sqlStatement ="DB2" identity ="true" /> <columnOverride column ="DATE_FIELD" property ="startDate" /> <ignoreColumn column ="FRED" /> <columnOverride column ="LONG_VARCHAR_FIELD" jdbcType ="VARCHAR" /> </table > </context > </generatorConfiguration >
配置文件中,最外层的标签为<generatorConfiguration>
,它的子标签包括:
0或者1个<properties>
标签,用于指定全局配置文件,下面可以通过占位符的形式读取<properties>
指定文件中的值。
0或者N个<classPathEntry>
标签,<classPathEntry>
只有一个location
属性,用于指定数据源驱动包(jar
或者zip
)的绝对路径,具体选择什么驱动包取决于连接什么类型的数据源。
1或者N个<context>
标签,用于运行时的解析模式和具体的代码生成行为,所以这个标签里面的配置是最重要的。
下面分别列举和分析一下<context>
标签和它的主要子标签的一些属性配置和功能。
context标签 <context>
标签在mybatis-generator-core
中对应的实现类为org.mybatis.generator.config.Context
,它除了大量的子标签配置之外,比较主要的属性是:
id
:Context
示例的唯一ID
,用于输出错误信息时候作为唯一标记。
targetRuntime
:用于执行代码生成模式。
defaultModelType
:控制Domain
类的生成行为。执行引擎为MyBatis3DynamicSql
或者MyBatis3Kotlin
时忽略此配置,可选值:
conditional
:默认值,类似hierarchical
,但是只有一个主键的时候会合并所有属性生成在同一个类。
flat
:所有内容全部生成在一个对象中。
hierarchical
:键生成一个XXKey对象,Blob等单独生成一个对象,其他简单属性在一个对象中。
targetRuntime
属性的可选值比较多,这里做个简单的小结:
属性
功能描述
MyBatis3DynamicSql
默认值,兼容JDK8+
和MyBatis 3.4.2+
,不会生成XML
映射文件,忽略<sqlMapGenerator>
的配置项,也就是Mapper
全部注解化,依赖于MyBatis Dynamic SQL
类库
MyBatis3Kotlin
行为类似于MyBatis3DynamicSql
,不过兼容Kotlin
的代码生成
MyBatis3
提供基本的基于动态SQL
的CRUD
方法和XXXByExample
方法,会生成XML
映射文件
MyBatis3Simple
提供基本的基于动态SQL
的CRUD
方法,会生成XML
映射文件
MyBatis3DynamicSqlV1
已经过时,不推荐使用
笔者偏向于把SQL
文件和代码分离,所以一般选用MyBatis3
或者MyBatis3Simple
。例如:
<context id ="default" targetRuntime ="MyBatis3" >
<context>
标签支持0或N个<property>
标签,<property>
的可选属性有:
property属性
功能描述
默认值
备注
autoDelimitKeywords
是否使用分隔符号括住数据库关键字
false
例如MySQL
中会使用反引号括住关键字
beginningDelimiter
分隔符号的开始符号
"
endingDelimiter
分隔符号的结束号
"
javaFileEncoding
文件的编码
系统默认值
来源于java.nio.charset.Charset
javaFormatter
类名和文件格式化器
DefaultJavaFormatter
见JavaFormatter
和DefaultJavaFormatter
targetJava8
是否JDK8和启动其特性
true
kotlinFileEncoding
Kotlin
文件编码
系统默认值
来源于java.nio.charset.Charset
kotlinFormatter
Kotlin
类名和文件格式化器
DefaultKotlinFormatter
见KotlinFormatter
和DefaultKotlinFormatter
xmlFormatter
XML
文件格式化器
DefaultXmlFormatter
见XmlFormatter
和DefaultXmlFormatter
jdbcConnection标签 <jdbcConnection>
标签用于指定数据源的连接信息 ,它在mybatis-generator-core
中对应的实现类为org.mybatis.generator.config.JDBCConnectionConfiguration
,主要属性包括:
属性
功能描述
是否必须
driverClass
数据源驱动的全类名
Y
connectionURL
JDBC
的连接URL
Y
userId
连接到数据源的用户名
N
password
连接到数据源的密码
N
<commentGenerator>
标签是可选的,用于控制生成的实体的注释内容 。它在mybatis-generator-core
中对应的实现类为org.mybatis.generator.internal.DefaultCommentGenerator
,可以通过可选的type
属性指定一个自定义的CommentGenerator
实现。<commentGenerator>
标签支持0或N个<property>
标签,<property>
的可选属性有:
property属性
功能描述
默认值
suppressAllComments
是否生成注释
false
suppressDate
是否在注释中添加生成的时间戳
false
dateFormat
配合suppressDate
使用,指定输出时间戳的格式
java.util.Date#toString()
addRemarkComments
是否输出表和列的Comment
信息
false
笔者建议保持默认值,也就是什么注释都不输出,生成代码干净的实体。
javaTypeResolver标签 <javaTypeResolver>
标签是<context>
的子标签,用于解析和计算数据库列类型和Java
类型的映射关系,该标签只包含一个type
属性,用于指定org.mybatis.generator.api.JavaTypeResolver
接口的实现类。<javaTypeResolver>
标签支持0或N个<property>
标签,<property>
的可选属性有:
property属性
功能描述
默认值
forceBigDecimals
是否强制把所有的数字类型强制使用java.math.BigDecimal
类型表示
false
useJSR310Types
是否支持JSR310
,主要是JSR310
的新日期类型
false
如果useJSR310Types
属性设置为true
,那么生成代码的时候类型映射关系如下(主要针对日期时间类型):
数据库(JDBC)类型
Java类型
DATE
java.time.LocalDate
TIME
java.time.LocalTime
TIMESTAMP
java.time.LocalDateTime
TIME_WITH_TIMEZONE
java.time.OffsetTime
TIMESTAMP_WITH_TIMEZONE
java.time.OffsetDateTime
引入mybatis-generator-core
后,可以查看JavaTypeResolver
的默认实现为JavaTypeResolverDefaultImpl
,从它的源码可以得知一些映射关系:
BIGINT --> Long BIT --> Boolean INTEGER --> Integer SMALLINT --> Short TINYINT --> Byte ......
有些时候,我们希望INTEGER
、SMALLINT
和TINYINT
都映射为Integer
,那么我们需要覆盖JavaTypeResolverDefaultImpl
的构造方法:
public class DefaultJavaTypeResolver extends JavaTypeResolverDefaultImpl { public DefaultJavaTypeResolver () { super (); typeMap.put(Types.SMALLINT, new JdbcTypeInformation("SMALLINT" , new FullyQualifiedJavaType(Integer.class.getName()))); typeMap.put(Types.TINYINT, new JdbcTypeInformation("TINYINT" , new FullyQualifiedJavaType(Integer.class.getName()))); } }
注意一点的是这种自定义实现JavaTypeResolver
接口的方式使用编程式运行MBG
会相对方便,如果需要使用Maven
插件运行,那么需要把上面的DefaultJavaTypeResolver
类打包到插件中。
javaModelGenerator标签 <javaModelGenerator标签>
标签是<context>
的子标签,主要用于控制实体(Model
)类的代码生成行为。它支持的属性如下:
属性
功能描述
是否必须
备注
targetPackage
生成的实体类的包名
Y
例如club.throwable.model
targetProject
生成的实体类文件相对于项目(根目录)的位置
Y
例如src/main/java
<javaModelGenerator标签>
标签支持0或N个<property>
标签,<property>
的可选属性有:
property属性
功能描述
默认值
备注
constructorBased
是否生成一个带有所有字段属性的构造函数
false
MyBatis3Kotlin
模式下忽略此属性配置
enableSubPackages
是否允许通过Schema
生成子包
false
如果为true
,例如包名为club.throwable
,如果Schema
为xyz
,那么实体类文件最终会生成在club.throwable.xyz
目录
exampleTargetPackage
生成的伴随实体类的Example
类的包名
-
-
exampleTargetProject
生成的伴随实体类的Example
类文件相对于项目(根目录)的位置
-
-
immutable
是否不可变
false
如果为true
,则不会生成Setter
方法,所有字段都使用final
修饰,提供一个带有所有字段属性的构造函数
rootClass
为生成的实体类添加父类
-
通过value
指定父类的全类名即可
trimStrings
Setter
方法是否对字符串类型进行一次trim
操作
false
-
javaClientGenerator标签 <javaClientGenerator>
标签是<context>
的子标签,主要用于控制Mapper
接口的代码生成行为。它支持的属性如下:
属性
功能描述
是否必须
备注
type
Mapper
接口生成策略
Y
<context>
标签的targetRuntime
属性为MyBatis3DynamicSql
或者MyBatis3Kotlin
时此属性配置忽略
targetPackage
生成的Mapper
接口的包名
Y
例如club.throwable.mapper
targetProject
生成的Mapper
接口文件相对于项目(根目录)的位置
Y
例如src/main/java
type
属性的可选值如下:
ANNOTATEDMAPPER
:Mapper
接口生成的时候依赖于注解和SqlProviders
(也就是纯注解实现),不会生成XML
映射文件。
XMLMAPPER
:Mapper
接口生成接口方法,对应的实现代码生成在XML
映射文件中(也就是纯映射文件实现)。
MIXEDMAPPER
:Mapper
接口生成的时候复杂的方法实现生成在XML
映射文件中,而简单的实现通过注解和SqlProviders
实现(也就是注解和映射文件混合实现)。
注意两点:
<context>
标签的targetRuntime
属性指定为MyBatis3Simple
的时候,type
只能选用ANNOTATEDMAPPER
或者XMLMAPPER
。
<context>
标签的targetRuntime
属性指定为MyBatis3
的时候,type
可以选用ANNOTATEDMAPPER
、XMLMAPPER
或者MIXEDMAPPER
。
<javaClientGenerator>
标签支持0或N个<property>
标签,<property>
的可选属性有:
property属性
功能描述
默认值
备注
enableSubPackages
是否允许通过Schema
生成子包
false
如果为true
,例如包名为club.throwable
,如果Schema
为xyz
,那么Mapper
接口文件最终会生成在club.throwable.xyz
目录
useLegacyBuilder
是否通过SQL Builder
生成动态SQL
false
rootInterface
为生成的Mapper
接口添加父接口
-
通过value
指定父接口的全类名即可
sqlMapGenerator标签 <sqlMapGenerator>
标签是<context>
的子标签,主要用于控制XML
映射文件的代码生成行为。它支持的属性如下:
属性
功能描述
是否必须
备注
targetPackage
生成的XML
映射文件的包名
Y
例如mappings
targetProject
生成的XML
映射文件相对于项目(根目录)的位置
Y
例如src/main/resources
<sqlMapGenerator>
标签支持0或N个<property>
标签,<property>
的可选属性有:
property属性
功能描述
默认值
备注
enableSubPackages
是否允许通过Schema
生成子包
false
-
plugin标签 <plugin>
标签是<context>
的子标签,用于引入一些插件对代码生成的一些特性进行扩展,该标签只包含一个type
属性,用于指定org.mybatis.generator.api.Plugin
接口的实现类。内置的插件实现见Supplied Plugins 。例如:引入org.mybatis.generator.plugins.SerializablePlugin
插件会让生成的实体类自动实现java.io.Serializable
接口并且添加serialVersionUID
属性。
table标签 <table>
标签是<context>
的子标签,主要用于配置要生成代码的数据库表格,定制一些代码生成行为等等。它支持的属性众多,列举如下:
属性
功能描述
是否必须
备注
tableName
数据库表名称
Y
例如t_order
schema
数据库Schema
N
-
catalog
数据库Catalog
N
-
alias
表名称标签
N
如果指定了此值,则查询列的时候结果格式为alias_column
domainObjectName
表对应的实体类名称,可以通过.
指定包路径
N
如果指定了bar.User
,则包名为bar
,实体类名称为User
mapperName
表对应的Mapper
接口类名称,可以通过.
指定包路径
N
如果指定了bar.UserMapper
,则包名为bar
,Mapper
接口类名称为UserMapper
sqlProviderName
动态SQL
提供类SqlProvider
的类名称
N
-
enableInsert
是否允许生成insert
方法
N
默认值为true
,执行引擎为MyBatis3DynamicSql
或者MyBatis3Kotlin
时忽略此配置
enableSelectByPrimaryKey
是否允许生成selectByPrimaryKey
方法
N
默认值为true
,执行引擎为MyBatis3DynamicSql
或者MyBatis3Kotlin
时忽略此配置
enableSelectByExample
是否允许生成selectByExample
方法
N
默认值为true
,执行引擎为MyBatis3DynamicSql
或者MyBatis3Kotlin
时忽略此配置
enableUpdateByPrimaryKey
是否允许生成updateByPrimaryKey
方法
N
默认值为true
,执行引擎为MyBatis3DynamicSql
或者MyBatis3Kotlin
时忽略此配置
enableDeleteByPrimaryKey
是否允许生成deleteByPrimaryKey
方法
N
默认值为true
,执行引擎为MyBatis3DynamicSql
或者MyBatis3Kotlin
时忽略此配置
enableDeleteByExample
是否允许生成deleteByExample
方法
N
默认值为true
,执行引擎为MyBatis3DynamicSql
或者MyBatis3Kotlin
时忽略此配置
enableCountByExample
是否允许生成countByExample
方法
N
默认值为true
,执行引擎为MyBatis3DynamicSql
或者MyBatis3Kotlin
时忽略此配置
enableUpdateByExample
是否允许生成updateByExample
方法
N
默认值为true
,执行引擎为MyBatis3DynamicSql
或者MyBatis3Kotlin
时忽略此配置
selectByPrimaryKeyQueryId
value
指定对应的主键列提供列表查询功能
N
执行引擎为MyBatis3DynamicSql
或者MyBatis3Kotlin
时忽略此配置
selectByExampleQueryId
value
指定对应的查询ID
提供列表查询功能
N
执行引擎为MyBatis3DynamicSql
或者MyBatis3Kotlin
时忽略此配置
modelType
覆盖<context>
的defaultModelType
属性
N
见<context>
的defaultModelType
属性
escapeWildcards
是否对通配符进行转义
N
-
delimitIdentifiers
标记匹配表名称的时候是否需要使用分隔符去标记生成的SQL
N
-
delimitAllColumns
是否所有的列都添加分隔符
N
默认值为false
,如果设置为true
,所有列名会添加起始和结束分隔符
<table>
标签支持0或N个<property>
标签,<property>
的可选属性有:
property属性
功能描述
默认值
备注
constructorBased
是否为实体类生成一个带有所有字段的构造函数
false
执行引擎为MyBatis3Kotlin
的时候此属性忽略
ignoreQualifiersAtRuntime
是否在运行时忽略别名
false
如果为true
,则不会在生成表的时候把schema
和catalog
作为表的前缀
immutable
实体类是否不可变
false
执行引擎为MyBatis3Kotlin
的时候此属性忽略
modelOnly
是否仅仅生成实体类
false
-
rootClass
如果配置此属性,则实体类会继承此指定的超类
-
如果有主键属性会把主键属性在超类生成
rootInterface
如果配置此属性,则实体类会实现此指定的接口
-
执行引擎为MyBatis3Kotlin
或者MyBatis3DynamicSql
的时候此属性忽略
runtimeCatalog
指定运行时的Catalog
-
当生成表和运行时的表的Catalog
不一样的时候可以使用该属性进行配置
runtimeSchema
指定运行时的Schema
-
当生成表和运行时的表的Schema
不一样的时候可以使用该属性进行配置
runtimeTableName
指定运行时的表名称
-
当生成表和运行时的表的表名称不一样的时候可以使用该属性进行配置
selectAllOrderByClause
指定字句内容添加到selectAll()
方法的order by
子句之中
-
执行引擎为MyBatis3Simple
的时候此属性才适用
trimStrings
实体类的字符串类型属性会做trim
处理
-
执行引擎为MyBatis3Kotlin
的时候此属性忽略
useActualColumnNames
是否使用列名作为实体类的属性名
false
-
useColumnIndexes
XML
映射文件中生成的ResultMap
使用列索引定义而不是列名称
false
执行引擎为MyBatis3Kotlin
或者MyBatis3DynamicSql
的时候此属性忽略
useCompoundPropertyNames
是否把列名和列备注拼接起来生成实体类属性名
false
-
<table>
标签还支持众多的非 property
的子标签:
0或1个<generatedKey>
用于指定主键生成的规则,指定此标签后会生成一个<selectKey>
标签:
<generatedKey column ="id" sqlStatement ="MySql" type ="post" identity ="true" />
0或1个<domainObjectRenamingRule>
用于指定实体类重命名规则:
<domainObjectRenamingRule searchString ="^Sys" replaceString ="" />
0或1个<columnRenamingRule>
用于指定列重命名规则:
<columnRenamingRule searchString ="^CUST_" replaceString ="" />
0或N个<columnOverride>
用于指定具体列的覆盖映射规则:
<columnOverride column ="customer_name" property ="customerName" javaType ="" jdbcType ="" typeHandler ="" delimitedColumnName ="" isGeneratedAlways ="" > <property name ="trimStrings" value ="true" /> <columnOverride />
0或N个<ignoreColumn>
用于指定忽略生成的列:
<ignoreColumn column ="version" delimitedColumnName ="false" />
实战 如果需要深度定制一些代码生成行为,建议引入mybatis-generator-core
并且通过编程式执行代码生成方法,否则可以选用Maven
插件。假设我们在本地数据local
有一张t_order
表如下:
CREATE TABLE `t_order`( id BIGINT UNSIGNED PRIMARY KEY COMMENT '主键' , order_id VARCHAR (64 ) NOT NULL COMMENT '订单ID' , create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间' , amount DECIMAL (10 , 2 ) NOT NULL DEFAULT 0 COMMENT '金额' , order_status TINYINT NOT NULL DEFAULT 0 COMMENT '订单状态' , UNIQUE uniq_order_id (`order_id`) ) COMMENT '订单表' ;
假设项目的结构如下:
mbg-sample - main - java - club - throwable - resources
下面会基于此前提举三个例子。编写基础的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 > <classPathEntry location ="I:\Develop\Maven-Repository\mysql\mysql-connector-java\5.1.48\mysql-connector-java-5.1.48.jar" /> <context id ="default" targetRuntime ="这里选择合适的引擎" > <property name ="javaFileEncoding" value ="UTF-8" /> <commentGenerator > <property name ="suppressDate" value ="true" /> <property name ="suppressAllComments" value ="true" /> </commentGenerator > <jdbcConnection driverClass ="com.mysql.jdbc.Driver" connectionURL ="jdbc:mysql://localhost:3306/local" userId ="root" password ="root" > </jdbcConnection > <javaTypeResolver > <property name ="forceBigDecimals" value ="false" /> </javaTypeResolver > <javaModelGenerator targetPackage ="club.throwable.entity" targetProject ="src/main/java" > <property name ="enableSubPackages" value ="true" /> </javaModelGenerator > <sqlMapGenerator targetPackage ="mappings" targetProject ="src/main/resources" > <property name ="enableSubPackages" value ="true" /> </sqlMapGenerator > <javaClientGenerator type ="这里选择合适的Mapper类型" targetPackage ="club.throwable.dao" targetProject ="src/main/java" > <property name ="enableSubPackages" value ="true" /> </javaClientGenerator > <table tableName ="t_order" enableCountByExample ="false" enableDeleteByExample ="false" enableSelectByExample ="false" enableUpdateByExample ="false" domainObjectName ="Order" mapperName ="OrderMapper" > <generatedKey column ="id" sqlStatement ="MySql" /> </table > </context > </generatorConfiguration >
纯注解 使用纯注解需要引入mybatis-dynamic-sql
:
<dependency > <groupId > org.mybatis.dynamic-sql</groupId > <artifactId > mybatis-dynamic-sql</artifactId > <version > 1.1.4</version > </dependency >
需要修改两个位置:
<context id ="default" targetRuntime ="MyBatis3DynamicSql" > ... <javaClientGenerator type ="ANNOTATEDMAPPER" ...
运行结果会生成三个类:
public class Order { @Generated("org.mybatis.generator.api.MyBatisGenerator") private Long id; @Generated("org.mybatis.generator.api.MyBatisGenerator") private String orderId; @Generated("org.mybatis.generator.api.MyBatisGenerator") private Date createTime; @Generated("org.mybatis.generator.api.MyBatisGenerator") private BigDecimal amount; @Generated("org.mybatis.generator.api.MyBatisGenerator") private Byte orderStatus; @Generated("org.mybatis.generator.api.MyBatisGenerator") public Long getId () { return id; } @Generated("org.mybatis.generator.api.MyBatisGenerator") public void setId (Long id) { this .id = id; } @Generated("org.mybatis.generator.api.MyBatisGenerator") public String getOrderId () { return orderId; } @Generated("org.mybatis.generator.api.MyBatisGenerator") public void setOrderId (String orderId) { this .orderId = orderId; } @Generated("org.mybatis.generator.api.MyBatisGenerator") public Date getCreateTime () { return createTime; } @Generated("org.mybatis.generator.api.MyBatisGenerator") public void setCreateTime (Date createTime) { this .createTime = createTime; } @Generated("org.mybatis.generator.api.MyBatisGenerator") public BigDecimal getAmount () { return amount; } @Generated("org.mybatis.generator.api.MyBatisGenerator") public void setAmount (BigDecimal amount) { this .amount = amount; } @Generated("org.mybatis.generator.api.MyBatisGenerator") public Byte getOrderStatus () { return orderStatus; } @Generated("org.mybatis.generator.api.MyBatisGenerator") public void setOrderStatus (Byte orderStatus) { this .orderStatus = orderStatus; } } public final class OrderDynamicSqlSupport { @Generated("org.mybatis.generator.api.MyBatisGenerator") public static final Order order = new Order(); @Generated("org.mybatis.generator.api.MyBatisGenerator") public static final SqlColumn<Long> id = order.id; @Generated("org.mybatis.generator.api.MyBatisGenerator") public static final SqlColumn<String> orderId = order.orderId; @Generated("org.mybatis.generator.api.MyBatisGenerator") public static final SqlColumn<Date> createTime = order.createTime; @Generated("org.mybatis.generator.api.MyBatisGenerator") public static final SqlColumn<BigDecimal> amount = order.amount; @Generated("org.mybatis.generator.api.MyBatisGenerator") public static final SqlColumn<Byte> orderStatus = order.orderStatus; @Generated("org.mybatis.generator.api.MyBatisGenerator") public static final class Order extends SqlTable { public final SqlColumn<Long> id = column("id" , JDBCType.BIGINT); public final SqlColumn<String> orderId = column("order_id" , JDBCType.VARCHAR); public final SqlColumn<Date> createTime = column("create_time" , JDBCType.TIMESTAMP); public final SqlColumn<BigDecimal> amount = column("amount" , JDBCType.DECIMAL); public final SqlColumn<Byte> orderStatus = column("order_status" , JDBCType.TINYINT); public Order () { super ("t_order" ); } } } @Mapper public interface OrderMapper { @Generated("org.mybatis.generator.api.MyBatisGenerator") BasicColumn[] selectList = BasicColumn.columnList(id, orderId, createTime, amount, orderStatus); @Generated("org.mybatis.generator.api.MyBatisGenerator") @SelectProvider(type=SqlProviderAdapter.class, method="select") long count (SelectStatementProvider selectStatement) ; @Generated("org.mybatis.generator.api.MyBatisGenerator") @DeleteProvider(type=SqlProviderAdapter.class, method="delete") int delete (DeleteStatementProvider deleteStatement) ; @Generated("org.mybatis.generator.api.MyBatisGenerator") @InsertProvider(type=SqlProviderAdapter.class, method="insert") @SelectKey(statement="SELECT LAST_INSERT_ID()", keyProperty="record.id", before=true, resultType=Long.class) int insert (InsertStatementProvider<Order> insertStatement) ; @Generated("org.mybatis.generator.api.MyBatisGenerator") @SelectProvider(type=SqlProviderAdapter.class, method="select") @Results(id="OrderResult", value = { @Result(column="id", property="id", jdbcType=JdbcType.BIGINT, id=true), @Result(column="order_id", property="orderId", jdbcType=JdbcType.VARCHAR), @Result(column="create_time", property="createTime", jdbcType=JdbcType.TIMESTAMP), @Result(column="amount", property="amount", jdbcType=JdbcType.DECIMAL), @Result(column="order_status", property="orderStatus", jdbcType=JdbcType.TINYINT) }) Optional<Order> selectOne (SelectStatementProvider selectStatement) ; @Generated("org.mybatis.generator.api.MyBatisGenerator") @SelectProvider(type=SqlProviderAdapter.class, method="select") @Results(id="OrderResult", value = { @Result(column="id", property="id", jdbcType=JdbcType.BIGINT, id=true), @Result(column="order_id", property="orderId", jdbcType=JdbcType.VARCHAR), @Result(column="create_time", property="createTime", jdbcType=JdbcType.TIMESTAMP), @Result(column="amount", property="amount", jdbcType=JdbcType.DECIMAL), @Result(column="order_status", property="orderStatus", jdbcType=JdbcType.TINYINT) }) List<Order> selectMany (SelectStatementProvider selectStatement) ; @Generated("org.mybatis.generator.api.MyBatisGenerator") @UpdateProvider(type=SqlProviderAdapter.class, method="update") int update (UpdateStatementProvider updateStatement) ; @Generated("org.mybatis.generator.api.MyBatisGenerator") default long count (CountDSLCompleter completer) { return MyBatis3Utils.countFrom(this ::count, order, completer); } @Generated("org.mybatis.generator.api.MyBatisGenerator") default int delete (DeleteDSLCompleter completer) { return MyBatis3Utils.deleteFrom(this ::delete, order, completer); } @Generated("org.mybatis.generator.api.MyBatisGenerator") default int deleteByPrimaryKey (Long id_) { return delete(c -> c.where(id, isEqualTo(id_)) ); } @Generated("org.mybatis.generator.api.MyBatisGenerator") default int insert (Order record) { return MyBatis3Utils.insert(this ::insert, record, order, c -> c.map(id).toProperty("id" ) .map(orderId).toProperty("orderId" ) .map(createTime).toProperty("createTime" ) .map(amount).toProperty("amount" ) .map(orderStatus).toProperty("orderStatus" ) ); } @Generated("org.mybatis.generator.api.MyBatisGenerator") default int insertSelective (Order record) { return MyBatis3Utils.insert(this ::insert, record, order, c -> c.map(id).toProperty("id" ) .map(orderId).toPropertyWhenPresent("orderId" , record::getOrderId) .map(createTime).toPropertyWhenPresent("createTime" , record::getCreateTime) .map(amount).toPropertyWhenPresent("amount" , record::getAmount) .map(orderStatus).toPropertyWhenPresent("orderStatus" , record::getOrderStatus) ); } @Generated("org.mybatis.generator.api.MyBatisGenerator") default Optional<Order> selectOne (SelectDSLCompleter completer) { return MyBatis3Utils.selectOne(this ::selectOne, selectList, order, completer); } @Generated("org.mybatis.generator.api.MyBatisGenerator") default List<Order> select (SelectDSLCompleter completer) { return MyBatis3Utils.selectList(this ::selectMany, selectList, order, completer); } @Generated("org.mybatis.generator.api.MyBatisGenerator") default List<Order> selectDistinct (SelectDSLCompleter completer) { return MyBatis3Utils.selectDistinct(this ::selectMany, selectList, order, completer); } @Generated("org.mybatis.generator.api.MyBatisGenerator") default Optional<Order> selectByPrimaryKey (Long id_) { return selectOne(c -> c.where(id, isEqualTo(id_)) ); } @Generated("org.mybatis.generator.api.MyBatisGenerator") default int update (UpdateDSLCompleter completer) { return MyBatis3Utils.update(this ::update, order, completer); } @Generated("org.mybatis.generator.api.MyBatisGenerator") static UpdateDSL<UpdateModel> updateAllColumns (Order record, UpdateDSL<UpdateModel> dsl) { return dsl.set(id).equalTo(record::getId) .set(orderId).equalTo(record::getOrderId) .set(createTime).equalTo(record::getCreateTime) .set(amount).equalTo(record::getAmount) .set(orderStatus).equalTo(record::getOrderStatus); } @Generated("org.mybatis.generator.api.MyBatisGenerator") static UpdateDSL<UpdateModel> updateSelectiveColumns (Order record, UpdateDSL<UpdateModel> dsl) { return dsl.set(id).equalToWhenPresent(record::getId) .set(orderId).equalToWhenPresent(record::getOrderId) .set(createTime).equalToWhenPresent(record::getCreateTime) .set(amount).equalToWhenPresent(record::getAmount) .set(orderStatus).equalToWhenPresent(record::getOrderStatus); } @Generated("org.mybatis.generator.api.MyBatisGenerator") default int updateByPrimaryKey (Order record) { return update(c -> c.set(orderId).equalTo(record::getOrderId) .set(createTime).equalTo(record::getCreateTime) .set(amount).equalTo(record::getAmount) .set(orderStatus).equalTo(record::getOrderStatus) .where(id, isEqualTo(record::getId)) ); } @Generated("org.mybatis.generator.api.MyBatisGenerator") default int updateByPrimaryKeySelective (Order record) { return update(c -> c.set(orderId).equalToWhenPresent(record::getOrderId) .set(createTime).equalToWhenPresent(record::getCreateTime) .set(amount).equalToWhenPresent(record::getAmount) .set(orderStatus).equalToWhenPresent(record::getOrderStatus) .where(id, isEqualTo(record::getId)) ); } }
极简XML映射文件 极简XML
映射文件生成只需要简单修改配置文件:
<context id ="default" targetRuntime ="MyBatis3Simple" > ... <javaClientGenerator type ="XMLMAPPER" ...
生成三个文件:
public class Order { private Long id; private String orderId; private Date createTime; private BigDecimal amount; private Byte orderStatus; public Long getId () { return id; } public void setId (Long id) { this .id = id; } public String getOrderId () { return orderId; } public void setOrderId (String orderId) { this .orderId = orderId; } public Date getCreateTime () { return createTime; } public void setCreateTime (Date createTime) { this .createTime = createTime; } public BigDecimal getAmount () { return amount; } public void setAmount (BigDecimal amount) { this .amount = amount; } public Byte getOrderStatus () { return orderStatus; } public void setOrderStatus (Byte orderStatus) { this .orderStatus = orderStatus; } } public interface OrderMapper { int deleteByPrimaryKey (Long id) ; int insert (Order record) ; Order selectByPrimaryKey (Long id) ; List<Order> selectAll () ; int updateByPrimaryKey (Order record) ; } <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="club.throwable.dao.OrderMapper" > <resultMap id="BaseResultMap" type="club.throwable.entity.Order" > <id column="id" jdbcType="BIGINT" property="id" /> <result column="order_id" jdbcType="VARCHAR" property="orderId" /> <result column="create_time" jdbcType="TIMESTAMP" property="createTime" /> <result column="amount" jdbcType="DECIMAL" property="amount" /> <result column="order_status" jdbcType="TINYINT" property="orderStatus" /> </resultMap> <delete id="deleteByPrimaryKey" parameterType="java.lang.Long" > delete from t_order where id = #{id,jdbcType=BIGINT} </delete> <insert id="insert" parameterType="club.throwable.entity.Order" > <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Long" > SELECT LAST_INSERT_ID () </selectKey> insert into t_order (order_id, create_time, amount, order_status) values (#{orderId,jdbcType=VARCHAR}, #{createTime,jdbcType=TIMESTAMP}, #{amount,jdbcType=DECIMAL}, #{orderStatus,jdbcType=TINYINT}) </insert> <update id ="updateByPrimaryKey" parameterType="club.throwable.entity.Order" > update t_order set order_id = #{orderId,jdbcType=VARCHAR}, create_time = #{createTime,jdbcType=TIMESTAMP}, amount = #{amount,jdbcType=DECIMAL}, order_status = #{orderStatus,jdbcType=TINYINT} where id = #{id,jdbcType=BIGINT} </update> <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap" > select id, order_id, create_time, amount, order_status from t_order where id = #{id,jdbcType=BIGINT} </select> <select id="selectAll" resultMap="BaseResultMap" > select id, order_id, create_time, amount, order_status from t_order </select> <resultMap id="BaseResultMap" type="club.throwable.entity.Order" > <id column="id" jdbcType="BIGINT" property="id" /> <result column="order_id" jdbcType="VARCHAR" property="orderId" /> <result column="create_time" jdbcType="TIMESTAMP" property="createTime" /> <result column="amount" jdbcType="DECIMAL" property="amount" /> <result column="order_status" jdbcType="TINYINT" property="orderStatus" /> </resultMap> <delete id="deleteByPrimaryKey" parameterType="java.lang.Long" > delete from t_order where id = #{id,jdbcType=BIGINT} </delete> <insert id="insert" parameterType="club.throwable.entity.Order" > <selectKey keyProperty="id" order="BEFORE" resultType="java.lang.Long" > SELECT LAST_INSERT_ID () </selectKey> insert into t_order (id, order_id, create_time, amount, order_status) values (#{id,jdbcType=BIGINT}, #{orderId,jdbcType=VARCHAR}, #{createTime,jdbcType=TIMESTAMP}, #{amount,jdbcType=DECIMAL}, #{orderStatus,jdbcType=TINYINT}) </insert> <update id ="updateByPrimaryKey" parameterType="club.throwable.entity.Order" > update t_order set order_id = #{orderId,jdbcType=VARCHAR}, create_time = #{createTime,jdbcType=TIMESTAMP}, amount = #{amount,jdbcType=DECIMAL}, order_status = #{orderStatus,jdbcType=TINYINT} where id = #{id,jdbcType=BIGINT} </update> <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap" > select id, order_id, create_time, amount, order_status from t_order where id = #{id,jdbcType=BIGINT} </select> <select id="selectAll" resultMap="BaseResultMap" > select id, order_id, create_time, amount, order_status from t_order </select> </mapper>
编程式自定义类型映射 笔者喜欢把所有的非长整型的数字,统一使用Integer
接收,因此需要自定义类型映射。编写映射器如下:
public class DefaultJavaTypeResolver extends JavaTypeResolverDefaultImpl { public DefaultJavaTypeResolver () { super (); typeMap.put(Types.SMALLINT, new JdbcTypeInformation("SMALLINT" , new FullyQualifiedJavaType(Integer.class.getName()))); typeMap.put(Types.TINYINT, new JdbcTypeInformation("TINYINT" , new FullyQualifiedJavaType(Integer.class.getName()))); } }
此时最好使用编程式运行代码生成器,修改XML
配置文件:
<javaTypeResolver type ="club.throwable.mbg.DefaultJavaTypeResolver" > <property name ="forceBigDecimals" value ="false" /> </javaTypeResolver > ...
运行方法代码如下:
public class Main { public static void main (String[] args) throws Exception { List<String> warnings = new ArrayList<>(); boolean overwrite = true ; ConfigurationParser cp = new ConfigurationParser(warnings); Configuration config = cp.parseConfiguration(Main.class.getResourceAsStream("/generator-configuration.xml" )); DefaultShellCallback callback = new DefaultShellCallback(overwrite); MyBatisGenerator generator = new MyBatisGenerator(config, callback, warnings); generator.generate(null ); } }
数据库的order_status
是TINYINT
类型,生成出来的文件中的orderStatus
字段全部替换使用Integer
类型定义。
小结 本文相对详尽地介绍了Mybatis Generator
的使用方式,具体分析了XML
配置文件中主要标签以及标签属性的功能。因为Mybatis
在Java
的ORM
框架体系中还会有一段很长的时间处于主流地位,了解Mybatis Generator
可以简化CRUD
方法模板代码、实体以及Mapper
接口代码生成,从而解放大量生产力。Mybatis Generator
有不少第三方的扩展,例如tk.mapper
或者mybatis-plus
自身的扩展,可能附加的功能不一样,但是基本的使用是一致的。
参考资料:
(本文完 c-5-d e-a-20191216 1:00)