Table of contents
1. Introduction to MyBatis-Plus
3. Detailed explanation of MyBatis-Plus strategy
3.1 Primary key generation strategy
3.3 Field automatic filling strategy
4. Use of MyBatis-Plus plug-in
4.1.1 What are optimistic locking and pessimistic locking?
4.1.2 Optimistic lock implementation
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()
anddeleteById()
, 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
-
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.
-
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.
-
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.
-
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.
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:
- Define the data source configuration, specify the database URL, username and password.
- Create a FastAutoGenerator instance and specify the data source configuration.
- Configure global configuration, including author, output directory, comment date and date type, etc.
- Configuration package configuration, including parent package name, entity package name, service package name, service implementation package name, mapper package name, XML package name, etc.
- Configure injection configuration to record table information before outputting the file.
- Configure policy configuration, including table names to be generated, prefixes to be removed, entity class configuration, controller class configuration, etc.
- Specify the template engine as Freemarker.
- 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 IdentifierGenerator method nextId (the default implementation class is DefaultIdentifierGenerator Snowflake algorithm) |
ASSIGN_UUID | Assign UUID, primary key type is String (since 3.3.0), use interface IdentifierGenerator method nextUUID (default default method) |
Distributed globally unique ID long integer type (please use ASSIGN_ID ) |
|
32-bit UUID string (please use ASSIGN_UUID ) |
|
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:
- spring-boot example: portal
method | Primary key generation strategy | Primary key type | illustrate |
---|---|---|---|
nextId | ASSIGN_ID, |
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, |
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?
- 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.
- Recover data: If project data is accidentally deleted, it can be easily restored through tombstones. Physically deleted data cannot be recovered.
- 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.
- 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.
- 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@TableField
the 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.