Article Directory
The process of manually writing SQL statements and mapping entity classes is often tedious and error-prone. At this time, we can use the powerful tool MyBatis Generator (MBG) to automatically generate this part of the code.
1. What is MyBatis Generator
MyBatis Generator is a code generator designed for MyBatis or iBATIS, officially provided by MyBatis. It can generate MyBatis Java entity classes, mapper.xml files and corresponding Mapper interfaces, which greatly reduces the workload of developers writing SQL statements and mapping entity classes by hand, and improves development efficiency.
2. Use MyBatis Generator
2.1 Import dependencies
First, we need to add MyBatis Generator dependencies to the project:
<!-- MyBatis 生成器 -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>${mybatis-generator.version}</version>
</dependency>
Of course, in order to complete the entire process, we also need some other related dependencies, such as MyBatis, PageHelper, Druid and MySQL drivers:
<!-- SpringBoot整合MyBatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis-starter.version}</version>
</dependency>
<!-- MyBatis分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>${pagehelper-starter.version}</version>
</dependency>
<!-- 集成druid连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<!-- Mysql数据库驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-connector.version}</version>
</dependency>
Note: The version number of the above
${}
part needs to be adjusted according to the actual situation of the specific project.
2.2 application configuration
application.yml
Perform some basic configurations in the configuration file of Spring Boot , mainly including the configuration of the data source and the related configuration of MyBatis:
# 配置数据源
spring:
datasource:
url: jdbc:mysql://localhost:3306/<database>?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
username: <username>
password: <password>
# Mybatis 相关配置
mybatis:
# 配置映射文件路径
mapper-locations:
- classpath:mapper/*.xml
- classpath:mbg/mapper/*/*.xml
Here is a brief description of the basic configuration above:
<database>
Replace ,<username>
and in the above data source configuration<password>
with your specific connection information;- In order to distinguish between the mapper.xml automatically generated by MyBatis Generator and our own handwritten mapper.xml, we agree that the mapping file written by ourselves is placed in the
resources/mapper
directory, while the mapping file automatically generated by MyBatis Generator is placedresources/mbg/mapper
in the directory.
2.3 Add Java configuration
@MapperScan
In order for Spring Boot to automatically scan the generated Mapper interface, we need to prepare a configuration class to specify the scan path by adding annotations to the Java configuration class .
/**
* MyBatis 配置类(扫描 Mapper 接口)
*/
@MapperScan({
"cn.javgo.learningmybatis.mapper","cn.javgo.learningmybatis.mbg.mapper"})
@SpringBootConfiguration
public class MyBatisConfig {
}
Note that above we also agree that the Mapper interface written by ourselves is placed cn.javgo.learningmybatis.mapper
under the package, while the Mapper interface automatically generated by MyBatis Generator is placed cn.javgo.learningmybatis.mbg.mapper
under the package.
2.4 MBG configuration
resources
Prepare a configuration file under the directory to generator.properties
configure the basic information of the database connection:
jdbc.driverClass=com.mysql.cj.jdbc.Driver
jdbc.connectionURL=jdbc:mysql://localhost:3306/<database>?useSSL=false&serverTimezone=UTC
jdbc.username=<username>
jdbc.password=<password>
<database>
Note: You need to replace ,<username>
and in the above data source configuration<password>
with your specific connection information.
Then, you need to resources
create a MBG configuration file in the directory generatorConfig.xml
, configure the database to be connected, and specify the location of the generated entity class, Mapper interface and XML mapping file.
The specific content of the configuration file is set according to the actual situation. The following is a simple example:
<?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"/>
<!-- MBG 上下文环境
id:上下文环境唯一标识,必须唯一
targetRuntime:生成的目标运行环境
MyBatis3:代表生成MyBatis3.x版本的代码
MyBatis3Simple:新版本的生成器
MyBatis3DynamicSql:新版本的生成器
defaultModelType:生成的默认Model类型
flat:代表生成的Model都是一个个的独立的类,例如生成Dept类(推荐)
hierarchical:代表生成的Model类会按照层级进行组织,例如生成Dept、DeptMapper、DeptExample三个类
-->
<context id="MySqlContext" targetRuntime="MyBatis3" defaultModelType="flat">
<!-- 配置SQL语句中的前置分隔符 -->
<property name="beginningDelimiter" value="`"/>
<!-- 配置SQL语句中的后置分隔符 -->
<property name="endingDelimiter" value="`"/>
<!-- 配置生成Java文件的编码 -->
<property name="javaFileEncoding" value="UTF-8"/>
<!--生成mapper.xml时覆盖原文件-->
<plugin type="org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin"/>
<!-- 为模型生成序列化方法-->
<plugin type="org.mybatis.generator.plugins.SerializablePlugin"/>
<!-- 为生成的Java模型创建一个toString方法 -->
<plugin type="org.mybatis.generator.plugins.ToStringPlugin"/>
<!--可以自定义生成model的代码注释
type:自定义注释生成器的类全限定名
-->
<commentGenerator type="cn.javgo.learningmybatis.mbg.CommentGenerator">
<!-- 是否阻止生成的注释 -->
<property name="suppressAllComments" value="true"/>
<!-- 是否阻止生成的注释包含时间戳 -->
<property name="suppressDate" value="true"/>
<!-- 是否添加数据库表的备注信息 -->
<property name="addRemarkComments" value="true"/>
</commentGenerator>
<!-- 配置数据库连接 -->
<jdbcConnection driverClass="${jdbc.driverClass}"
connectionURL="${jdbc.connectionURL}"
userId="${jdbc.username}"
password="${jdbc.password}">
<!-- 配置数据库连接的属性信息
autoReconnect:是否自动重连
allowMultiQueries:是否允许执行多条SQL语句
useUnicode:是否使用Unicode字符集
characterEncoding:设置字符集编码
useSSL:是否使用SSL
nullCatalogMeansCurrent:是否将null catalog看作当前catalog(解决 MySql 8.0 以上版本不生成指定数据库代码的问题)
-->
<property name="nullCatalogMeansCurrent" value="true"/>
</jdbcConnection>
<!-- 配置实体类的包名和位置
targetPackage:生成的实体类存放的包名
targetProject:生成的实体类存放的位置
-->
<javaModelGenerator targetPackage="cn.javgo.learningmybatis.mbg.model" targetProject="src/main/java">
<!-- 是否允许子包,即targetPackage.schemaName.tableName -->
<property name="enableSubPackages" value="true"/>
<!-- 是否对model添加构造函数 -->
<property name="constructorBased" value="true"/>
<!-- 是否对类CHAR类型的列的数据进行trim操作 -->
<property name="trimStrings" value="true"/>
</javaModelGenerator>
<!-- 配置SQL映射文件的包名和位置
targetPackage:生成的SQL映射文件存放的包名
targetProject:生成的SQL映射文件存放的位置
-->
<sqlMapGenerator targetPackage="cn.javgo.learningmybatis.mbg.mapper" targetProject="src/main/resources">
<!-- 是否允许子包,即targetPackage.schemaName.tableName -->
<property name="enableSubPackages" value="true"/>
</sqlMapGenerator>
<!-- 配置Mapper接口的包名和位置
type:选择怎么生成mapper接口(推荐使用XMLMAPPER)
XMLMAPPER:生成XML对应的Mapper接口
ANNOTATEDMAPPER:生成基于注解的Mapper接口
MIXEDMAPPER:生成XML对应的Mapper接口,同时也生成基于注解的Mapper接口
targetPackage:生成的Mapper接口存放的包名
targetProject:生成的Mapper接口存放的位置
-->
<javaClientGenerator type="XMLMAPPER" targetPackage="cn.javgo.learningmybatis.mbg.mapper"
targetProject="src/main/java">
<!-- 是否允许子包,即targetPackage.schemaName.tableName -->
<property name="enableSubPackages" value="true"/>
</javaClientGenerator>
<!-- 指定数据库表
tableName:数据库表名
domainObjectName:生成的实体类名(默认去掉下划线,驼峰命名)
-->
<table tableName="student">
<!-- 配置自增主键
column:指定获取自增主键的列
sqlStatement:指定获取自增主键的SQL语句
identity:指定是否为自增主键
-->
<generatedKey column="id" sqlStatement="MySql" identity="true"/>
</table>
</context>
</generatorConfiguration>
The comments in the above examples are already complete, so I won’t explain too much, just adjust them according to actual needs.
Notice:
MyBatis Generator will generate the required files according to the path specified in the configuration file at runtime. If the directory in the path already exists, MBG will directly create the file in it. If a directory in the path does not exist, MBG will try to create it.
However, there is one caveat: MBG will only try to create the last level of directories, not the entire directory path. For example, if the configuration
targetProject
issrc/main/java
insteadtargetPackage
ofcn.javgo.learningmybatis.mbg.model
, MBG willsrc/main/java
createcn/javgo/learningmybatis/mbg/model
the directory under the directory. The premise is thatsrc/main/java
this directory already exists. MBG will not be able to create the file if itsrc/main/java
does not exist.Therefore, usually we need to create the project path (
targetProject
) specified in the MBG configuration in advance , so that MBG can generate files correctly. As for the package path (targetPackage
), MBG will create it automatically, we don't need to create it manually.
If you want to customize the code generated by MBG, you can customize a CommentGenerator
to inherit from DefaultCommentGenerator
for personalized customization, which corresponds to commentGenerator
the tag section in the configuration file above.
For example, we can customize the generation of entity class code and add support for Swagger annotations to the entity class code:
package cn.javgo.learningmybatis.mbg;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.dom.java.CompilationUnit;
import org.mybatis.generator.api.dom.java.Field;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.internal.DefaultCommentGenerator;
import org.mybatis.generator.internal.util.StringUtility;
import java.util.Properties;
/**
* 自定义注释生成器
*/
public class CommentGenerator extends DefaultCommentGenerator {
// 是否添加数据库表的注释
private boolean addRemarkComments = false;
// Example 类名后缀
private static final String EXAMPLE_SUFFIX = "Example";
// Mapper 类名后缀
private static final String MAPPER_SUFFIX = "Mapper";
// ApiModelProperty 注解类的全限定名(Swagger)
private static final String API_MODEL_PROPERTY_FULL_CLASS_NAME = "io.swagger.annotations.ApiModelProperty";
/**
* 设置用户配置的参数
* @param properties 用户配置的参数
*/
@Override
public void addConfigurationProperties(Properties properties) {
// 调用父类方法保证父类方法可以正常使用
super.addConfigurationProperties(properties);
// 从 properties 中获取 addRemarkComments 参数值来判断是否添加数据库表的注释
this.addRemarkComments = Boolean.parseBoolean(properties.getProperty("addRemarkComments"));
}
/**
* 给字段添加注释
* @param field 字段
* @param introspectedTable 数据库表
* @param introspectedColumn 数据库表字段
*/
@Override
public void addFieldComment(Field field, IntrospectedTable introspectedTable,
IntrospectedColumn introspectedColumn) {
// 获取数据库表字段的注释
String remarks = introspectedColumn.getRemarks();
// 根据参数和备注信息判断是否添加备注信息
if (addRemarkComments && StringUtility.stringHasValue(remarks)) {
// 如果存在特殊字符,需要转义
if (remarks.contains("\"")) {
remarks = remarks.replace("\"", "'");
}
// 给 model 的字段添加 Swagger 注解
field.addJavaDocLine("@ApiModelProperty(value = \"" + remarks + "\")");
}
}
/**
* 给 Java 文件添加注释
* @param compilationUnit Java 文件
*/
@Override
public void addJavaFileComment(CompilationUnit compilationUnit) {
// 调用父类方法保证父类方法可以正常使用
super.addJavaFileComment(compilationUnit);
// 获取 Java 文件的全限定名
String fullyQualifiedName = compilationUnit.getType().getFullyQualifiedName();
// 如果不是 Mapper 类或者 Example 类,就添加 Swagger 注解类的导入(因为只有 model 类需要添加 Swagger 注解)
if (!fullyQualifiedName.contains(MAPPER_SUFFIX) && !fullyQualifiedName.contains(EXAMPLE_SUFFIX)) {
// 添加 Swagger 注解类的导入
compilationUnit.addImportedType(new FullyQualifiedJavaType(API_MODEL_PROPERTY_FULL_CLASS_NAME));
}
}
}
To use Swagger, you need to add corresponding dependencies to the pom file of the project:
<!-- Swagger -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>${springfox-swagger.version}</version>
</dependency>
Note that the above ${springfox-swagger.version}
version number needs to be selected according to the actual situation.
TIP:
For the use of the Swagger API document generation tool, please read the official document or the corresponding tutorial on the homepage, which will not be elaborated here.
2.5 Generate code
Once configured, you can run MBG via the command line, a Maven plugin, or directly using the Java API.
If we use the Java API, we need to write a Generator
class, and run main
the method directly after writing the following code:
package cn.javgo.learningmybatis.mbg;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
/**
* 通过 Java API 的方式运行 MyBatis Generator
*/
public class Generator {
public static void main(String[] args) throws Exception {
// 存储运行 MBG 时的警告信息
List<String> warnings = new ArrayList<>();
// 生成的代码重复时,覆盖原代码
boolean overwrite = true;
// 读取 MBG 配置文件 generatorConfig.xml
InputStream inputStream = Generator.class.getResourceAsStream("/generatorConfig.xml");
// 传入警告信息创建配置解析器(用于解析 generatorConfig.xml 配置文件)
ConfigurationParser cp = new ConfigurationParser(warnings);
// 解析配置文件
Configuration config = cp.parseConfiguration(inputStream);
// 关闭输入流
assert inputStream != null;
inputStream.close();
// 创建 DefaultShellCallback 对象,用于解决重复文件覆盖问题
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
// 创建 MyBatisGenerator 对象,执行生成代码
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
// 执行生成代码
myBatisGenerator.generate(null);
// 输出警告信息
for (String warning : warnings) {
System.out.println(warning);
}
}
}
If you use the Maven plugin, just add the following plugin to pom.xml:
<!-- MyBatis 生成器 -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.4.0</version>
<!-- 基本信息配置 -->
<configuration>
<!-- MyBatis Generator 配置文件 -->
<configurationFile>${basedir}/src/main/resources/generatorConfig.xml</configurationFile>
<!-- 允许移动生成的文件 -->
<overwrite>true</overwrite>
<!-- 是否自动覆盖 -->
<verbose>true</verbose>
</configuration>
</plugin>
Then run the following Maven command:
$ mvn mybatis-generator:generate
If it is through the command line, you can run the following command, the JAR package needs to be a specific version of the package, and provide a configuration file at the same time:
$ java -jar mybatis-generator-core-x.x.x.jar -configfile generatorConfig.xml
After running successfully, you will see the generated code structure information in the specified package:
You can view the interface generated by MBG StudentMapper
, and find that the basic CRUD method has been included, and the specific SQL implementation has also been generated in mapper.xml, and the single-table CRUD can directly call the corresponding method to meet daily needs. At the same time, you will also find a corresponding StudentExample
class in the generated code, which can be understood as a condition constructor, which is used to construct various conditions in SQL statements.
2.6 Basic CRUD operations
Based on the code generated above, our persistence layer task is completed, and then we only need to write the corresponding control layer and business logic layer to make reasonable calls. For example, the following is a corresponding StudentServiceImpl
business logic layer implementation class:
/**
* 学生业务实现类
*/
@Service
public class StudentServiceImpl implements StudentService {
@Autowired
private StudentMapper studentMapper;
@Override
public void save(Student student) {
studentMapper.insert(student);
}
@Override
public void update(Student student) {
studentMapper.updateByPrimaryKeySelective(student);
}
@Override
public void delete(Long id) {
studentMapper.deleteByPrimaryKey(id);
}
@Override
public Student select(Long id) {
return studentMapper.selectByPrimaryKey(id);
}
@Override
public List<Student> selectAll(int pageNum, int pageSize) {
PageHelper.startPage(pageNum, pageSize);
return studentMapper.selectByExample(new StudentExample());
}
}
3. Advanced use of MyBatis Generator
In addition to the above-mentioned basic single-table CRDU, we can also make good use of MyBatis Generator through methods such as constructing conditions, subqueries, grouping, joining, and using advanced mapping.
Before we start, let's introduce Example
the class. MyBatis Generator (MBG) generates a corresponding Example
class for each database table. Example
Classes are a very powerful tool, primarily for generating dynamic SQL statements.
Example
The class allows you to build complex WHERE
clauses without hardcoding SQL directly in the mapper file, and it contains many WHERE
methods for building clauses. Each method corresponds to a SQL comparison operator, such as " =
", " <>
", " >
", " <
", " LIKE
" and so on. You can dynamically add, modify or delete query conditions. Using Example
the class, you can build almost any kind of query, including queries with AND
and OR
clauses , queries with subqueries, etc.
When using Example
the class, you create a object, and then create a object Example
by calling the object's method . You can then call various methods on the object to add query criteria.createCriteria()
Criteria
Criteria
3.1 Condition-based CRUD
Conditional query, conditional modification, and conditional deletion can all Example
be implemented through the class, and each generated mapper has a corresponding Example
class. For example, for the table we tested above Student
, MBG generates a StudentExample
class.
We can StudentExample
query, modify or delete by creating an object and adding conditions.
For example, to query for users whose ID is greater than 10, you can do this:
@Override
public List<Student> selectByCondition() {
// 构建一个 Example 对象
StudentExample example = new StudentExample();
// 添加条件
example.createCriteria().andIdGreaterThan(10L);
// 执行查询
return studentMapper.selectByExample(example);
}
You can also create multiple Criteria
objects and OR
connect them to achieve more complex queries. For example, to query for users whose ID is greater than 10 or whose student name is "john", you can do this:
@Override
public List<Student> selectByCondition() {
// 构建一个 Example 对象
StudentExample example = new StudentExample();
// 添加条件
example.or().andIdGreaterThan(10L);
example.or().andNameEqualTo("john");
// 执行查询
return studentMapper.selectByExample(example);
}
In addition, Example
the class also supports sorting, and you can add sorting conditions by calling the method Example
of the object setOrderByClause
. For example, to query users by ID in ascending order, you can do this:
@Override
public List<Student> selectByCondition() {
// 构建一个 Example 对象
StudentExample example = new StudentExample();
// 添加条件(desc 降序, asc 升序)
example.setOrderByClause("id asc");
// 执行查询
return studentMapper.selectByExample(example);
}
For conditional modification and deletion, you can also use Example
the class. For example, to delete users whose IDs are greater than 10:
@Override
public void deleteByCondition() {
// 构建一个 Example 对象
StudentExample example = new StudentExample();
// 添加条件
example.createCriteria().andIdGreaterThan(10L);
// 执行删除
studentMapper.deleteByExample(example);
}
Visible, Example
class is a very useful tool, it can make your SQL query more dynamic and flexible.
TIP:
But also be aware that overuse of
Example
classes can make your code complex and difficult to understand. Therefore, for complex queries, sometimes writing SQL directly may be a better choice.
3.2 Subqueries, Group and Join queries
MBG is mainly used to generate code for simple single-table-based CRUD operations. For more complex SQL operations, such as subqueries, Group queries, and Join queries, MBG does not directly support them. These complex queries require you to write the corresponding SQL in Mapper's xml file or based on annotations.
3.3 One-to-one query, one-to-many query
One-to-one and one-to-many queries are also not directly supported in MBG. Generally speaking, both of these queries require you to implement custom SQL in the Mapper xml file. Of course, you can also choose to use the annotation-based approach. We have already explained this in the advanced query article, if you forget, go back and review the following.