Mybatis plus learning notes

The notes are arranged in the crazy God's mybatis plus

Mybatis plus study notes

1. Introduction

Mybatis-plus

MyBatis plus (MP for short) is an enhancement tool of MyBatis. On the basis of MyBatis, it only makes enhancement without change, and is born to simplify development and improve efficiency.

1.1 characteristics

  • 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. There is also 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

1.2. Support database

Any database that can use mybatis for CRUD and supports standard sql

1.3 frame structure

2. Quick start

2.1. Create database

CREATE DATABASE mybatis_plus;

2.2. Create data table

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)
);
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');

2.3. Create project

Create an empty Spring Boot project

2.4. Import dependency

<!--Database driven-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.20</version>
</dependency>
<!--mybatis-plus-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.0.5</version>
</dependency>
<!--lombok-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

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

2.5. Connect to the database

# mysql
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?userSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

2.6 coding

Traditional method: POJO Dao (connect to Mybatis and configure mapper XML file) - service controller

After using mybatis plus

  • pojo

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
        
        private Long id;
        private String name;
        private Integer age;
        private String email;
    }
    
  • mapper interface

    // Inherit the basic interface BaseMapper on the corresponding Mapper
    @Repository // Represents the persistence layer
    public interface UserMapper extends BaseMapper<User> {
        // All CRUD operations have been basically completed
        // You don't need a lot of configuration files like before
    }
    
  • Add the @ MapperScan annotation in the Spring Boot startup class and scan the Mapper folder

    @SpringBootApplication
    @MapperScan("com.thomas.mybatisplus.mapper")
    public class Application {
        public static void main(String[] args) {
            SpringApplication.run(QuickStartApplication.class, args);
        }
    }
    
  • use

    @SpringBootTest
    class MybatisPlusApplicationTests {
    
        // It inherits BaseMapper, and all methods come from the parent class
        // We can also write our own extension methods
        @Autowired
        UserMapper userMapper;
    
        @Test
        void contextLoads() {
            // The parameter is a Wrapper and condition constructor. We don't need null here
            // Query all users
            List<User> users = userMapper.selectList(null);
            users.forEach(System.out::println);
        }
    }
    
  • console output

    User(id=1, name=Jone, age=18, email=test1@baomidou.com)
    User(id=2, name=Jack, age=20, email=test2@baomidou.com)
    User(id=3, name=Tom, age=28, email=test3@baomidou.com)
    User(id=4, name=Sandy, age=21, email=test4@baomidou.com)
    User(id=5, name=Billie, age=24, email=test5@baomidou.com)
    

    Thinking questions:

    1. Who wrote SQL for us? Mybatis plus is all written

    2. Where did the method come from? Mybatis plus is all written

3. Configuration log

All our sql is invisible now. We want to know how it is executed, so we must look at the log!

# Configuration log (self-contained, console output)
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

4,CRUD

4.1. Insertion operation

@Test
public void testInsert(){
    User user = new User();
    user.setName("thomas");
    user.setAge(20);
    user.setEmail("test6@baomidou.com");
    int result = userMapper.insert(user);// Help us generate id automatically
    System.out.println(result);// Number of rows affected
    System.out.println(user);// Discovery: id auto backfill
}

The default value of the ID inserted in the database is the global unique ID (ID_WORKER)

4.2. Primary key generation strategy

Generation of unique id of distributed system

  • Snowflake algorithm:
    • snowflake is Twitter's open source distributed ID generation algorithm, and the result is a long ID. Its core idea is to use 41bit as the number of milliseconds, 10bit as the machine ID (five bits are the data center and five bit machine ID), 12bit as the serial number within milliseconds (meaning that each node can generate 4096 IDS per millisecond), and finally there is a symbol bit, which is always 0. Can guarantee almost the only one in the world!
  • We need to configure primary key auto increment:
    • @ TableId(type = IdType.AUTO) on entity class field
    • The database field must be self incremented

Other attributes:

public enum IdType {
     
    AUTO(0),//Self increment of database ID  
    NONE(1),//The type is not set with primary key      
    INPUT(2),//The user enters the ID, which can be filled by registering the auto fill plug-in  
    
	//The following three types are automatically filled only when the inserted object ID is empty.     
    ID_WORKER(3),//Globally unique ID (idWorker)      
    UUID(4),//Globally unique ID (UUID)    
    ID_WORKER_STR(5);//String globally unique ID (string representation of idworker)  
}

4.3. Update operation

@Test
public void testUpdate(){
    User user = new User();
    // Automatic splicing of dynamic sql through conditions
    user.setId(6L);
    user.setName("thomas1");
    user.setAge(18);
    // Note: updateById, but the parameter is an object
    int i = userMapper.updateById(user);
    System.out.println(i);
}

4.3. Automatic filling

Creation time and modification time! These operations are generally completed automatically. We don't want to update them manually!

Alibaba Development Manual: all database tables: gmt_create,gmt_modified almost all tables should be configured! And need automation!

Method 1: database level (database modification is not allowed during work)

  • Add a new field create in the table_ time,update_ time

    • The default value of the field is set to current_ Only MySQL update 7.5 and later_ Time settings are updated automatically.
  • To test the insertion method again, we need to synchronize the entity class first

    private Date createTime;
    private Date updateTime;
    
  • Just review the update results again

Method 2: code level

  • Delete default values and update operations in the database

  • Annotation needs to be added to the field attribute of the entity class

    //Add padding to field
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
    
    public enum FieldFill {
        /**
         * Do not process by default
         */
        DEFAULT,
        /**
         * Insert fill field
         */
        INSERT,
        /**
         * Update fill field
         */
        UPDATE,
        /**
         * Insert and update populated fields
         */
        INSERT_UPDATE
    }
    
  • Just write a processor to handle this annotation

    @Slf4j
    @Component // Don't forget to add the processor to the IOC container
    public class MyMetaObjectHandler implements MetaObjectHandler {
    
        // Filling strategy during insertion
        @Override
        public void insertFill(MetaObject metaObject) {
            log.info("start insert fill ...");
            // setFieldValByName(String fieldName, Object fieldVal, MetaObject metaObject)
            this.setFieldValByName("createTime", new Date(), metaObject);
            this.setFieldValByName("updateTime", new Date(), metaObject);
        }
    
        // Population policy when updating
        @Override
        public void updateFill(MetaObject metaObject) {
            log.info("start update fill ...");
            this.setFieldValByName("updateTime", new Date(), metaObject);
        }
    }
    
  • Test insertion

  • Test update, observation time

4.4 Le Guan lock

  • Optimistic lock: hence the name Siyi is very optimistic. It always thinks that there will be no problem. No matter what you do, don't lock it! If there is a problem, update the test value again.
  • Pessimistic lock: so it is very pessimistic. It always thinks that there is always a problem. No matter what you do, you will lock it and operate it again.

Optimistic lock implementation method:

  • When fetching the record, get the current version.
  • When updating, bring this version.
  • When updating, set version = newVersion where version = oldVersion.
  • If the version is wrong, the update fails.
-- Optimistic lock: 1. Query first to obtain the version number version = 1
-- A
update user set name = "kuangshen", version = version + 1 where id = 2 and version = 1
-- B: The thread finishes first. At this time version = 2,Will lead to A Modification failed!
update user set name = "kuangshen", version = version + 1 where id = 2 and version = 1

Optimistic lock plug-in for MyBatisPlus

  • Add the version field to the database.

  • Add the corresponding field to the entity class and mark it with @ Version

    @Version // Optimistic lock Version annotation
    private Integer version;
    
  • Register components

    //Scan our mapper folder
    @MapperScan("com.kuang.mapper")
    @EnableTransactionManagement
    @Configuration // Configuration class
    public class MyBatisPlusConfig {
    
    	// Register optimistic lock plug-in
        @Bean
        public OptimisticLockerInterceptor optimisticLockerInterceptor(){
            return new OptimisticLockerInterceptor();
        }
    }
    
  • test

    // Test optimistic lock successful
    @Test
    public void testOptimisticLocker(){
        // 1. Query user information
        User user = userMapper.selectById(1L);
        // 2. Modify user information
        user.setName("xxx");
        user.setEmail("3419339201@qq.com");
        userMapper.updateById(user);
    }
    
    // Failed to test optimistic lock, in multi-threaded environment!
    @Test
    public void testOptimisticLocker2(){
        // Thread 1
        User user = userMapper.selectById(1L);
        user.setName("user1");
        user.setEmail("2439115397@qq.com");
    
        // Simulate another thread to perform queue jumping
        User user2 = userMapper.selectById(1L);
        user2.setName("user2");
        user2.setEmail("2953887629@qq.com");
        userMapper.updateById(user2);
    
        userMapper.updateById(user); // If there is no optimistic lock, the value of queue jumping thread will be overwritten!
    }
    

4.5. Query operation

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

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

// Use map according to one of the query criteria
@Test
public void testSelectByMap(){
    HashMap<String, Object> map = new HashMap<>();
    // Custom query
    map.put("name","thomas");
    map.put("age","20");
    List<User> users = userMapper.selectByMap(map);
    users.forEach(System.out::println);
}

4.6 paging query

  • Paging the original limit
  • pageHelper third party plug-in
  • MyBatisPlus actually has a built-in paging plug-in

Use the paging plug-in provided by MyBatisPlus

  • Configuring the paging interceptor component

    @Bean
    public PaginationInterceptor paginationInterceptor(){
        return new PaginationInterceptor();
    }
    
  • You can directly use the Page object provided by MyBatisPlus

    // Test paging plug-in
    @Test
    public void testSelectPage(){
        // Parameter 1: current page
        // Parameter 2: page size
        Page<User> page = new Page<>(1,5);
        userMapper.selectPage(page,null);
        List<User> records = page.getRecords();
        records.forEach(System.out::println);
        System.out.println(page.getTotal()); //Total records
    }
    

4.7. Delete

// Delete by id
@Test
public void testDeleteById(){
    int result = userMapper.deleteById(1L);
    System.out.println(result);
}

// Batch deletion based on id set
@Test
public void testDeleteBatchIds(){
    int result = userMapper.deleteBatchIds(Arrays.asList(2, 3, 4));
    System.out.println(result);
}

// Delete according to map as condition
@Test
public void test(){
    Map<String,Object> map = new HashMap<>();
    map.put("name","thomas");
    map.put("age",20);
    int result = userMapper.deleteByMap(map);
    System.out.println(result);
}

4.8. Logical deletion

  • Physical delete: remove directly from the database.
  • Logical deletion: it is not removed in the database, but invalidated by a variable, deleted = 0 = > deleted = 1.

Logical deletion can prevent data loss, similar to the recycle bin.

test

  • Add a deleted field in the data table. The default value is 0

  • Add corresponding attributes to the entity class and use @ TableLogic

    @TableLogic // Logical deletion annotation
    private Integer deleted;
    
  • Configure logical deletion

    // Register logical deletion plug-in
    @Bean
    public ISqlInjector iSqlInjector(){
        return new LogicSqlInjector();
    }
    
    # Configure logical deletion
    mybatis-plus.global-config.db-config.logic-delete-value=1
    mybatis-plus.global-config.db-config.logic-not-delete-value=0
    
  • The test deletion is an update operation rather than a deletion operation. The record is still in the database, but the corresponding deleted value is modified to 1

  • Execute the query test and automatically filter the logically deleted fields when the query is found (the query statement will automatically splice the condition of deleted = 0)

5. Performance analysis plug-in

Function: performance analysis interceptor, which is used to output each SQL statement and its execution time

MybatisPlus also provides a performance analysis plug-in. If it exceeds this time, it will stop running

use

  • Import plug-in

    // Register SQL performance analysis plug-in
    @Bean
    @Profile({"dev","test"}) // Set the dev and test environment to start to ensure the running efficiency of the program
    public PerformanceInterceptor performanceInterceptor(){
        PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
        performanceInterceptor  .setMaxTime(100); // Unit: milliseconds. Set the maximum time of sql execution. If it exceeds, it will not be executed
        performanceInterceptor.setFormat(true); // Format code
        return performanceInterceptor;
    }
    

    Configure the environment in SpringBoot as dev or test environment.

    # Setting up the development environment
    spring.profiles.active=dev
    
  • test

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

    It is found that the log output from the console has been formatted, and an exception will be thrown as long as the SQL execution time exceeds the specified time!

    Using the performance analysis plug-in can improve efficiency. The disassembly has been removed in the new version of MP, and druid can be used

6. Conditional constructor

Mybatis plus conditional constructor

We can write some complex SQL and use it instead

@Test
public void test(){
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.isNotNull("name") // Name cannot be empty
            .isNotNull("email") // Mailbox is not empty
            .ge("age",20); // Age 18 or older
    userMapper.selectList(wrapper).forEach(System.out::println);
}

@Test
public void test2(){
    QueryWrapper<User> wrapper = new QueryWrapper();
    wrapper.eq("name","thomas"); //Name is Mike
    // Query a piece of data and use List or Map when multiple results appear
    User user = userMapper.selectOne(wrapper);
    System.out.println(user);
}

@Test
public void test3(){
    QueryWrapper<User> wrapper = new QueryWrapper();
    wrapper.between("age",20,30); //The query age range is [20,30]: including the values at both ends
    Integer count = userMapper.selectCount(wrapper);// Number of query results
    System.out.println(count);
}

@Test
public void test4(){
    QueryWrapper<User> wrapper = new QueryWrapper();
    wrapper.notLike("name","K") //The name does not include K
            .likeRight("name","C"); //The name ends in C
    List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);
    maps.forEach(System.out::println);
}

@Test
public void test5(){
    QueryWrapper<User> wrapper = new QueryWrapper();
    wrapper.inSql("id","select id from user where id < 3"); // id is found in the sub query

    List<Object> objects = userMapper.selectObjs(wrapper);
    objects.forEach(System.out::println);
}

@Test
public void test6(){
    QueryWrapper<User> wrapper = new QueryWrapper();
    wrapper.orderByAsc("id"); // Sort ascending by id

    List<User> users = userMapper.selectList(wrapper);
    users.forEach(System.out::println);
}

7. Code generator

Mybaits plus 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.

  • rely on

    <dependencies>
        <!-- web rely on -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- mybatis-plus rely on-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.1.tmp</version>
        </dependency>
        <!-- mybatis-plus Code generator dependency -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.3.1.tmp</version>
        </dependency>
        <!-- freemarker rely on -->
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
        </dependency>
        <!-- mysql rely on -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
    </dependencies>
    
  • code

    // Demo example: execute the main method, input the table name of the module on the console, and press enter to automatically generate the table name in the corresponding project directory
    public class CodeGenerator {
        /**
         * <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("jobob");
            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/ant?useUnicode=true&useSSL=false&characterEncoding=utf8");
            // dsc.setSchemaName("public");
            dsc.setDriverName("com.mysql.jdbc.Driver");
            dsc.setUsername("root");
            dsc.setPassword("password");
            mpg.setDataSource(dsc);
    
            // Package configuration
            PackageConfig pc = new PackageConfig();
            pc.setModuleName(scanner("Module name"));
            pc.setParent("com.baomidou.ant");
            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/" + 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 don't want to regenerate it, false will be 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 with you 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, you don't need to set it!");
            strategy.setEntityLombokModel(true);
            strategy.setRestControllerStyle(true);
            // Public parent class
            strategy.setSuperControllerClass("Your own parent controller,No, you don't need to set 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();
        }
    }
    

Modify the content according to your own project

Keywords: mybatis-plus

Added by AVATAr on Mon, 31 Jan 2022 23:05:56 +0200