SpringBoot2.4.x integrate mybatis plus3 4. X (detailed tutorial)

Differences and relations between Mybatis and MybatisPlus

Mybatis plus is an enhancement tool for mybatis. It is only enhanced on the basis of mybatis without change. Mybatis plus supports all the native features of mybatis. Therefore, the introduction of mybatis plus will not have any impact on the existing mybatis architecture. Mybatis plus (MP) is designed to simplify development and improve development efficiency. As the official website said,


Click here to enter the Official Learning Website

Get started quickly integrating the basics with SpringBoot

Import must depend on

  1. MybatisPlus integrates the scene launcher jar of SpringBoot
 <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.4.3</version>
  </dependency>
  1. Connect mysql driver jar
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

Note that when the MySQL connector Java jar package version is not specified here, SpringBoot will specify a version for us by default

Configure data sources

#---------------Database connection configuration--------------
# user name
spring.datasource.username=root
# password
spring.datasource.password=root
# Connection url
spring.datasource.url=jdbc:mysql://localhost:3306/school?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&autoReconnect=true
# Drive name
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

Simple CRUD

Write entity class and database mapping

The entity class student corresponds to the database table student

package cn.soboys.springbootmybatisplus.bean;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.Data;

/**
 * @author kenx
 * @version 1.0
 * @date 2021/6/25 10:08
 */

@TableName("student")
public class Student extends Model {
    @TableId(value = "student_id",type = IdType.AUTO)
    private Long studentId;
    @TableField("student_name")
    private String studentName;
    @TableField("age")
    private int age;
    @TableField("phone")
    private String phone;
    @TableField("addr")
    private String addr;


    public Long getStudentId() {
        return studentId;
    }

    public void setStudentId(Long studentId) {
        this.studentId = studentId;
    }

    public String getStudentName() {
        return studentName;
    }

    public void setStudentName(String studentName) {
        this.studentName = studentName;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getAddr() {
        return addr;
    }

    public void setAddr(String addr) {
        this.addr = addr;
    }
}

Write mapper(dao) to interact with database

The interface StudentMapper is implemented by mybatis agent

package cn.soboys.springbootmybatisplus.mapper;

import cn.soboys.springbootmybatisplus.bean.Student;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.mybatis.spring.annotation.MapperScan;

/**
 * @author kenx
 * @version 1.0
 * @date 2021/6/25 10:53
 */
@Mapper
public interface StudentMapper extends BaseMapper<Student> {


}

Write a service to implement specific business

  1. Interface IStudentService
package cn.soboys.springbootmybatisplus.service;

import cn.soboys.springbootmybatisplus.bean.Student;
import com.baomidou.mybatisplus.extension.service.IService;
import org.springframework.stereotype.Service;

/**
 * @author kenx
 * @version 1.0
 * @date 2021/6/25 10:59
 */

public interface IStudentService extends IService<Student> {
}

  1. Implementation class StudentServiceImpl
package cn.soboys.springbootmybatisplus.service.impl;

import cn.soboys.springbootmybatisplus.bean.Student;
import cn.soboys.springbootmybatisplus.mapper.StudentMapper;
import cn.soboys.springbootmybatisplus.service.IStudentService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

/**
 * @author kenx
 * @version 1.0
 * @date 2021/6/25 13:35
 */
@Service
public class StudentServiceImpl extends ServiceImpl<StudentMapper,Student> implements IStudentService {
}

Write controller main program

package cn.soboys.springbootmybatisplus.controller;

import cn.soboys.springbootmybatisplus.bean.Student;

import cn.soboys.springbootmybatisplus.service.IStudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * @author kenx
 * @version 1.0
 * @date 2021/6/25 11:09
 */
@RestController
@RequestMapping("/student")
public class StudentController {

    @Autowired
    private IStudentService studentService;

    /**
     * Add student
     *
     * @param student
     * @return
     */
    @PostMapping("/add")
    public boolean addStudent(@RequestBody Student student) {
        boolean flag = studentService.save(student);
        return flag;
    }

    /**
     * Update student information according to id
     *
     * @param student
     * @return
     */
    @PutMapping("/update")
    public boolean updateStudent(@RequestBody Student student) {
        //Update student based on student id
        boolean flag = studentService.updateById(student);
        return flag;
    }


    /**
     * Find all student information
     *
     * @return
     */
    @GetMapping("/list")
    public List<Student> list() {
        return studentService.list();
    }

    /**
     * Delete student information according to id
     *
     * @param studentId
     * @return
     */
    @DeleteMapping("/del/{studentId}")
    public boolean del(@PathVariable long studentId) {
        boolean flag = studentService.removeById(studentId);
        return flag;
    }

    /**
     * Get student information according to id
     * @param studentId
     * @return
     */
    @GetMapping("{studentId}")
    public Student getStudentInfo(@PathVariable long studentId){
        return studentService.getById(studentId);
    }


}

Add a student to the database


We see that the returned result is true, indicating that the addition is successful

Modify the student information just added according to the student id


We can see that true is returned after the modification is successful. Note that when modifying here, an additional studentId parameter is passed, that is, the corresponding student is found through the student id to modify.

Query all student information

Delete student information according to student id


We see that the return of true indicates that the deletion is successful

Get a student's information according to id

Here, the most basic crud functions of a single table can be used normally

Advanced use of SpringBoot integration

We can see that the most basic integration has been completed, and many places can be further optimized

Simplify entity bean s

We can see that the Student entity class in the above integration method contains many getters, setters and unnecessary mapping annotations, which can be appropriately simplified

  1. Import the jar package lombok. There is no version written here. We use the version specified by springboot by default
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

lombok can automatically generate getters and setters by annotating @ Data. We only need to add this annotation to the corresponding entity class to remove redundant getters and setters in the code.

Simplified entity bean and database mapping annotation

We can see that the Student entity class in the above integration method contains @ TableName annotation and many @ TableField annotations. In fact, it complies with the mapping rules between java entity class and database in MybatisPlus, which can be appropriately simplified. By default, MybatisPlus will convert the big hump naming method (Pascal naming method) to the underline naming method corresponding to the database

For example, the entity class name is OwnUser, which will give you the corresponding own in the database_ User table
The field StudentId will give you the student in the corresponding database table_ Field of ID

Therefore, the final entity class can be simplified into the following code

package cn.soboys.springbootmybatisplus.bean;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.Data;

/**
 * @author kenx
 * @version 1.0
 * @date 2021/6/25 10:08
 */

@TableName
@Data
public class Student extends Model {
    @TableId(type = IdType.AUTO)
    private Long studentId;
    private String studentName;
    private int age;
    private String phone;
    private String addr;
}

Simplify mapper scanning

The above integration method will add @ mapper annotation to each mapper interface class for scanning, which will cause redundancy. We can directly add @ MapperScan batch scanning mapper package on the SpringBoot startup class. Of course, we can also add @ MapperScan batch scanning mapper package on any other configuration class, However, it is generally added to the SpringBoot startup class (essentially, the SpringBoot startup class is also a configuration class),
This configuration is more centralized and meaningful, and there is no need to write an unintentional configuration class

package cn.soboys.springbootmybatisplus;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

@SpringBootApplication
@MapperScan({"cn.soboys.springbootmybatisplus.mapper"})
public class SpringbootMybatisplusApplication {

    private static ApplicationContext applicationContext;

    public static void main(String[] args) {
        applicationContext = SpringApplication.run(SpringbootMybatisplusApplication.class, args);
        //displayAllBeans();
    }

    /**
     * Print all loaded bean s
     */
    public static void displayAllBeans() {
        String[] allBeanNames = applicationContext.getBeanDefinitionNames();
        for (String beanName : allBeanNames) {
            System.out.println(beanName);
        }
    }
}

In this way, you can scan without adding @ Mapper annotation to each Mapper interface class separately

Database connection pool configuration

We only have simple database connection configuration above, but in real practical applications, we will use database connection pool to improve data connection efficiency and reduce unnecessary database resource overhead. The specific configuration is as follows

#---------------Database connection configuration--------------
#Data source type
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
# user name
spring.datasource.username=root
# password
spring.datasource.password=root
# Connection url
spring.datasource.url=jdbc:mysql://localhost:3306/school?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&autoReconnect=true
# Drive name
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#---------------Database connection pool HikariCP configuration--------------
#The minimum idle connection is 10 by default. If it is less than 0 or greater than maximum pool size, it will be reset to maximum pool size
spring.datasource.hikari.minimum-idle=10
#The maximum number of connections, less than or equal to 0, will be reset to the default value of 10; Greater than zero and less than 1 will be reset to the value of minimum idle
spring.datasource.hikari.maximum-pool-size=20
#Idle connection timeout. The default value is 600000 milliseconds (10 minutes),
# If it is greater than or equal to Max lifetime and Max lifetime > 0, it will be reset to 0; If it is not equal to 0 and less than 10 seconds, it will be reset to 10 seconds.
spring.datasource.hikari.idle-timeout=500000
#The maximum connection lifetime, which is not equal to 0 and less than 30 seconds, will be reset to the default value of 30 minutes The setting should be shorter than the timeout set by mysql
spring.datasource.hikari.max-lifetime=540000
#Connection timeout: ms, less than 250 ms, otherwise it will be reset to the default value of 30 seconds
spring.datasource.hikari.connection-timeout=60000
#A query statement used to test whether a connection is available
spring.datasource.hikari.connection-test-query=SELECT 1

MybatisPlus configuration

We can see that the above integration is only a simple crud, single table operation. Our service and mapper do not write any methods. They only inherit the general Mapper,BaseMapper, general service interface IService of MybatisPlus and realize ServiceImpl. They have the basic crud methods. However, when encountering multi table complex condition query, they need to write sql separately, In this case, mapper needs to be configured separately XML file

#--------------------mybatisPlus configuration------------------
#mapper.xml file location
mybatis-plus.mapper-locations=classpath:mapper/*.xml
#Alias package scanning path. This property allows you to register aliases for classes in the package
mybatis-plus.type-aliases-package=cn.soboys.springbootmybatisplus.bean
#Print mybatisPlus LOGO on the console
mybatis-plus.global-config.banner=true

MybatisPlus more detailed configuration

Paging plug-in usage

In MybatisPlus, we have also done relevant processing for paging. We need to do relevant configuration before we can use it normally

SpringBoot configuration class

package cn.soboys.springbootmybatisplus.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;

/**
 * @author kenx
 * @version 1.0
 * @date 2021/6/28 10:19
 */
@SpringBootConfiguration
public class MyBatisCfg {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

StudentMapper class

/**
     * Paging query of student information of each class
     * @param page Paging object, from which values can be obtained in xml. Pass the parameter Page, that is, automatic paging, which must be put in the first place (you can inherit Page to realize your own paging object)
     * @param gradeId Class id
     * @return Paging object
     */
    IPage<Student> findStudentPage(Page<?> page, long gradeId);

studentMapper.xml

<!-- Equivalent to writing a common list Query, mybatis-plus Automatically page for you-->
        <select id="findStudentPage" resultType="Student">
            select * from student ,grade g where g.grade_id=#{gradeId}
        </select>

IStudentService interface

/**
     * Paging query
     * @param page
     * @param gradeId Class id
     * @return
     */
    IPage<Student> getStudentPage(Page<Student> page,long gradeId);

StudentServiceImpl implementation class

@Override
    public IPage<Student> getStudentPage(Page<Student> page,long gradeId) {
        // Without count sql optimization, you can solve the problem that MP cannot automatically optimize SQL. At this time, you need to query the count part yourself
        // page.setOptimizeCountSql(false);
        // When the total is less than 0 or setSearchCount(false) is set, the paging plug-in will not query the count
        // main points!! The object returned by paging is the same as the object passed in

        return this.baseMapper.findStudentPage(page,gradeId);
    }

StudentController main program call

 /**
     * Page to get student details
     *
     * @return
     */
    @GetMapping("listDetailPage")
    public IPage<Student> getStudentDetailPage(PageRequest request) {
        Page<Student> page = new Page<>();
        //Set how many items are displayed on each page
        page.setSize(request.getPageSize());
        //Set page
        page.setCurrent(request.getPageNum());
        return studentService.getStudentPage(page, 1);
    }

Here, you need to transfer paging and other related information. You can encapsulate a paging query general object PageRequest

package cn.soboys.springbootmybatisplus;

import lombok.Data;

import java.io.Serializable;

/**
 * @author kenx
 * @version 1.0
 * @date 2021/6/28 10:41
 * Paging query
 */
@Data
public class PageRequest implements Serializable {
    private static final long serialVersionUID = -4869594085374385813L;

    /**
     * Current page data volume
     */
    private int pageSize = 10;

    /**
     * Current page number
     */
    private int pageNum = 1;

    /**
     * sort field
     */
    private String field;

    /**
     * Collation, asc ascending, desc descending
     */
    private String order;
}


We see that the normal paging query displays 2 items on page 1 per page


Page 2

Code generator

We know that Mybatis can generate basic entity mapping beans through configuration, which simplifies our development time. There is no need to write cumbersome mapping beans, including a pile of attributes. MybatisPlus also has its own code generator AutoGenerator. Through simple configuration, we can quickly generate a complete model, service and mapper. We don't need to write it ourselves and inherit the general mapper, The original words of the service official website are as follows

Add dependency

 <!--Generator dependency-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.4.1</version>
</dependency>
  <!--MyBatis-Plus From 3.0.3 After that, the default dependencies of the code generator and the template engine are removed, and the related dependencies need to be added manually:-->
<!--Add template engine dependencies, MyBatis-Plus support Velocity(Default)
Freemarker,Beetl,Users can choose their familiar template engine,
If it does not meet your requirements, you can use a custom template engine.-->
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.31</version>
</dependency>

Custom generator code

package cn.soboys.springbootmybatisplus;


import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

/**
 * @author kenx
 * @version 1.0
 * @date 2021/6/28 11:31
 * Automatic code generator
 */
public class MyBatisGeneratorCode {
    /**
     * <p>
     * Read console content
     * </p>
     */
    public static String scanner(String tip) {
        Scanner scanner = new Scanner(System.in);
        StringBuilder help = new StringBuilder();
        help.append("Please enter" + tip + ": ");
        System.out.println(help.toString());
        if (scanner.hasNext()) {
            String ipt = scanner.next();
            if (StringUtils.isNotBlank(ipt)) {
                return ipt;
            }
        }
        throw new MybatisPlusException("Please enter the correct" + tip + "!");
    }

    public static void main(String[] args) {
        // Code generator 
        AutoGenerator mpg = new AutoGenerator();

        // Global configuration
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir(projectPath + "/src/main/java");
        gc.setAuthor("kenx");
        gc.setOpen(false);
        // gc.setSwagger2(true);  Entity attribute Swagger2 annotation
        mpg.setGlobalConfig(gc);

        // Data source configuration
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/school?useUnicode=true&useSSL=false&characterEncoding=utf8");
        // dsc.setSchemaName("public");
        dsc.setDriverName("com.mysql.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("root");
        mpg.setDataSource(dsc);

        // Package configuration
        PackageConfig pc = new PackageConfig();
        pc.setModuleName(scanner("Module name"));
        pc.setParent("cn.soboys.springbootmybatisplus.generator");
        mpg.setPackageInfo(pc);

        // Custom configuration
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                // to do nothing
            }
        };

        // If the template engine is freemaker
        String templatePath = "/templates/mapper.xml.ftl";
        // If the template engine is velocity
        // String templatePath = "/templates/mapper.xml.vm";

        // Custom output configuration
        List<FileOutConfig> focList = new ArrayList<>();
        // Custom configuration will be output first
        focList.add(new FileOutConfig(templatePath) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // Customize the output file name. If you set the Prefix suffix for Entity, note that the name of xml will change!!
                return projectPath + "/src/main/resources/mapper/generator/" + pc.getModuleName()
                        + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
            }
        });
        /*
        cfg.setFileCreate(new IFileCreate() {
            @Override
            public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) {
                // Determine whether custom folders need to be created
                checkDir("The directory created by calling the default method, and the user-defined directory is marked with "");
                if (fileType == FileType.MAPPER) {
                    // The mapper file has been generated. It is judged that it exists. If you do not want to regenerate it, false is returned
                    return !new File(filePath).exists();
                }
                // Allow template files to be generated
                return true;
            }
        });
        */
        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);

        // Configuration template
        TemplateConfig templateConfig = new TemplateConfig();

        // Configure custom output templates
        //Specify the path of custom template. Be careful not to bring it ftl/.vm, which will be automatically identified according to the template engine used
        // templateConfig.setEntity("templates/entity2.java");
        // templateConfig.setService();
        // templateConfig.setController();

        templateConfig.setXml(null);
        mpg.setTemplate(templateConfig);

        // Policy configuration
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        //strategy.setSuperEntityClass("your own parent entity, no need to set it!");
        strategy.setEntityLombokModel(true);
        strategy.setRestControllerStyle(true);
        // Public parent class
        //strategy.setSuperControllerClass("your own parent controller, you don't need to set it if you don't have it!");
        // Public fields written in the parent class
        strategy.setSuperEntityColumns("id");
        strategy.setInclude(scanner("Table name, separated by multiple English commas").split(","));
        strategy.setControllerMappingHyphenStyle(true);
        strategy.setTablePrefix(pc.getModuleName() + "_");
        mpg.setStrategy(strategy);
        mpg.setTemplateEngine(new FreemarkerTemplateEngine());
        mpg.execute();
    }
}

Post run results


We see that the operation is successful and the directory structure we need has been generated normally


This template is a general template. You only need to change it a little. You need to generate databases, packages and directories

For more information, please refer to the official website

SpringBoot integrates high-level use

Debug and print sql

During development, we often need to debug the code and check whether the generated sql is correct. At this time, we need to print the sql on the console

Import dependency

<!-- https://mvnrepository.com/artifact/p6spy/p6spy -->
<dependency>
    <groupId>p6spy</groupId>
    <artifactId>p6spy</artifactId>
    <version>3.9.0</version>
</dependency>

Profile configuration

#Change the url to the connection url starting with p6spy
spring.datasource.url=jdbc:p6spy:mysql://localhost:3306/school?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&autoReconnect=true
# Drive name
# You need to debug the print sql driver to p6spy intercept
spring.datasource.driver-class-name=com.p6spy.engine.spy.P6SpyDriver

spy profile

#3.2.1 use of the above
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
#3.2.1 the following are used or not configured:
#modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory
# Custom log printing
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
#Log output to console
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
# Use the logging system to record sql
#appender=com.p6spy.engine.spy.appender.Slf4JLogger
# Set p6spy driver agent
deregisterdrivers=true
# Remove JDBC URL prefix
useprefix=true
# Log exceptions for configuration records. The result sets that can be removed include error,info,batch,debug,statement,commit,rollback,result,resultset
excludecategories=info,debug,result,commit,resultset
# Date format
dateformat=yyyy-MM-dd HH:mm:ss
# The actual drive can be multiple
#driverlist=org.h2.Driver
# Enable slow SQL logging
outagedetection=true
# Slow SQL record standard 2 seconds
outagedetectioninterval=2

We see that the running sql is successfully printed out through the simple configuration console

GitHub project source code

Keywords: Spring Boot MybatisPlus

Added by bpops on Sat, 22 Jan 2022 22:05:01 +0200