MyBatis - MyBatis Generator

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.ymlPerform 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:

  1. <database>Replace , <username>and in the above data source configuration <password>with your specific connection information;
  2. 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/mapperdirectory, while the mapping file automatically generated by MyBatis Generator is placed resources/mbg/mapperin the directory.

2.3 Add Java configuration

@MapperScanIn 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.mapperunder the package, while the Mapper interface automatically generated by MyBatis Generator is placed cn.javgo.learningmybatis.mbg.mapperunder the package.

2.4 MBG configuration

resourcesPrepare a configuration file under the directory to generator.propertiesconfigure 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 resourcescreate 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 targetProjectis src/main/javainstead targetPackageof cn.javgo.learningmybatis.mbg.model, MBG will src/main/javacreate cn/javgo/learningmybatis/mbg/modelthe directory under the directory. The premise is that src/main/javathis directory already exists. MBG will not be able to create the file if it src/main/javadoes 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 CommentGeneratorto inherit from DefaultCommentGeneratorfor personalized customization, which corresponds to commentGeneratorthe 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 Generatorclass, and run mainthe 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 StudentExampleclass 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 StudentServiceImplbusiness 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 Examplethe class. MyBatis Generator (MBG) generates a corresponding Exampleclass for each database table. ExampleClasses are a very powerful tool, primarily for generating dynamic SQL statements.

ExampleThe class allows you to build complex WHEREclauses without hardcoding SQL directly in the mapper file, and it contains many WHEREmethods 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 Examplethe class, you can build almost any kind of query, including queries with ANDand ORclauses , queries with subqueries, etc.

When using Examplethe class, you create a object, and then create a object Exampleby calling the object's method . You can then call various methods on the object to add query criteria.createCriteria()CriteriaCriteria

3.1 Condition-based CRUD

Conditional query, conditional modification, and conditional deletion can all Examplebe implemented through the class, and each generated mapper has a corresponding Exampleclass. For example, for the table we tested above Student, MBG generates a StudentExampleclass.

We can StudentExamplequery, 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 Criteriaobjects and ORconnect 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, Examplethe class also supports sorting, and you can add sorting conditions by calling the method Exampleof 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 Examplethe 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, Exampleclass is a very useful tool, it can make your SQL query more dynamic and flexible.

TIP:

But also be aware that overuse of Exampleclasses 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.

Guess you like

Origin blog.csdn.net/ly1347889755/article/details/130995174