MyBatis - MyBatis Generator

手动编写 SQL 语句和映射实体类的过程常常是繁琐且易出错的。这时,我们就可以借助 MyBatis Generator (MBG) 这个强大的工具来自动化生成这部分代码。

1.什么是 MyBatis Generator

MyBatis Generator 是一款针对 MyBatis 或 iBATIS 设计的代码生成器,由 MyBatis 官方提供。它可以生成 MyBatis 的 Java 实体类、mapper.xml 文件以及对应的 Mapper 接口,极大地减少了开发人员手写 SQL 语句和映射实体类的工作量,提高了开发效率。

2.使用 MyBatis Generator

2.1 导入依赖

首先,我们需要在项目中添加 MyBatis Generator 的依赖:

<!-- MyBatis 生成器 -->
<dependency>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-core</artifactId>
    <version>${mybatis-generator.version}</version>
</dependency>

当然,为了完成的完成整套流程,我们还需要一些相关的其他依赖,如 MyBatis、PageHelper、Druid 和 MySQL 驱动:

<!-- 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>

注意:上面的 ${} 部分的版本号需要根据具体项目的实际情况进行调整。

2.2 application 配置

在 Spring Boot 的 application.yml 配置文件中进行一些基本的配置,主要包括数据源的配置、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

下面是对上面基本配置的简单说明:

  1. 将上述数据源配置中的 <database><username><password> 换为你具体的连接信息;
  2. 为了便于区分 MyBatis Generator 自动生成的 mapper.xml 和我们自己手写的 mapper.xml,我们约定自己写的映射文件放在 resources/mapper 目录下,而 MyBatis Generator 自动生成的映射文件放在 resources/mbg/mapper 目录下。

2.3 添加 Java 配置

为了让 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 包下。

扫描二维码关注公众号,回复: 15653777 查看本文章

2.4 MBG 配置

resources 目录下准备一个 generator.properties 配置文件,用于配置数据库连接的基本信息:

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><username><password> 换为你具体的连接信息。

然后,需要在 resources 目录下创建一个 MBG 的配置文件 generatorConfig.xml,配置好需要连接的数据库,以及指定生成的实体类、Mapper 接口和 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"/>

    <!-- 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>

上述示例中注释已经很完善了,不再过多解释,根据实际需要进行调整即可。

注意:

MyBatis Generator 在运行时会根据配置文件中指定的路径生成所需的文件。如果路径中的目录已经存在,MBG 会直接在其中创建文件。如果路径中的某个目录不存在,MBG 会尝试创建这个目录。

然而,有一点需要注意:MBG 只会尝试创建最后一级的目录,而不会创建整个目录路径。 例如,如果配置中的 targetProjectsrc/main/java,而 targetPackagecn.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 文件中添加对应的依赖:

<!-- Swagger -->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-boot-starter</artifactId>
    <version>${springfox-swagger.version}</version>
</dependency>

注意,上述 ${springfox-swagger.version} 版本号需要根据实际情况进行选择。

TIP:

关于 Swagger API 文档生成工具的使用请自行阅读官方文档或主页也有对应教程,此处不再详细展开。

2.5 生成代码

配置完成后,你可以通过命令行、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 中添加以下插件即可:

<!-- 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>

然后运行以下 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 语句中的各种条件。

2.6 基本 CRUD 操作

基于上述生成的代码,我们的持久层任务也就完成了,接下来只需要编写对应的控制层和业务逻辑层进行合理的调用即可。例如下面是一个对应的 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());
    }
}

3.进阶使用 MyBatis Generator

除了上述的基本单表 CRDU 外,我们还可以通过诸如构造条件、子查询、分组、连接和使用高级映射等方式来利用好 MyBatis Generator。

在开始之前,我们先介绍一下 Example 类。MyBatis Generator (MBG) 为每个数据库表生成了一个对应的 Example 类。Example 类是一个很强大的工具,它主要用于生成动态 SQL 语句。

Example 类允许你构建复杂的 WHERE 子句,而无需直接在 mapper 文件中硬编码 SQL,它包含了很多用于构建 WHERE 子句的方法。每个方法都对应一个 SQL 比较运算符,比如 “=”, “<>”, “>”, “<”, “LIKE” 等。你可以动态地添加、修改或删除查询条件。使用 Example 类,你可以构建几乎任何类型的查询,包括带有 ANDOR 子句的查询,带有子查询的查询等。

在使用 Example 类时,你需要创建一个 Example 对象,然后通过调用该对象的 createCriteria() 方法创建一个 Criteria 对象。然后你可以在 Criteria 对象上调用各种方法来添加查询条件。

3.1 基于条件的 CRUD

条件查询、条件修改和条件删除都可以通过 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 可能是一个更好的选择。

3.2 子查询、Group 与 Join 查询

MBG 主要用来生成简单的基于单表的 CRUD 操作的代码。对于更复杂的 SQL 操作,比如子查询、Group 查询和 Join 查询,MBG 并不直接支持。这些复杂查询需要你自己在 Mapper 的 xml 文件或基于注解编写相应的 SQL。

3.3 一对一查询、一对多查询

一对一和一对多查询在 MBG 中也并不直接支持。一般来说,这两种查询都需要你在 Mapper 的 xml 文件中自定义 SQL 来实现。当然,你也可以选择使用基于注解的方式。这在高级查询一文中我们已经进行过讲解,忘记的回去复习以下即可。

猜你喜欢

转载自blog.csdn.net/ly1347889755/article/details/130995174