手动编写 SQL 语句和映射实体类的过程常常是繁琐且易出错的。这时,我们就可以借助 MyBatis Generator (MBG) 这个强大的工具来自动化生成这部分代码。
MyBatis Generator 是一款针对 MyBatis 或 iBATIS 设计的代码生成器,由 MyBatis 官方提供。它可以生成 MyBatis 的 Java 实体类、mapper.xml 文件以及对应的 Mapper 接口,极大地减少了开发人员手写 SQL 语句和映射实体类的工作量,提高了开发效率。
首先,我们需要在项目中添加 MyBatis Generator 的依赖:
<dependency>
<groupId>org.mybatis.generatorgroupId>
<artifactId>mybatis-generator-coreartifactId>
<version>${mybatis-generator.version}version>
dependency>
当然,为了完成的完成整套流程,我们还需要一些相关的其他依赖,如 MyBatis、PageHelper、Druid 和 MySQL 驱动:
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>${mybatis-starter.version}version>
dependency>
<dependency>
<groupId>com.github.pagehelpergroupId>
<artifactId>pagehelper-spring-boot-starterartifactId>
<version>${pagehelper-starter.version}version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druid-spring-boot-starterartifactId>
<version>${druid.version}version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>${mysql-connector.version}version>
dependency>
注意:上面的
${}
部分的版本号需要根据具体项目的实际情况进行调整。
在 Spring Boot 的 application.yml
配置文件中进行一些基本的配置,主要包括数据源的配置、MyBatis 的相关配置:
# 配置数据源
spring:
datasource:
url: jdbc:mysql://localhost:3306/>?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
username: >
password: >
# Mybatis 相关配置
mybatis:
# 配置映射文件路径
mapper-locations:
- classpath:mapper/*.xml
- classpath:mbg/mapper/*/*.xml
下面是对上面基本配置的简单说明:
、
和
换为你具体的连接信息;resources/mapper
目录下,而 MyBatis Generator 自动生成的映射文件放在 resources/mbg/mapper
目录下。为了让 Spring Boot 能够自动扫描到生成的 Mapper 接口,我们需要准备一个配置类通过在 Java 配置类上添加 @MapperScan
注解来指定扫描路径。
/**
* MyBatis 配置类(扫描 Mapper 接口)
*/
@MapperScan({"cn.javgo.learningmybatis.mapper","cn.javgo.learningmybatis.mbg.mapper"})
@SpringBootConfiguration
public class MyBatisConfig {
}
注意,上面我们同样约定自己写的 Mapper 接口放在 cn.javgo.learningmybatis.mapper
包下,而 MyBatis Generator 自动生成的 Mapper 接口放在 cn.javgo.learningmybatis.mbg.mapper
包下。
在 resources
目录下准备一个 generator.properties
配置文件,用于配置数据库连接的基本信息:
jdbc.driverClass=com.mysql.cj.jdbc.Driver
jdbc.connectionURL=jdbc:mysql://localhost:3306/?useSSL=false&serverTimezone=UTC
jdbc.username=
jdbc.password=
注意:你需要将上述数据源配置中的
、
和
换为你具体的连接信息。
然后,需要在 resources
目录下创建一个 MBG 的配置文件 generatorConfig.xml
,配置好需要连接的数据库,以及指定生成的实体类、Mapper 接口和 XML 映射文件的位置。
配置文件的具体内容根据实际情况设置,以下是一个简单的例子:
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"/>
<context id="MySqlContext" targetRuntime="MyBatis3" defaultModelType="flat">
<property name="beginningDelimiter" value="`"/>
<property name="endingDelimiter" value="`"/>
<property name="javaFileEncoding" value="UTF-8"/>
<plugin type="org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin"/>
<plugin type="org.mybatis.generator.plugins.SerializablePlugin"/>
<plugin type="org.mybatis.generator.plugins.ToStringPlugin"/>
<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}">
<property name="nullCatalogMeansCurrent" value="true"/>
jdbcConnection>
<javaModelGenerator targetPackage="cn.javgo.learningmybatis.mbg.model" targetProject="src/main/java">
<property name="enableSubPackages" value="true"/>
<property name="constructorBased" value="true"/>
<property name="trimStrings" value="true"/>
javaModelGenerator>
<sqlMapGenerator targetPackage="cn.javgo.learningmybatis.mbg.mapper" targetProject="src/main/resources">
<property name="enableSubPackages" value="true"/>
sqlMapGenerator>
<javaClientGenerator type="XMLMAPPER" targetPackage="cn.javgo.learningmybatis.mbg.mapper"
targetProject="src/main/java">
<property name="enableSubPackages" value="true"/>
javaClientGenerator>
<table tableName="student">
<generatedKey column="id" sqlStatement="MySql" identity="true"/>
table>
context>
generatorConfiguration>
上述示例中注释已经很完善了,不再过多解释,根据实际需要进行调整即可。
注意:
MyBatis Generator 在运行时会根据配置文件中指定的路径生成所需的文件。如果路径中的目录已经存在,MBG 会直接在其中创建文件。如果路径中的某个目录不存在,MBG 会尝试创建这个目录。
然而,有一点需要注意:MBG 只会尝试创建最后一级的目录,而不会创建整个目录路径。 例如,如果配置中的
targetProject
是src/main/java
,而targetPackage
是cn.javgo.learningmybatis.mbg.model
,则 MBG 会在src/main/java
目录下创建cn/javgo/learningmybatis/mbg/model
目录。前提是src/main/java
这个目录已经存在。如果src/main/java
不存在,MBG 将无法创建文件。因此,通常我们需要提前创建好 MBG 配置中指定的项目路径(
targetProject
),这样 MBG 才能正确生成文件。而对于包路径(targetPackage
),MBG 会自动创建,我们无需手动创建。
如果你想自定义 MBG 生成的代码,可以自定义一个 CommentGenerator
来继承 DefaultCommentGenerator
进行个性化的定制,这对应了上面配置文件中的 commentGenerator
标签部分。
比如我们可以自定义实体类代码的生成,在实体类代码上添加 Swagger 注解的支持:
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));
}
}
}
使用 Swagger 需要在项目的 pom 文件中添加对应的依赖:
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-boot-starterartifactId>
<version>${springfox-swagger.version}version>
dependency>
注意,上述 ${springfox-swagger.version}
版本号需要根据实际情况进行选择。
TIP:
关于 Swagger API 文档生成工具的使用请自行阅读官方文档或主页也有对应教程,此处不再详细展开。
配置完成后,你可以通过命令行、Maven 插件或者直接使用 Java API 来运行 MBG。
如果是使用 Java API 的方式,我们需编写一个 Generator
类,完成如下代码编写后直接运行 main
方法即可:
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);
}
}
}
如果使用 Maven 插件,只需要在 pom.xml 中添加以下插件即可:
<plugin>
<groupId>org.mybatis.generatorgroupId>
<artifactId>mybatis-generator-maven-pluginartifactId>
<version>1.4.0version>
<configuration>
<configurationFile>${basedir}/src/main/resources/generatorConfig.xmlconfigurationFile>
<overwrite>trueoverwrite>
<verbose>trueverbose>
configuration>
plugin>
然后运行以下 Maven 命令即可:
$ mvn mybatis-generator:generate
如果是通过命令行的方式,可以运行如下命令,JAR 包需要为具体版本的包,同时提供配置文件:
$ java -jar mybatis-generator-core-x.x.x.jar -configfile generatorConfig.xml
运行成功后,你会在指定的包中看到生成的代码结构信息:
可以查看 MBG 生成的 StudentMapper
接口,发现已经包含了基本的 CRUD 方法,具体 SQL 实现也已经在 mapper.xml 中生成了,单表 CRUD 直接调用对应方法即可满足日常需求。同时,生成的代码中你还会发现有一个对应的 StudentExample
类,可以将其理解为一个条件构造器,用于构建 SQL 语句中的各种条件。
基于上述生成的代码,我们的持久层任务也就完成了,接下来只需要编写对应的控制层和业务逻辑层进行合理的调用即可。例如下面是一个对应的 StudentServiceImpl
业务逻辑层实现类:
/**
* 学生业务实现类
*/
@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());
}
}
除了上述的基本单表 CRDU 外,我们还可以通过诸如构造条件、子查询、分组、连接和使用高级映射等方式来利用好 MyBatis Generator。
在开始之前,我们先介绍一下 Example
类。MyBatis Generator (MBG) 为每个数据库表生成了一个对应的 Example
类。Example
类是一个很强大的工具,它主要用于生成动态 SQL 语句。
Example
类允许你构建复杂的 WHERE
子句,而无需直接在 mapper 文件中硬编码 SQL,它包含了很多用于构建 WHERE
子句的方法。每个方法都对应一个 SQL 比较运算符,比如 “=
”, “<>
”, “>
”, “<
”, “LIKE
” 等。你可以动态地添加、修改或删除查询条件。使用 Example
类,你可以构建几乎任何类型的查询,包括带有 AND
和 OR
子句的查询,带有子查询的查询等。
在使用 Example
类时,你需要创建一个 Example
对象,然后通过调用该对象的 createCriteria()
方法创建一个 Criteria
对象。然后你可以在 Criteria
对象上调用各种方法来添加查询条件。
条件查询、条件修改和条件删除都可以通过 Example
类来实现,每个生成的映射器都有一个相应的 Example
类。例如,对于我们上面测试的 Student
表,MBG 会生成一个 StudentExample
类。
我们可以通过创建一个 StudentExample
对象,并添加条件来进行查询,修改或删除。
例如,要查询 ID 大于 10 的用户,你可以这样做:
@Override
public List<Student> selectByCondition() {
// 构建一个 Example 对象
StudentExample example = new StudentExample();
// 添加条件
example.createCriteria().andIdGreaterThan(10L);
// 执行查询
return studentMapper.selectByExample(example);
}
你也可以创建多个 Criteria
对象,并通过 OR
连接它们,来实现更复杂的查询。例如,要查询 ID 大于 10 或学生姓名为 “john” 的用户,你可以这样做:
@Override
public List<Student> selectByCondition() {
// 构建一个 Example 对象
StudentExample example = new StudentExample();
// 添加条件
example.or().andIdGreaterThan(10L);
example.or().andNameEqualTo("john");
// 执行查询
return studentMapper.selectByExample(example);
}
此外,Example
类也支持排序,你可以通过调用 Example
对象的 setOrderByClause
方法来添加排序条件。例如,要按照 ID 升序查询用户,你可以这样做:
@Override
public List<Student> selectByCondition() {
// 构建一个 Example 对象
StudentExample example = new StudentExample();
// 添加条件(desc 降序, asc 升序)
example.setOrderByClause("id asc");
// 执行查询
return studentMapper.selectByExample(example);
}
对于条件修改和删除,你也可以使用 Example
类。例如,删除 ID 大于 10 的用户:
@Override
public void deleteByCondition() {
// 构建一个 Example 对象
StudentExample example = new StudentExample();
// 添加条件
example.createCriteria().andIdGreaterThan(10L);
// 执行删除
studentMapper.deleteByExample(example);
}
可见,Example
类是一个非常有用的工具,它可以让你的 SQL 查询更加动态和灵活。
TIP:
但是也需要注意,过度使用
Example
类可能会导致你的代码变得复杂和难以理解。因此,对于复杂的查询,有时直接编写 SQL 可能是一个更好的选择。
MBG 主要用来生成简单的基于单表的 CRUD 操作的代码。对于更复杂的 SQL 操作,比如子查询、Group 查询和 Join 查询,MBG 并不直接支持。这些复杂查询需要你自己在 Mapper 的 xml 文件或基于注解编写相应的 SQL。
一对一和一对多查询在 MBG 中也并不直接支持。一般来说,这两种查询都需要你在 Mapper 的 xml 文件中自定义 SQL 来实现。当然,你也可以选择使用基于注解的方式。这在高级查询一文中我们已经进行过讲解,忘记的回去复习以下即可。