Get started with Mybatis-Plus!

Official website documents: https://baomidou.com/

1 Introduction

MyBatis-Plus (opens new window) (referred to as MP) is a MyBatis (opens new window) The enhancement tool of MyBatis only enhances and does not change on the basis of MyBatis, and is born to simplify development and improve efficiency.

1.1. Features

  • Non-invasive: only make enhancements without changing, introducing it will not affect existing projects, smooth as silk
  • Low loss: Basic CURD is automatically injected at startup, performance is basically lossless, and direct object-oriented operation
  • Powerful CRUD operations: Built-in general Mapper and general Service, most CRUD operations on a single table can be implemented with only a small amount of configuration, and more powerful conditional constructors to meet various usage needs
  • Support Lambda form invocation: Through Lambda expressions, you can easily write various query conditions, and you don't need to worry about writing wrong fields.
  • Supports automatic primary key generation: supports up to 4 primary key strategies (including a distributed unique ID generator - Sequence), which can be freely configured to perfectly solve the primary key problem
  • Support ActiveRecord mode: Support ActiveRecord form call, entity class can perform powerful CRUD operations only by inheriting Model class
  • Support custom global general operations: support global general method injection ( Write once, use anywhere )
  • Built-in code generator: Use code or Maven plugin to quickly generate Mapper , Model , Service , Controller layer code, support template engine, and more custom configurations waiting 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 plugin supports multiple databases: MySQL, MariaDB, Oracle, DB2, H2, HSQL, SQLite, Postgre, SQLServer, etc.
  • 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, which can quickly identify slow queries
  • Built-in global interception plug-in: provides intelligent analysis and blocking of delete and update operations on the entire table, and can also customize interception rules to prevent misoperation

2. Quick Start

  1. Create database mybatis_plus

  2. create table user

    DROP TABLE IF EXISTS user;
    
    CREATE TABLE user
    (
        id BIGINT(20) NOT NULL COMMENT 'primary key ID',
        name VARCHAR(30) NULL DEFAULT NULL COMMENT 'Name',
        age INT(11) NULL DEFAULT NULL COMMENT 'age',
        email VARCHAR(50) NULL DEFAULT NULL COMMENT 'Mail',
        PRIMARY KEY (id)
    );
    
  3. insert data

    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');
    
  4. Create project and initialize! !

  5. import dependencies

    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.2</version>
    </dependency>
    

    Note: Try not to import mybatis and mybatis-plus dependencies at the same time, it may cause conflicts

  6. Configure the data source

    spring:
      datasource:
        username: root
        password: 18227022334a
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/mybatis_plus?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
    
  7. Inherit BaseMapper on the corresponding UserMaaper interface

    @Repository
    public interface UserMapper extends BaseMapper<User> {
    }
    

    Note:

    • There is no need to configure the xml file, all the basic query statements are in the BaseMapper interface! ! , the basic CRUD has been implemented, but if there are special sql statements, you still need to write them yourself! !
    • Need to pass the entity class generic of pojo in the inherited BaseMaaper
  8. test

    @SpringBootTest
    class MybatisPlusStudyApplicationTests {
    
        @Autowired
        private UserMapper userMapper;
    
        @Test
        void contextLoads() {
            List<User> userList = userMapper.selectList(null);
            for (User user : userList) {
                System.out.println(user);
            }
        }
    }
    

    Note: The parameter of the selectList() method in UserMapper is MP's built-in conditional wrapper Wrapper, so if you don't fill it in, there will be no conditions.

3. Configure the log

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

4.CRUD extension

4.1. Insert

  1. When inserting data, if we do not set the primary key id ourselves, a globally unique id will be automatically generated, and it will be automatically backfilled into our object, that is, it will automatically assign the id of the object for which we did not set the id. ! !

  2. Primary key generation strategy:

  3. Configure the primary key generation strategy:

    public class User {
        @TableId(type = IdType.ASSIGN_ID)//default
        private Long id;
        private String name;
        private Integer age;
        private String email;
    }
    

    IdType:

    value describe
    AUTO Database ID auto increment
    NONE Stateless, the type is not set the primary key type (annotation is equal to follow the global, global Rio is equal to INPUT)
    INPUT set the primary key value by yourself before insert
    ASSIGN_ID Assign ID (the primary key type is Number(Long and Integer) or String)(since 3.3.0), use the method nextId of the interface IdentifierGenerator (the default implementation class is DefaultIdentifierGenerator snowflake algorithm)
    ASSIGN_UUID Assign UUID, the primary key type is String(since 3.3.0), and use the method nextUUID of the interface IdentifierGenerator (default default method)
    ID_WORKER Distributed globally unique ID long type (please use ASSIGN_ID) (deprecated)
    UUID 32-bit UUID string (please use ASSIGN_UUID) (deprecated)
    ID_WORKER_STR Distributed globally unique ID string type (please use ASSIGN_ID) (deprecated)

    Note: If you use auto-increment, it may be related to the fact that the database does not check the auto-increment option! !

4.2. Update

  1. When updating, although the name is ById, an adapted generic class object needs to be passed in. At the same time, it can be found from the log that mybatis-plus will automatically help us splicing sql statements according to the conditions, that is, adding a field with an empty value , then the field will not be set when it is set! !

  2. autofill

    • Alibaba development manual shows that all database tables should contain two fields: gmt_create, gmt_modified, that is, creation time and modification time, and we should use automation to complete this operation! !

      • Database level operations (actual work is not allowed): way of using default values

    • Code level:

      • Annotate with @TableField(fill=...)

        //@TableField(fill = FieldFill.DEFAULT) default
        //Has the following values: DEFAULT, INSERT,UPDATE,INSERT_UPDATE;
        
        @TableField(fill = FieldFill.INSERT)
        private Data gmtCreate;//mybatis-plus automatically turns on hump naming
        @TableField(fill = FieldFill.INSERT_UPDATE)
        private Data gmtModified;
        
      • Write a custom implementation class MyMetaObjectHandler to handle this annotation

        @Slf4j
        @Component
        public class MyMetaObjectHandler implements MetaObjectHandler {
           
          //Autofill policy on insert
            @Override
            public void insertFill(MetaObject metaObject) {
                log.info("start insert fill......");
                this.setFieldValByName("gmtCreate",LocalDateTime.now(),metaObject);
                this.setFieldValByName("gmtModified",LocalDateTime.now(),metaObject);
            }
        
            //Autofill policy on update
            @Override
            public void updateFill(MetaObject metaObject) {
                log.info("start update fill......");
                this.setFieldValByName("gmtModified", LocalDateTime.now(),metaObject);
            }
        }
        
      • test! !

      • Note: When the data type is long, you should add l after the data to indicate that the data is a long integer! !

  3. optimistic locking pessimistic locking

    • Optimistic lock: very optimistic, always think that there will be no problem, no matter what you do, it will not be locked. If there is a problem, perform the update value test again!

    • Pessimistic lock: very pessimistic, always think that there will be problems with everything, no matter what you do, it will be locked, and you are going to operate!

    • When a record is to be updated, it is hoped that the record has not been updated by others. The implementation of optimistic locking is as follows:

      1. When fetching a record, get the current version
      2. When updating, bring this version
      3. When performing an update, set version = newVersion where version = oldVersion
      4. If the version is incorrect, the update fails
      5. illustrate:
        • The only supported data types are: int,Integer,long,Long,Date,Timestamp,LocalDateTime
        • In integer type newVersion = oldVersion + 1
        • newVersion will be written back to entity
        • Only support updateById(id) and update(entity, wrapper) methods
        • Under the update(entity, wrapper) method, wrapper cannot be reused!!!
    • Implement optimistic locking:

      1. Database add field version

      2. Add property to entity class

        @Version
        private Integer version;
        
      3. Create a new configuration class configuration plugin

        @Configuration
        @MapperScan("com.xiaoye.mapper")//You can put it not on the startup class, you can also put it here
        @EnableTransactionManagement//Manage transactions automatically
        public class MyBatisPlusConfig {
            //Register optimistic locking plugin
            @Bean
            public MybatisPlusInterceptor mybatisPlusInterceptor() {
                MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
                mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
                return mybatisPlusInterceptor;
            }
        }
        
      4. test! !

4.3. Query

  1. Common queries:

    • selectById(): query by id
    • selectList(): query all
    • selectBatchIds(): batch query, the parameter is a collection
    • selectByMap(): conditional query, the parameter is map, the key of the map is the field name, and the value is the field value
  2. Paging query:

    1. Add plugins under the configuration class

      /**
           * The new paging plug-in, one and two, follow the rules of mybatis. You need to set MybatisConfiguration#useDeprecatedExecutor = false to avoid cache problems (this property will be removed after the old plug-in is removed)
           */
      @Bean
      public MybatisPlusInterceptor mybatisPlusInterceptor2() {
          MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
          interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
          return interceptor;
      }
      
    2. use

      void contextLoads() {
          //The first parameter: the current page
          //The second parameter: the number of pages
          Page<User> userPage = new Page<>(2,3);
          userMapper.selectPage(userPage, (Wrapper<User>) null);
          for (User record : userPage.getRecords()) {
              System.out.println(record);
          }
      }
      ///userPage.getTotal() gets the total number of data
      

4.4, delete

  1. Common deletes:

    //
    userMapper.delete();
    //batch deletion
    userMapper.deleteBatchIds();
    //delete by id
    userMapper.deleteById();
    //delete by condition
    userMapper.deleteByMap();
    
  2. Logical deletion: It is not deleted directly in the database, but through a logical variable, such as is_delete=0|1, instead of real deletion

    Additional instructions:

    • Only works for automatically injected sql:
    • Insert: no restrictions
    • Find: Append a where condition to filter out deleted data, and the where condition generated using wrapper.entity will ignore this field
    • Update: Append where condition to prevent updates to deleted data, and where condition generated with wrapper.entity ignores this field
    • delete: convert to update
    1. Add an is_delete field to the database

    2. Add attributes to the entity class (after 3.3, no annotations can be added)

      @TableLogic
      private Integer isDelete;
      
    3. Configure in yaml

      mybatis-plus:
        global-config:
          db-config:
            logic-delete-field: isDelete # Global tombstone entity field name
            logic-delete-value: 1 # logical removed value (default 1)
            logic-not-delete-value: 0 # Logical undeleted value (default 0)
      
    4. test! !

    5. common problem:

      1. How to insert:
        • Database defined defaults (recommended)
        • set yourself
        • Use autofill
      2. The automatic filling function of the delete interface function fails:
        • Use deleteById method (recommended)
        • Use the update method and: UpdateWrapper.set(column, value) (recommended)
        • Use the update method and: UpdateWrapper.setSql("column=value")

5. Performance analysis plugin

This function relies on the p6spy component, which perfectly outputs and prints SQL and execution time

  1. Import dependencies:

    <dependency>
        <groupId>p6spy</groupId>
        <artifactId>p6spy</artifactId>
        <version>3.2.0</version>
    </dependency>
    
  2. application.yaml changes the configuration of all aspects of the database

    username: root
    password: 18227022334a
    driver-class-name: com.p6spy.engine.spy.P6SpyDriver
    url: jdbc:p6spy:mysql://localhost:3306/mybatis_plus?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
    
  3. Configure spy.properties

    #3.2.1 Use above
    modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
    # Custom log printing
    logMessageFormat=com.xiaoye.log.P6SpyLogger
    #log output to console
    appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
    # log sql using the logging system
    #appender=com.p6spy.engine.spy.appender.Slf4JLogger
    # Set p6spy driver proxy
    deregisterdrivers=true
    # Unprefix JDBC URL
    useprefix=true
    # With the exception of the configuration record Log, the result sets that can be removed are error, info, batch, debug, statement, commit, rollback, result, and resultset.
    excludecategories=info,debug,result,commit,resultset
    # date format
    dateformat=yyyy-MM-dd HH:mm:ss
    # The actual drive can be multiple
    #driverlist=org.h2.Driver
    # Whether to enable slow SQL logging
    outagedetection=true
    # Slow SQL logging standard 2 seconds
    outagedetectioninterval=2
    
  4. Write a custom log class

    @Slf4j
    public class P6SpyLogger implements MessageFormattingStrategy {
    
        private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
        @Override
        public String formatMessage(int connectionId, String now, long elapsed, String category, String prepared, String sql) {
            String date = dateFormat.format(new Date());
            if (log.isInfoEnabled()) {
                log.info("execution time: {}", date);
                log.info("sql:  {}", sql);
                log.info("time consuming:{} millisecond", elapsed);
            }
            return "";
        }
    }
    
  5. Notice:

    • driver-class-name is the driver class provided by p6spy
    • The url prefix is ​​jdbc:p6spy followed by a colon for the corresponding database connection address
    • Print out sql as null, add commit in excludecategories
    • Batch operations do not print sql, remove batch in excludecategories
    • Please use MybatisPlusLogFactory (new in 3.2.1) for the problem of repeated printing in batch operations
    • This plugin has performance loss and is not recommended for production environments.

6. Conditional constructor Wrapper

  1. QueryWrapper: select, delete use

  2. UpdateWrapper: update use

  3. common:

  4. Chain programming can be used:

    public void test1(){
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.gt("age",21).like("name","plum");
        List<User> userList = userMapper.selectList(wrapper);
        userList.forEach(System.out::println);
    }
    

7. Code Generator

  1. import dependencies

    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.2</version>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-generator</artifactId>
        <version>3.5.2</version>
    </dependency>
    <dependency>
        <groupId>org.apache.velocity</groupId>
        <artifactId>velocity</artifactId>
        <version>1.7</version>
    </dependency>
    
  2. Write auto-generated code classes

    package com.xiaoye;
    
    import com.baomidou.mybatisplus.annotation.FieldFill;
    import com.baomidou.mybatisplus.generator.AutoGenerator;
    import com.baomidou.mybatisplus.generator.IFill;
    import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
    import com.baomidou.mybatisplus.generator.config.GlobalConfig;
    import com.baomidou.mybatisplus.generator.config.PackageConfig;
    import com.baomidou.mybatisplus.generator.config.StrategyConfig;
    import com.baomidou.mybatisplus.generator.config.rules.DateType;
    import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
    import com.baomidou.mybatisplus.generator.fill.Column;
    import com.baomidou.mybatisplus.generator.fill.Property;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public  class Test{
        private static final DataSourceConfig dataSourceConfig = new DataSourceConfig.Builder("jdbc:mysql://localhost:3306/mybatis_plus?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC", "root", "18227022334a")
                .build();
        public static void main(String[] args) {
            /**
             * code generation
             */
            GlobalConfig globalConfig = new GlobalConfig.Builder()
                    .author("Xiaoye")
                    .dateType(DateType.TIME_PACK)
                    .commentDate("yyyy-MM-dd hh:mm:ss")
                    .outputDir(System.getProperty("user.dir") + "/src/main/java")
                    .build();
    
            PackageConfig packageConfig = new PackageConfig.Builder()
                    .parent("com.xiaoye")
                    .entity("pojo")
                    .service("service")
                    .serviceImpl("service.Impl")
                    .mapper("mapper")
                    .xml("mapper.xml")
                    .controller("controller")
                    .build();
    
            StrategyConfig strategyConfig = new StrategyConfig.Builder()
                    .entityBuilder()
                    .enableTableFieldAnnotation()
                    .enableLombok()
                    .enableTableFieldAnnotation()
                    .versionColumnName("version")
                    .versionPropertyName("version")
                    .logicDeleteColumnName("is_delete")
                    .logicDeletePropertyName("isDelete")
                    .columnNaming(NamingStrategy.underline_to_camel)
                    .addTableFills(new Column("gmt_create", FieldFill.INSERT))
                    .addTableFills(new Column("gmt_modified", FieldFill.INSERT))
                    .addTableFills(new Property("gmtCreate", FieldFill.INSERT_UPDATE))
                    .addTableFills(new Property("gmtModified", FieldFill.INSERT_UPDATE))
                    .build();
    
            StrategyConfig strategyConfig1 = new StrategyConfig.Builder()
                    .controllerBuilder()
                    .enableHyphenStyle()
                    .enableRestStyle()
                    .build();
    
            StrategyConfig strategyConfig2 = new StrategyConfig.Builder()
                    .serviceBuilder()
                    .build();
    
            StrategyConfig strategyConfig3 = new StrategyConfig.Builder()
                    .mapperBuilder()
                    .enableMapperAnnotation()
                    .enableBaseResultMap()
                    .enableBaseColumnList()
                    .build();
            AutoGenerator generator = new AutoGenerator(dataSourceConfig);
            generator.global(globalConfig);
            generator.strategy(strategyConfig);
            generator.strategy(strategyConfig1);
            generator.strategy(strategyConfig2);
            generator.strategy(strategyConfig3);
            generator.packageInfo(packageConfig);
            generator.execute();
        }
    }
    

8. Configuration files

mybatis-plus:
  mapperPackage: com.**.**.mapper
  # Corresponding XML file location
  mapperLocations: classpath*:mapper/**/*Mapper.xml
  # Entity scan, multiple package s are separated by commas or semicolons
  typeAliasesPackage: com.**.**.domain
  # For typeAliasesPackage, if this property is configured, only domain objects under the path with this class as the parent class will be scanned
  #typeAliasesSuperType: Class<?>
  # If this property is configured, SqlSessionFactoryBean will register the class under the package as the corresponding TypeHandler
  #typeHandlersPackage: null
  # If this property is configured, the enumeration class under the path will be injected, so that the entity class field can use the enumeration property easily and quickly
  #typeEnumsPackage: null
  # Whether to check the existence of the MyBatis XML file at startup, not checked by default
  checkConfigLocation: false
  # Through this attribute, the executor of MyBatis can be specified. There are three types of executors in MyBatis:
  # SIMPLE: This executor type does nothing special, creating a new PreparedStatement for each statement execution
  # REUSE: This executor type reuses prepared statements (PreparedStatement)
  # BATCH: This executor type executes all update statements in batches
  executorType: SIMPLE
  # Specify the externalized MyBatis Properties configuration, through which the configuration can be extracted and deployed in different environments
  configurationProperties: null
  configuration:
    # Automatic camel case mapping
    # If your database naming conforms to the rules, you do not need to use the @TableField annotation to specify the database field name
    mapUnderscoreToCamelCase: true
    # The default enumeration processing class, if this property is configured, the enumeration will be processed uniformly using the specified processor
    # org.apache.ibatis.type.EnumTypeHandler : the name of the storage enumeration
    # org.apache.ibatis.type.EnumOrdinalTypeHandler : stores the index of the enumeration
    # com.baomidou.mybatisplus.extension.handlers.MybatisEnumTypeHandler : The enumeration class needs to implement the IEnum interface or the field tag @EnumValue annotation.
    defaultEnumTypeHandler: org.apache.ibatis.type.EnumTypeHandler
    # When set to true, lazy loaded objects may all be loaded by any lazy property, otherwise, each property is loaded on demand. Need to be used with lazyLoadingEnabled.
    aggressiveLazyLoading: true
    # MyBatis automatic mapping strategy
    # NONE: do not enable automatic mapping
    # PARTIAL: only do automatic mapping for non-nested resultMap
    # FULL: Automatically map all resultMap s
    autoMappingBehavior: PARTIAL
    # Unknown column or unknown attribute handling strategy when MyBatis automatically maps
    # NONE: do nothing (default)
    # WARNING: Print relevant warning information in the form of a log
    # FAILING: Treat as a mapping failure and throw an exception and details
    autoMappingUnknownColumnBehavior: NONE
    # Mybatis first level cache, default is SESSION
    # SESSION session level cache, the same query statement in the same session will not query the database again
    # STATEMENT closes the first level cache
    localCacheScope: SESSION
    # Enable Mybatis secondary cache, the default is true
    cacheEnabled: true
  global-config:
    # Whether to print Logo banner
    banner: true
    # Whether to initialize SqlRunner
    enableSqlRunner: false
    dbConfig:
      # Primary key type
      # AUTO database ID auto increment
      # NONE empty
      # INPUT User input ID
      # ASSIGN_ID globally unique ID
      # ASSIGN_UUID Globally unique ID UUID
      idType: AUTO
      # table name prefix
      tablePrefix: null
      # Field format, for example: %s, (invalid for primary key)
      columnFormat: null
      # Whether the table name is named using camel case to underscore, it only takes effect for the table name
      tableUnderline: true
      # Uppercase naming, valid for both table and field names
      capitalMode: false
      # The tombstone field attribute name of the global entity
      logicDeleteField: deleteFlag
      # logical deleted value
      logicDeleteValue: 1
      # Logical undeleted value
      logicNotDeleteValue: 0
      # The field validation strategy of insert, the field validation strategy when inserting
      # IGNORED ignore judgment
      # NOT_NULL non-NULL judgment
      # NOT_EMPTY non-null judgment (only for string type fields, other types of fields are still non-NULL judgment)
      # DEFAULT is the default, generally only used in annotations
      # NEVER does not join SQL
      insertStrategy: NOT_EMPTY
      # The update of the field validation strategy, the field validation strategy at the time of update
      updateStrategy: NOT_NULL
      # The field validation strategy of select, the field validation strategy at the time of select is both wrapper based on the where condition generated by the internal entity
      selectStrategy: NOT_EMPTY

Tags: mybatis-plus

Posted by Zmodem on Tue, 30 Aug 2022 21:02:36 +0530