【MyBatis-Plus】Advanced use of MyBatis

Table of contents

1. Introduction to MyBatis-Plus

1.1 Introduction

1.2 Advantages

1.3 Structure

2. Basic use of MyBatis-Plus

2.1 Configuration

2.2 Code generation

2.3 CRUD interface testing

3. Detailed explanation of MyBatis-Plus strategy

3.1 Primary key generation strategy

3.2 Snowflake ID Generator

3.3 Field automatic filling strategy

3.4 Logical deletion

4. Use of MyBatis-Plus plug-in

4.1 Optimistic lock plug-in

4.1.1 What are optimistic locking and pessimistic locking?

4.1.2 Optimistic lock implementation

4.2 Pagination plug-in 


1. Introduction to MyBatis-Plus

1.1 Introduction

MyBatis-Plus (opens new window) (referred to as MP) is an  enhancement tool for MyBatis (opens new window) . Based on MyBatis, it only enhances and does not make changes (that is to say, all mybatis and plus are available). In order to simplify development , born to improve efficiency.

1.2 Advantages

  • Code generator:  MyBatis-Plus provides a code generator that can automatically generate entity classes, mapper interfaces and XML mapping files based on database tables, greatly improving development efficiency.
  • CRUD operation simplification:  MyBatis-Plus provides a series of CRUD operation methods, such as  save(), saveOrUpdate(), updateById() and  deleteById(), which can simplify common database operations.
  • Paging query:  MyBatis-Plus provides support for paging query. You can use  PageHelper plug-ins or built-in paging functions to implement paging query.
  • Performance optimization:  MyBatis-Plus provides support for second-level cache and batch operations, which can improve the performance of database operations.
  • Code simplicity:  MyBatis-Plus provides many useful annotations that can simplify MyBatis code, such as  @TableId, @TableField and  @TableLogic.
  • Extensibility:  MyBatis-Plus provides many extension points, and you can extend the functionality of MyBatis-Plus through custom plug-ins or interceptors.

If you are using MyBatis, I highly recommend using MyBatis-Plus! !

1.3 Structure

  1. Core Module: The core module contains the core functions and basic components of Mybatis-Plus. It provides a common Mapper interface, the implementation of common CRUD methods, the implementation of core functions such as conditional constructors and paging plug-ins. The core module is the basis of the entire framework, and other modules depend on it.

  2. Code Generator: The code generator is an optional auxiliary tool that is used to automatically generate basic codes such as entity classes, Mapper interfaces, and XML mapping files based on the database table structure. Through the code generator, developers can quickly generate basic code and reduce the workload of hand-written repetitive code.

  3. Extension Module: Extension module is a collection of extension functions and enhanced components provided by Mybatis-Plus. It includes some commonly used enhancements, such as logical deletion, field autofill, optimistic locking, etc. Developers can easily add support for these features by introducing extension modules.

  4. Annotation: used to configure and mark entity classes, Mapper interfaces and SQL statements. By using annotations, you can simplify the writing of configuration files and improve the readability and maintainability of the code.  

Introduction | MyBatis-Plus MyBatis-Plus official document icon-default.png?t=N7T8https://baomidou.com/pages/24112f/#%E7%89%B9%E6%80%A7

2. Basic use of MyBatis-Plus

2.1 Configuration

1. Import pom dependencies

        <!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.2</version>
        </dependency>
        <!--自动生成模版引擎-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>
        <!--代码生成器-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.5.2</version>
        </dependency>

2. Configure database and mybatis-plus

server:
  port: 8080
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/bookshop

mybatis-plus:
  # Mybatis Mapper所对应的XML位置
  mapper-locations: classpath:mapper/*.xml
  # 别名包扫描路径
  type-aliases-package: com.ycxw.mybatis_test.model
  # 是否开启自动驼峰命名规则(camel case)映射
  configuration:
    map-underscore-to-camel-case: true
logging:
  level:
    com.ycxw.mybatis_test.mapper: debug

2.2 Code generation

1. Add code generator

package com.ycxw.mybatis_test.config;

import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import lombok.extern.slf4j.Slf4j;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

@Slf4j
public class MySQLGenerator {

    private final static String URL = "jdbc:mysql://localhost:3306/bookshop";
    private final static String USERNAME = "root";
    private final static String PASSWORD = "123456";

    private final static DataSourceConfig.Builder DATA_SOURCE_CONFIG =
            new DataSourceConfig.Builder(URL, USERNAME, PASSWORD);

    public static void main(String[] args) {
        FastAutoGenerator.create(DATA_SOURCE_CONFIG)
                .globalConfig(
                        (scanner, builder) ->
                                builder.author("云村小威")
                                        .outputDir(System.getProperty("user.dir") + "\\src\\main\\java")
                                        .commentDate("yyyy-MM-dd")
                                        .dateType(DateType.TIME_PACK)
                                        .disableOpenDir()
                )
                .packageConfig((builder) ->
                        builder.parent("com.ycxw.mybatis_test")
                                .entity("entity")
                                .service("service")
                                .serviceImpl("service.impl")
                                .mapper("mapper")
                                .xml("mapper.xml")
                                .pathInfo(Collections.singletonMap(OutputFile.xml, System.getProperty("user.dir") + "\\src\\main\\resources\\mapper"))
                )
                .injectionConfig((builder) ->
                        builder.beforeOutputFile(
                                (a, b) -> log.warn("tableInfo: " + a.getEntityName())
                        )
                )
                .strategyConfig((scanner, builder) ->
                        builder.addInclude(getTables(scanner.apply("请输入表名,多个英文逗号分隔?所有输入 all")))
                                .addTablePrefix("tb_", "t_", "lay_", "meeting_", "sys_")
                                .entityBuilder()
                                .enableChainModel()
                                .enableLombok()
                                .enableTableFieldAnnotation()
                                .controllerBuilder()
                                .enableRestStyle()
                                .enableHyphenStyle()
                                .build()
                )
                .templateEngine(new FreemarkerTemplateEngine())
                .execute();
    }

    protected static List<String> getTables(String tables) {
        return "all".equals(tables) ? Collections.emptyList() : Arrays.asList(tables.split(","));
    }

}

This Java code is used to automatically generate MyBatis-Plus code. It uses FastAutoGenerator to generate entity classes, service classes, service implementation classes, mapper interfaces, and mapper XML files.

The code mainly does the following things:

  1. Define the data source configuration, specify the database URL, username and password.
  2. Create a FastAutoGenerator instance and specify the data source configuration.
  3. Configure global configuration, including author, output directory, comment date and date type, etc.
  4. Configuration package configuration, including parent package name, entity package name, service package name, service implementation package name, mapper package name, XML package name, etc.
  5. Configure injection configuration to record table information before outputting the file.
  6. Configure policy configuration, including table names to be generated, prefixes to be removed, entity class configuration, controller class configuration, etc.
  7. Specify the template engine as Freemarker.
  8. Perform code generation.

When this code is run, it will generate MyBatis-Plus code and output it to the specified directory.

3. Enter the name of the data table that needs to be generated when running this class.

tips:

        Here you can find that except for the Book class under the entity package, all other classes and interfaces are empty. In fact, it comes with methods such as addition, deletion, modification, and query, which can be called directly. If you need a more complex data interface, you can write it yourself.

Finally, pay attention to adding mapper annotations to spring management, and adding annotations for scanning mappers in the starter:

2.3 CRUD interface testing

package com.ycxw.mybatis_test.controller;

import com.github.yitter.idgen.YitIdHelper;
import com.ycxw.mybatis_test.entity.Book;
import com.ycxw.mybatis_test.service.IBookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
 * <p>
 * 书本信息表 前端控制器
 * </p>
 *
 * @author 云村小威
 * @since 2023-12-16
 */
@RestController
@RequestMapping("/book")
public class BookController {
    @Autowired
    private IBookService bookService;

    @GetMapping("/list")
    public Object list() {
        return bookService.list();
    }

    @PostMapping("/save")
    public Object add(Book book) {
        /*设置雪花id*/
        book.setId(YitIdHelper.nextId());
        return bookService.save(book);
    }

    @PatchMapping("/update")
    public Object edit(Book book) {
        return bookService.updateById(book);
    }

    @DeleteMapping("/{id}")
    public Object del(@PathVariable Long id) {
        return bookService.removeById(id);
    }

}

Query test: 

etc. .

3. Detailed explanation of MyBatis-Plus strategy

3.1 Primary key generation strategy

@Getter
@Setter
@Accessors(chain = true)
@TableName("t_book")
public class Book implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 书本编号
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
  • @TableId annotation property

Attributes type Must be specified default value describe
value String no "" Primary key field name
type Enum no IdType.NONE Specify primary key type

IdType attribute:

value describe
AUTO Database ID auto-increment
NONE Stateless, this type has no primary key set (the annotation is equivalent to following the global, and the global Rio is approximately equal to INPUT)
INPUT Set the primary key value yourself before inserting
ASSIGN_ID Assign ID (primary key type is Number (Long and Integer) or String) (since 3.3.0), use the interface IdentifierGeneratormethod nextId(the default implementation class is DefaultIdentifierGeneratorSnowflake algorithm)
ASSIGN_UUID Assign UUID, primary key type is String (since 3.3.0), use interface IdentifierGeneratormethod nextUUID(default default method)
ID_WORKER Distributed globally unique ID long integer type (please use  ASSIGN_ID)
UUID 32-bit UUID string (please use  ASSIGN_UUID)
ID_WORKER_STR Distributed globally unique ID string type (please use  ASSIGN_ID)

3.2 Snowflake ID Generator

hint

Since 3.3.0, the snowflake algorithm + UUID (excluding the underscore) is used by default.

Custom sample project:

method Primary key generation strategy Primary key type illustrate
nextId ASSIGN_ID,ID_WORKERID_WORKER_STR Long,Integer,String Automatic conversion to String type is supported, but numerical types do not support automatic conversion and must be accurately matched. For example, if Long is returned, the entity primary key cannot be defined as Integer.
nextUUID ASSIGN_UUID,UUID String UUID generated without underscore by default

Example:

Why use an ID generator?

The main advantages of UUID are:

  • It is able to generate truly random identifiers without relying on a database or other external services.
  • It can be used across different systems and platforms without any modifications.

The main benefits of Snowflake ID are:

  • It is capable of generating incremental unique identifiers.
  • It can be generated distributedly and therefore scales to large systems.

        First of all, UUID is randomly generated, and the generated IDs may have a large span. In terms of performance, Snowflake ID is better than UUID. Snowflake IDs are faster to generate, smaller to store, and incremental. However, Snowflake ID relies on a database or other external service to store and manage metadata, and requires additional configuration and maintenance.

In actual applications, you can choose to use UUID or Snowflake ID, depending on the specific needs and scenarios.

3.3 Field automatic filling strategy

  • @TableField field annotation
    /**
     * 书本名称
     */
    @TableField(value = "bookname",fill = FieldFill.INSERT)
    private String bookname;
Attributes type Must be specified default value describe
value String no "" Database field name
exist boolean no true Whether it is a database table field
fill Enum no FieldFill.DEFAULT Field autofill strategy

Fill property value

value describe
DEFAULT Not processed by default
INSERT Populate fields on insert
UPDATE Populate fields on update
INSERT_UPDATE Populate fields on insert and update

Here we can set whether and when the field is automatically filled. Generally, setting automatic filling in the time field facilitates querying the insertion and update time of the data. Of course, the following is just a simple case:

We also need to write a custom implementation class MyMetaObjectHandler

package com.ycxw.mybatis_test.config;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("插入时自动填充 ....");
        this.strictInsertFill(metaObject, "bookname", String.class, "暂无"); // 起始版本 3.3.0(推荐使用)
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("修改时自动填充 ....");
        this.strictUpdateFill(metaObject, "updateTime", String.class, "未填写修改名称"); // 起始版本 3.3.0(推荐)
    }
}

With autofill defined, we don't need to assign a value to this field when adding or modifying it.

3.4 Logical deletion

Why use tombstones?

  1. Preserve history: With tombstones, the complete history of an item can be preserved without physically deleting the data. This is useful for debugging, auditing, and data analysis.
  2. Recover data: If project data is accidentally deleted, it can be easily restored through tombstones. Physically deleted data cannot be recovered.
  3. Performance Optimization: Logical deletion can improve the performance of your project because there is no need to physically delete the data. This is especially important for large projects or projects with large amounts of data.
  4. Security: Logical deletion can improve the security of the project because there is no need to physically delete the data, which can prevent unnecessary data leakage.
  5. Compliance: Some industries or regulations may require data to be retained for a specific period of time. Tombstone can help projects comply with these regulations without permanently storing the data.

        Through the logical deletion function extended by mybatis-plus, deletion will automatically be converted into modification when the deletion interface is called. Modify the data status to be unqueryable (provided the table contains a specific status column)

Instructions:

1. Global configuration

2. Local configuration

Annotations cannot be configured here @TableLogic, but @TableFieldthe field names in the corresponding database must be specified and set.

 

3. Test

For example, delete the first piece of data:

Look at the datasheet again:

Call query test:

You can find that the data just deleted will not be found, but the database still exists.

4. Use of MyBatis-Plus plug-in

4.1 Optimistic lock plug-in

4.1.1 What are optimistic locking and pessimistic locking?

        Optimistic locking and pessimistic locking are two different concurrency control mechanisms, used to prevent data inconsistency when multiple users modify the same data at the same time.

  • Optimistic locking assumes that data will not be modified by other users during concurrent operations, so there is no need to lock the data before performing update operations. When submitting an update, a check is made to see if the data has been modified. If the data has been modified, an exception will be thrown, requiring the user to re-fetch the data and resubmit the update.
  • Pessimistic locking assumes that during concurrent operations, data may be modified by other users, so the data needs to be locked before performing update operations. The lock will not be released until the update operation is completed. This ensures that when the data is updated, it will not be modified by other users.

Advantages of optimistic locking:

  • Performance is good because no locking is required in most cases.
  • Scalability is good because there is no need to maintain lock state.
  • No deadlocks will occur.

Disadvantages of optimistic locking:

  • Data inconsistencies may arise because the data may have been modified by other users when the update is submitted.
  • Application code is required to handle concurrency conflicts.

Advantages of pessimistic locking:

  • Data consistency is guaranteed because other users cannot modify the data while it is being updated.
  • No application code is required to handle concurrency conflicts.

Disadvantages of pessimistic locking:

  • Poor performance because locks are required when updating data.
  • Poor scalability because lock state needs to be maintained.
  • Deadlock may occur.

4.1.2 Optimistic lock implementation

When a record is to be updated, it is hoped that the record has not been updated by others.
Optimistic lock implementation:

  • When fetching records, get the current version
  • When updating, bring this version with you
  • When performing an update, set version = newVersion where version = oldVersion
  • If the version is incorrect, the update will fail.

 

So first add the version column to the table that needs to be locked, and add the @Version annotation to the entity class.

@Version
private Integer version;

1. Configure plug-in

package com.ycxw.mybatis_test.config;

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

// Spring Boot 方式
@Configuration
public class MybatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return mybatisPlusInterceptor;
    }
}

2. Write and modify interface tests

    @PutMapping("/edit")
    public Object update(){
        Book b1 = bookService.getById("16");
        b1.setPrice(133F);
        bookService.updateById(b1);
        return "1";
    }

You can see by calling the query interface: first query the data, then modify the version value and determine whether the version value of the data is equal to the current version value. 

        You can see that the version value has become 2, and it will increase by 1 if it is not modified once. This means that when I modify the data, the version changes to 2, and when others modify the data at the same time, the first query is 1, and I have modified it to 2. When others modify the data, the condition is no longer true and cannot be modified.

The following cases are verified:

    @PutMapping("/edit")
    public Object update(){
        /*模仿两人同时修改不同数据时*/
        Book b1 = bookService.getById("16");
        b1.setPrice(133F);
        Book b2 = bookService.getById("16");
        b2.setPrice(210F);

        bookService.updateById(b1);
        bookService.updateById(b2);
        return "1";
    }

        You can see that only the first modification was successful, and the version changed to 3 (the first query value was 2, and it automatically changed to 3 after the modification). If others modify it at the same time and the version judgment is not established, the modification cannot be made. 

4.2 Pagination plug-in 

1. First add the paging plug-in configuration. Like the optimistic lock configuration position, this class can configure multiple plug-ins at the same time.

package com.ycxw.mybatis_test.config;

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

// Spring Boot 方式
@Configuration
public class MybatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        /*配置乐观锁*/
        mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        /*配置分页*/
        //interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); 如果有多数据源可以不配具体类型 否则都建议配上具体的DbType
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));//如果配置多个插件,切记分页最后添加

        return mybatisPlusInterceptor;
    }
}

 2. Write query interface tests

    @GetMapping("/list")
    public Object list() {
        //简单的分页查询
        Page<Book> page = new Page<>(1,5);
        Page<Book> res = bookService.page(page, wrapper);
        res.getTotal();//数据总数
        res.getPages();//数据总页数
        return res.getRecords();
    }

 

The above is just a simple query case. MP implements 简单分页模型。需要进行条件查询则要了解the query constructor through the page class:

        QueryWrapper is a conditional constructor provided by Mybatis-Plus, which is used to quickly construct the conditional part of SQL query statements.

        The syntax of QueryWrapper is similar to the where tag in Mybatis's XML file, which will eventually be converted into the conditional part of the SQL statement. We can construct complex query conditions by continuously adding query conditions through chain calls.

Code example:

    @GetMapping("/list")
    public Object list(@RequestParam(value = "bookname", required = false, defaultValue = "")  String bookname) {
        //查询条件
        QueryWrapper<Book> wrapper = new QueryWrapper<>();
        /**
         * 设置查询条件 bookname like %name%
         * 如:
         *    warpper.eq("price",100,300) == price 在 100 ~ 300 之间
         *    ...
         */
        wrapper.like("bookname",bookname);

        //分页条件条件
        Page<Book> page = new Page<>(1,5);
        Page<Book> res = bookService.page(page, wrapper);
        res.getTotal();//数据总数
        res.getPages();//数据总页数
        return res.getRecords();
    }

 You can see that the column names that need to be queried are specified through the like method, and the column names are customized and spliced ​​into sql.

Guess you like

Origin blog.csdn.net/Justw320/article/details/135029138