Mybatis plus learning notes

Self use notes, combined with crazy God talk videos and official documents, were written in May 2021. Mybatis plus is updated quickly and may fall behind in the future. Only share for reference

brief introduction

concept

MyBatis plus is an enhancement tool of MyBatis. Based on MyBatis, it only makes enhancement without change, and is born to simplify development and improve efficiency.

Premise:

  • Have Java development environment and corresponding IDE
  • Familiar with Spring Boot
  • Familiar with Maven
  • Familiar with SQL

characteristic

  • No invasion: it is only enhanced without change, and its introduction will not affect the existing project, which is as smooth as silk
  • Low loss: the basic CURD will be injected automatically upon startup, with basically no loss of performance and direct object-oriented operation
  • Powerful crud operation: built-in general Mapper and general Service. Most CRUD operations of a single table can be realized only through a small number of configurations. It also has a powerful condition constructor to meet various use needs.
  • Support Lambda form call: through Lambda expression, it is convenient to write various query conditions without worrying about wrong fields
  • Support automatic generation of primary key: support up to 4 primary key strategies (including distributed unique ID generator - Sequence), which can be configured freely to perfectly solve the problem of primary key
  • Support ActiveRecord mode: support ActiveRecord formal calls. Entity classes only need to inherit Model classes to perform powerful CRUD operations
  • Support custom global general operations: support global general method injection (Write once, use anywhere)
  • Built in code generator: code or Maven plug-in can be used to quickly generate Mapper, Model, Service and Controller layer code, support template engine, and have more custom configurations for you to use
  • Built in paging plug-in: Based on MyBatis physical paging, developers do not need to care about specific operations. After configuring the plug-in, writing paging is equivalent to ordinary List query
  • The paging plug-in supports multiple databases: MySQL, MariaDB, Oracle, DB2, H2, HSQL, SQLite, Postgre, SQLServer and other databases
  • Built in performance analysis plug-in: it can output Sql statements and their execution time. It is recommended to enable this function during development and testing to quickly find out slow queries
  • Built in global interception plug-in: it provides intelligent analysis and blocking of full table delete and update operations, and can also customize interception rules to prevent misoperation

Frame structure

quick get start

Address: Quick start | mybatis plus (Baidu. Com)

Using third-party components:

  1. Import corresponding dependencies
  2. Study how dependencies are configured
  3. How to write the code
  4. Improve and expand technical ability!

Build database

There is a User table. Its table structure is as follows:

idnameageemail
1Jone18test1@baomidou.com
2Jack20test2@baomidou.com
3Tom28test3@baomidou.com
4Sandy21test4@baomidou.com
5Billie24test5@baomidou.com

The corresponding database Schema script is as follows:

DROP TABLE IF EXISTS user;

CREATE TABLE user
(
	id BIGINT(20) NOT NULL COMMENT 'Primary key ID',
	name VARCHAR(30) NULL DEFAULT NULL COMMENT 'full name',
	age INT(11) NULL DEFAULT NULL COMMENT 'Age',
	email VARCHAR(50) NULL DEFAULT NULL COMMENT 'mailbox',
	PRIMARY KEY (id)
);
-- Fields needed in real development (Alibaba development manual):
--version(Le Guansuo) deleted(Logical deletion) gmt_create(Creation time) gmt_modified((update time)

The corresponding database Data script is as follows:

DELETE FROM user;

INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),
(5, 'Billie', 24, 'test5@baomidou.com');

Initialization project

  • Create an empty SpringBoot

Add dependency

Introduce Spring Boot Starter parent project:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.5.0</version>
    <relativePath/>
</parent>

Introduce spring boot starter, spring boot starter test, mybatis plus boot starter and h2 dependencies:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    
    <!--mybatis—plus It was developed by Chinese people mybatis Enhanced version, not official-->
    <dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.4.3</version>
    <classifier>sources</classifier>
    <type>java-source</type>
</dependency>
    
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>runtime</scope>
    </dependency>
    
     <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
</dependencies>

Note: using mybatis plus can save a lot of code. Try not to import mybatis and mybatis plus at the same time

to configure

Connect database

properties version:

spring.datasource.username=root
spring.datasource.password=dm123456
spring.datasource.url=jdbc:mysql://localhost:3306 / database name? useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=true
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

yaml version:

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: dm123456
    url: jdbc:mysql://localhost:3306 / database name? useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=true

Add the @ MapperScan annotation in the Spring Boot startup class and scan the Mapper folder:

@SpringBootApplication
@MapperScan("com.baomidou.mybatisplus.samples.quickstart.mapper")
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(QuickStartApplication.class, args);
    }
}
  • Mybatis process: create POJO Dao (connect mybatis and configure mapper.xml file) - service controller
  • Mybatis plus process: create pojo - implement mapper interface - use

code

  • pojo package User class
//Simplify the set/get method with lombok plug-in
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}
  • Mapper package UserMapper interface
@Mapper
public interface UserMapper extends BaseMapper<User> {
    /**
     * All CRUD operations have been written
     */
}

  • Test class
@SpringBootTest
class MybatisApplicationTests {
    /**
     * Inheriting BaseMapper, all methods come from the parent class. We can also write our own extension methods
     */
    @Autowired
    private UserMapper userMapper;

    @Test
    void contextLoads() {
        List<User> users = userMapper.selectList(null);
        users.forEach(System.out::println);
    }
}

Configuration log

All sql is now invisible. If you want to know how to execute it, you need to open the log.

# Database connection configuration - yaml syntax
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: dm123456
    url: jdbc:mysql://localhost:3306 / database name? useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=true
# Configuration log
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    # Whether to enable automatic hump naming rule mapping, that is, the underline naming in the database column name is converted into the hump naming of attribute value
    map-underscore-to-camel-case: false

Log obtained:

CRUD extension

Insert insert

@Test
//Test insertion
public void testInsert(){
    User user = new User();
    user.setName("Chongming bird");
    user.setAge(20);
    user.setEmail("2428028346@qq.com");
    int result = userMapper.insert(user);
    System.out.println(result); //Number of rows affected
    System.out.println(user); 
}

The id is not set, but it is found that the id is automatically generated

The default value of the ID inserted in the database here is the globally unique ID generated by the snowflake algorithm, ASSIGN_ID

Primary key policy

@TableId(type = IdType.XXX)

public enum IdType {
    AUTO(0), 
    NONE(1), 
    INPUT(2),
    ASSIGN_ID(3),
    ASSIGN_UUID(4);
    ...
}
valuedescribe
AUTOSelf increment of database ID
NONEStateless. This type has no primary key set (in the annotation, it is equal to follow the global, and the global value is equal to INPUT)
INPUTset the primary key value before insert ing
ASSIGN_IDAssign ID (the primary key type is Number(Long and Integer) or String)(since 3.3.0), and use the method NextID of the interface IdentifierGenerator (the default implementation class is DefaultIdentifierGenerator)
ASSIGN_UUIDAllocate UUID. The primary key type is String(since 3.3.0). Use the method nextuuid (default method) of the interface IdentifierGenerator
ID_WORKER
(abandoned)
Distributed globally unique ID long integer type (please use ASSIGN_ID)
UUID
(abandoned)
32-bit UUID string (please use ASSIGN_UUID)
ID_WORKER_STR
(abandoned)
Distributed globally unique ID string type (please use ASSIGN_ID)

Note that when the field is id, the default value of @ TableId is idtype ASSIGN_ id, idtype in the rest of the time NONE

update - update

//Test update
@Test
public void testUpdate() {
	User user = new User();
    //Automatically splice sql through conditions
	user.setId(5L);
	user.setName("Chongming bird");
    //Although the name is updateByID, the parameter is an object
	int i = userMapper.updateById(user);
	System.out.println(i);
}

Auto fill

Creation time and modification time! These operations are generally automated, and we don't want to update them manually.

Alibaba Development Manual: gmt_create,gmt_modified, almost all tables need to have and require automation.

Method 1: database level

  1. Add a new field in the table: gmt_create,gmt_modified and set the default value (CURRENT_TIMESTAMP, CURRENT_TIMESTAMP + updated according to the current timestamp)
  2. Attribute should also be added to entity class

Database modification is not allowed in the project

Method 2: code level

  1. Delete default values and update operations in the database
  2. Annotation needs to be added on the field attribute of entity class
// Add padding to field
@TableField(fill = FieldFill.INSERT)
private Date gmtCreate;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date gmtModified;
  1. Write processor (under the handler package)
@Slf4j
// Be sure to add the processor to the IOC container!
@Component 
public class MyMetaObjectHandler implements MetaObjectHandler {
    // Population policy at insertion
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("start insert fill...");
        this.setFieldValByName("gmtCreate", new Date(), metaObject);
        this.setFieldValByName("gmtModified", new Date(), metaObject);
    }
    // Population policy when updating
    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("start update fill...");
        this.setFieldValByName("gmtModified", new Date(), metaObject);
    }
}

Optimistic lock

  • Pessimistic lock:

    When you want to modify a piece of data in the database, in order to avoid being modified by others at the same time, the best way is to lock the data directly to prevent concurrency.

    With the help of the database locking mechanism, this method of locking before modifying data is called Pessimistic Concurrency Control (abbreviated as "PCC", also known as "pessimistic lock").

  • Optimistic lock:

    Optimistic lock is relative to pessimistic lock. Optimistic lock assumes that the data generally will not cause conflict, so it will formally detect whether the data conflict or not when the data is submitted and updated. If there is a conflict, it will return the exception information to the user to let the user decide what to do. Optimistic locking is suitable for scenarios with more reads and less writes, which can improve the throughput of the program.

When we want to update a record, we hope that the record has not been updated by others. Optimistic lock implementation method:

  • When the record is fetched, the current version is obtained
  • When updating, bring this version
  • When updating, set version = newVersion where version = oldVersion
  • If the version is incorrect, the update fails

give an example:

-- Optimistic lock:
-- Query first to obtain the version number vetsion = 1 
-- A
update user set name = "dongmeng", version = version + 1 
where id = 2 and version = 1

-- B If B The thread finishes first. At this time version = 2,Will lead to A Modification failed
update user set name = "dongmeng", version = version + 1 
where id = 2 and version = 1

Optimistic lock configuration steps:

1. Add the optimistic lock field version to the data table. The default value is 1 (version number is 1)

2. Synchronous entity class

//Optimistic lock Version annotation
@Version
private Integer version;

3. Register components

//You can choose to scan our mapper folder here
@MapperScan("com.dm.mapper")
//Enable transaction management. It is enabled by default
@EnableTransactionManagement
//Configuration class
@Configuration
public class MyBatisPlusConfig {
    //Register optimistic lock plug-in
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }
}

4. Testing

  • Successful cases
@Test
public void testOptimisticLocker1() {
	// 1. Query user information
	User user = userMapper.selectById(1L);
	// 2. Modify user information
	user.setName("Ionia");
	user.setAge(33);
	user.setEmail("564651612@163.com");
	// 3. Perform update operation
	userMapper.updateById(user);
}

version auto + 1

  • Failure cases
// Test optimistic lock success stories!
@Test
public void testOptimisticLocker2() {
	// Thread 1
	User user1 = userMapper.selectById(1L);
	user1.setName("Ionia");
	user1.setAge(33);
	user1.setEmail("564651612@163.com");

	// Simulate another thread to perform queue jumping
	User user2 = userMapper.selectById(1L);
	user2.setName("Giant God peak");
	user2.setAge(26);
	user2.setEmail("9999912@163.com");
	userMapper.updateById(user2);
	//If there is no optimistic lock, the following operation will override the operation of queue jumping thread
	userMapper.updateById(user1);
}

select query

Single query

// Test query
@Test
public void testSelectById() {
    User user = userMapper.selectById(1L);
    System.out.println(user.toString());
}

Batch query

// Test batch query
@Test
public void testSelectById() {
    List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
    users.forEach(System.out::println);
}

Condition query

  • map operation
// Multi condition query
@Test
public void testSelectById() {
    HashMap<String, Object> map = new HashMap<>();
   // Multiple conditions can be put in
    map.put("name", "Chongming bird");
    map.put("age", 3);
    List<User> users = userMapper.selectByMap(map);
    users.forEach(System.out::println);
}

Paging query

Pagination is used as much as ten times in the website!

  1. Paging the original limit
  2. pageHelper third party plug-in
  3. Mybatis plus also has a built-in paging plug-in

usage method:

1. Configure interceptor components

/**
 * The new paging plug-in follows the rules of mybatis, and MybatisConfiguration#useDeprecatedExecutor = false needs to be set to avoid cache problems (this attribute will be removed after the old plug-in is removed)
 */
@Bean
public MybatisPlusInterceptor paginationInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
    return interceptor;
}

2. Direct use

// Test paging query
@Test
public void testPage() {
    // Parameter 1: current page
    // Parameter 2: page size
    Page<User> page = new Page<>(2,5);
    userMapper.selectPage(page, null);
    page.getRecords().forEach(System.out::println);
}

Deleted - deleted

Single deletion

// Test delete
@Test
public void testDeleteById() {
    userMapper.deleteById(7L);
}

Batch delete

// Test batch deletion
@Test
public void testDeleteById() {
    userMapper.deleteBatchIds(Arrays.asList(1L, 2L, 3L));
}

Condition deletion

// Test delete
@Test
public void testDeleteById() {
    HashMap<String, Object> hashMap = new HashMap<>();
    hashMap.put("name", "Demacia");
    hashMap.put("age", 25);
    userMapper.deleteByMap(hashMap);
}

Logical deletion

  • Physical delete: remove directly from the database
  • Logical deletion: it is not removed from the database, but invalidated by a variable! deleted = 0 => deleted = 1
  • Allows administrators to view deleted records directly! Prevent data loss. Similar to recycle bin!

explain:

Only works for automatically injected sql:

  • Insert: no restrictions
  • Find: append the where condition to filter out the deleted data, and use wrapper The where condition generated by entity ignores this field
  • Update: append a where condition to prevent updating to deleted data, and use wrapper The where condition generated by entity ignores this field
  • Delete: convert to update

For example:

  • Delete: update user set deleted=1 where id = 1 and deleted=0
  • Search: select id,name,deleted from user where deleted=0

Field type support Description:

  • Support all data types (integer, Boolean, localdatetime are recommended)
  • If the database field uses datetime, the logical undeleted value and deleted value can be configured as string null, and the other value can be configured as a function to obtain the value, such as now()

Appendix:

  • Logical deletion is a scheme to facilitate data recovery and protect the value of data itself, but it is actually deletion.
  • If you need to check it frequently, you should not use logical deletion, but represent it in a state.

Test:

1. Add a in the data table deleted field
2. modify pojo Class - the latest version needs to be annotated
//Logical deletion annotation
@TableLogic
private int deleted;
  1. test
@Test
public void testDelete() {
	userMapper.deleteById(5L);
}

It is essentially an update operation

The record is still in the database, but the value has changed

But now we can't find this record

Conditional constructor

Official documents:

Conditional constructor | mybatis plus (Baidu. Com)

Test:

@Test
void contextLoads() {
    // Query users whose name is not empty, whose email address is not empty, and whose age is greater than or equal to 12 years old
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper
            .isNotNull("name")
            .isNotNull("email")
            .ge("age",12);
    //Query a structure with selectOne
    userMapper.selectList(wrapper).forEach(System.out::println);
}

Complete collection of methods (see official documents for details):

Code generator

AutoGenerator is the code generator of mybatis plus. Through AutoGenerator, you can quickly generate the code of Entity, Mapper, Mapper XML, Service, Controller and other modules, which greatly improves the development efficiency

Add dependency

  • Add code generator dependency
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.4.1</version>
</dependency>
  • Add template engine dependency
<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity-engine-core</artifactId>
    <version>2.3</version>
</dependency>

Write configuration

Build code generator

package com.dm.demo;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;

/**
 * @ClassName: DongCode
 * @Description: Automatic code generator
 * @Author: ChongMing
 * @Date: 2021 16:15, June 19
 * @Version: 1.0
 */
public class DongCode {
    public static void main(String[] args) {
        // First, you need to build a code generator object
        AutoGenerator mpg = new AutoGenerator();
        
        // The configuration is written in the middle
      
        //Perform operations
        mpg.execute(); 
    }
}

Global configuration

// Global configuration (package under generation)
GlobalConfig gc = new GlobalConfig();
// Get current project path
String projectPath = System.getProperty("user.dir");
// The location of the output to the current project directory
gc.setOutputDir(projectPath+"/src/main/java");
// Set author information
gc.setAuthor("ChongMing");
// Open Explorer
gc.setOpen(false);
// Whether to overwrite the original
gc.setFileOverride(false);
// Set primary key type
gc.setIdType(IdType.ASSIGN_ID);
// Set swagger document
gc.setSwagger2(true);
// Throw the configuration into the constructor
mpg.setGlobalConfig(gc);

Data source configuration

// 2. Set data source
// The data source is the user name and connection password
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/mp?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=true");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword(null);
// Database type
dsc.setDbType(DbType.MYSQL);
// Throw the configuration into the constructor
mpg.setDataSource(dsc);

Package configuration

// 3. Configuration package
PackageConfig pc = new PackageConfig();
// Setup package
pc.setParent("com.dm");
// Set the module, and the code will eventually be put on COM dm. Under blog
pc.setModuleName("blog");
// Set each package name
pc.setEntity("pojo");
pc.setMapper("mapper");
pc.setService("service");
pc.setController("controller");
// Throw the configuration into the constructor
mpg.setPackageInfo(pc);

Policy configuration

// 4. Policy configuration
StrategyConfig sc = new StrategyConfig();
// Set the table name to be mapped, that is, the table to be read. You can set multiple tables
sc.setInclude("user");
// Package name underline to hump
sc.setNaming(NamingStrategy.underline_to_camel);
// Data table column name underline turn hump
sc.setColumnNaming(NamingStrategy.underline_to_camel);
// Generate lombok annotation automatically
sc.setEntityLombokModel(true);
// Logical deletion: set the field name representing logical deletion
sc.setLogicDeleteFieldName("deleted");
// Auto fill configuration
TableFill gmtCreate = new TableFill("gmt_create", FieldFill.INSERT);
TableFill gmtModified = new TableFill("gmt_modified", FieldFill.UPDATE);
ArrayList<TableFill> tableFills = new ArrayList<>();
tableFills.add(gmtCreate);
tableFills.add(gmtModified);
// Put the policy auto fill policy into the configuration
sc.setTableFillList(tableFills);
// Optimistic lock configuration
sc.setVersionFieldName("version");
// Throw the configuration into the constructor
mpg.setStrategy(sc);

implement

After execution, everything is automatically generated

Keywords: Java MySQL Mybatis

Added by nitroxman on Sat, 29 Jan 2022 02:14:05 +0200