MybatisPlus study notes

MybatisPlus

Official website: MyBatis-Plus (baomidou.com)

=Introduction

MyBatis-Plus (opens new window) (MP) is a MyBatis (opens new window) On the basis of MyBatis, the enhancement tool is only enhanced without change. It is born to simplify development and improve efficiency.

vision

Our vision is to be the best partner of MyBatis, just like Contra 1P and 2P in, and the efficiency is doubled.

# 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, and there is a powerful condition constructor to meet various use requirements
  • Support Lambda formal call: it is convenient to write various query conditions through Lambda expression, and there is no need to worry 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 primary key problem
  • 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 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

quick get start

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

step

  1. Create database mybatis_plus
  2. Create user table and insert data
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)
)ENGINE=INNODB DEFAULT CHARSET=utf8 ;
-- In real development, version(Optimistic lock),deleted(Logical deletion),gmt_create(Creation time),gmt_modifed((modification time)

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');
  1. Write project, initialize project! Initialize with SpringBoot!

  2. Import dependency

     <dependency>
     <groupId>org.projectlombok</groupId>
     <artifactId>lombok</artifactId>
     </dependency>
     <dependency>
     <groupId>mysql</groupId>
     <artifactId>mysql-connector-java</artifactId>
     </dependency>
     <!--        mybatisPlus rely on-->
     <dependency>
     <groupId>com.baomidou</groupId>
     <artifactId>mybatis-plus-boot-starter</artifactId>
     <version>3.0.5</version>
     </dependency>
    

    Note: do not import mybatis and mybatisplus at the same time. There may be version conflicts;

  3. Connect to the database. This step is the same as mybatis

    #The mysql8 driver is different from mysql5. You need to add a time zone
    spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
    spring.datasource.password=onmyown123
    spring.datasource.username=root
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    

6. The traditional method is POJO Dao (connect to the database and configure mapper. XML) - service controller

6. After using mybatis plus

  • pojo

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

    //Inherit the basic class BaseMapper on the corresponding mapper
    @Mapper
    public interface UserMapper extends BaseMapper<User> {
        //Once Dao is configured, just call the parent method directly
    }
    
  • test

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

The sql and method mybatisPlus have been written for us

The above entry case clearly shows the advantages of built-in mapper and powerful CRUD operation

Configuration log

Our current sql and methods are not visible. We want to know how it is executed, so we need to look at the log!

#Configuration log
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

CRUD extension

Insert operation

Insert insert

@Test
void testInsert(){
    User user = new User();
    user.setName("Bao Bao Feng");
    user.setAge(100);
    user.setEmail("123456@qq.com");
    int insert = userMapper.insert(user);
    System.out.println(insert);
    System.out.println(user);
}

Test:

(note here that the id parameter type of the User entity class is Long(long is not a wrapper class, and MybatisPlus will automatically fill in 0))

We found that the id was automatically filled with a long number

The default value of the id inserted in the database is the global unique id

Primary key generation strategy

Generation of unique id of distributed system: Distributed system unique ID generation scheme Summary - nick hao - blog Park (cnblogs.com)

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 (5 bits are the data center and 5 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.

Primary key auto increment

We need to configure primary key auto increment

  1. The id of the database table type is changed to auto increment

  2. Add @ TableId(type = IdType.AUTO) to the entity class id

  3. Test the insertion again

    After this setting, there is no need to set the primary key id, and mybatisPlus can also help us increase the id automatically

Other source code explanations

public enum IdType {
    AUTO, //Database id self increment
    NONE, //No primary key set
    INPUT, //Manual input
    ID_WORKER, //The default globally unique id is a numeric representation
    UUID, //Globally unique id uuid
    ID_WORKER_STR;//The default globally unique id string representation

    private int key;

    private IdType(int key) { /* compiled code */ }

    public int getKey() { /* compiled code */ }
}

update operation

update

@Test
void testUpdate(){
    User user = new User();
    user.setId(6L);
    user.setName("Bao Bao Feng");
    user.setAge(100);
    int i = userMapper.updateById(user);
}

Test results:

Automatically complete dynamic sql settings!

Auto fill

The creation time and modification time are all completed automatically. We don't want to update them manually

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

Database level (database is not allowed to be modified during work)

  1. Add create in the user table_ time,update_ Time field

  1. Test the modification method again, and synchronize the entity class first

    @Test
    void testUpdate(){
        User user = new User();
        user.setId(6L);
        user.setName("Ruan Feng");
        user.setAge(80);
        int i = userMapper.updateById(user);
        System.out.println(i);
    }
    

    Then update_time is automatically updated after modifying the code execution!

Code level

  1. Create the database first_ time,update_ Remove the default value of time, and then update_time update

  1. Add the annotation @ tablefile on the field of the entity class
//Add padding to fields
@TableField(fill = FieldFill.INSERT)
private Date createTime;

@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
  1. Edit the processor to process these annotations~
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    //Auto fill for insert operation
    @Override
    public void insertFill(MetaObject metaObject) {
        //default MetaObjectHandler setFieldValByName(String fieldName, Object fieldVal, MetaObject metaObject)
        log.info("start insertFill");
        this.setFieldValByName("createTime",new Date(),metaObject);
        this.setFieldValByName("updateTime",new Date(),metaObject);
    }
    //Automatic population of update operations
    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("start updateFill");
        this.setFieldValByName("updateTime",new Date(),metaObject);
    }
}
  1. Test insertion

  2. Test update, observe time!

Optimistic lock

Optimistic lock: no matter what you do, there will be no problem. No matter what you do, you won't lock it. Wait until there is a problem, and then update the value test

Pessimistic lock: everything will have problems. No matter what you do, you will lock it,

Optimistic lock implementation method:

  • When fetching records, get the current version
  • When updating, bring this version
  • When updating, set version = newVersion where version = oldVersion
  • If the version is incorrect, the update fails

Optimistic lock understanding:

-- Suppose there are two threads
-- A:
update user set name="abc",set version=2 
where id=2 and version=1;
-- B:
update user set name="abc",set version=2 
where id=2 and version=1;
-- hypothesis B Thread preemptive completion, version=2,that A The thread will fail

Optimistic lock implementation

  1. Add the version field to the database and set the default value to 1

  2. Add version field to entity class

    @Version
    private Integer version;
    
  3. Register components

    //Scan Mapper package
    @MapperScan("com.yfm.mapper")
    @EnableTransactionManagement
    @Configuration
    public class MybatisPlusConfig {
        //Register optimistic lock plug-in
        @Bean
        public OptimisticLockerInterceptor optimisticLockerInterceptor() {
            return new OptimisticLockerInterceptor();
        }
    }
    
  4. Test it!

    //Test optimistic lock successful
    @Test
    void testOptimisticLocker(){
        User user = userMapper.selectById(1L);
        user.setName("Tongtian book");
        userMapper.updateById(user);
    }
    
    //Failed to test optimistic lock, multithreading
    @Test
    void testOptimisticLocker2(){
        //Thread A
        User user2 = userMapper.selectById(1L);
        user2.setName("Tongtian Book 22");
    
        //Thread B, queue jump update
        User user3 = userMapper.selectById(1L);
        user3.setName("Tongtian Book 33");
    
        userMapper.updateById(user3);
    //When userMapper.updateById(user3); After execution, version=3, and the queried version=2, so the following will fail
        userMapper.updateById(user2);
    }
    
    

    5. Test results

    ==>  Preparing: UPDATE user SET name=?, age=?, email=?, version=?, create_time=?, update_time=? WHERE id=? AND version=? 
    ==> Parameters: Tongtian Book 33(String), 18(Integer), test1@baomidou.com(String), 3(Integer), 2021-11-21 17:04:33.0(Timestamp), 2021-11-22 09:22:57.102(Timestamp), 1(Long), 2(Integer)
    <==    Updates: 1
    Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7afb9c93]
    Creating a new SqlSession
    SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@f4f843f] was not registered for synchronization because synchronization is not active
    2021-11-22 09:22:57.116  INFO 17600 --- [           main] com.yfm.handler.MyMetaObjectHandler      : start updateFill
    JDBC Connection [HikariProxyConnection@2069378030 wrapping com.mysql.cj.jdbc.ConnectionImpl@6eed46e9] will not be managed by Spring
    ==>  Preparing: UPDATE user SET name=?, age=?, email=?, version=?, create_time=?, update_time=? WHERE id=? AND version=? 
    ==> Parameters: Tongtian Book 22(String), 18(Integer), test1@baomidou.com(String), 3(Integer), 2021-11-21 17:04:33.0(Timestamp), 2021-11-22 09:22:57.116(Timestamp), 1(Long), 2(Integer)
    <==    Updates: 0
    

Query operation

@Test
void testSelectById(){
    User user = userMapper.selectById("1L");
    System.out.println(user);
}
//Test batch query
@Test
void testSelectByBatchIds(){
    List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 6));
    users.forEach(System.out::println);

}
//Test Map multi condition query
@Test
void testSelectByMap(){
    HashMap<String, Object> map = new HashMap<String,Object>();
    map.put("name","Rootless");
    map.put("age",70);
    List<User> users = userMapper.selectByMap(map);
    users.forEach(System.out::println);
}

Paging query

Pagination is used a lot on websites

  • Original limit
  • PageHelper third party paging plug-in-
  • MP also has a built-in paging plug-in!

How to use

  1. Configure interceptor components

    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        // After setting the requested page to be larger than the maximum page, true will be called back to the home page, false will continue to request, and the default is false
        // paginationInterceptor.setOverflow(false);
        // Set the maximum number of single page restrictions, 500 by default, - 1 unlimited
        // paginationInterceptor.setLimit(500);
    
        // Turn on the join optimization of count, only for some left join s
        //paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
        return paginationInterceptor;
    }
    
  2. Directly use the Page object!

//Test paging
@Test
void testPage(){
    //Parameter 1: current page
    //Parameter 2: size per page
    Page<User> userPage = new Page<>(1,5);
    userMapper.selectPage(userPage,null);
    //Printout
    userPage.getRecords().forEach(System.out::println);
    //Tip: the total number of records in the table will be queried before paging
}

Delete operation

Basic delete operations:

deleteById
deleteBatchIds
deleteByMap

Logical deletion

Physical delete: delete directly from the database

Logical deletion: it is not removed from the database, but invalidated by a variable! deleted=0 => deleted=1

Administrators can view deleted records! Preventing data loss is equivalent to the function of recycle bin. If you really want to delete, delete the data through deleted=1

Test:

1. Add the deleted field to the database

2. Add the deleted attribute in the entity class

@TableLogic  //Logical deletion
private Integer deleted;

3. Logically delete configuration and register components

//There is no need to inject since MP3.1.1
//Logical delete component
@Bean
public ISqlInjector sqlInjector(){
    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

4. Test delete

@Test
void testDelete(){
    int i = userMapper.deleteById(1462326539445100546L);
    System.out.println(i);
}

Result: the delete operation is changed to the update operation, and the deleted field is modified!

At this time, you can't find it again. The deleted=0 condition will be added during query!

Performance analysis plug-in

In our usual development, we will encounter some slow sql.

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

Steps:

1. Import components

//This component has been removed in the new version
@Bean
@Profile({"dev","test"}) //Set the dev test environment on to ensure our efficiency
public PerformanceInterceptor performanceInterceptor(){
    PerformanceInterceptor performanceInterceptor=new PerformanceInterceptor();
    performanceInterceptor.setMaxTime(100);//ms sets the maximum time for sql execution. If it exceeds, it will not be executed
    performanceInterceptor.setFormat(true);  //sql format print
    return  performanceInterceptor;
}

Note that add the development environment settings in application.properties

#Set as development environment
spring.profiles.active=dev

2. Test use!

Conditional constructor

Very important: Wrapper

We can write some complex sql and use it instead!

Test:

Test 1:

@Test
void contextLoads() {
    //Note that you should add generics. You can't add conditions in a chain without adding generics!
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    //Test and query records whose age is greater than 30, email address and name are not empty
    wrapper
        .isNotNull("email")
        .isNotNull("name")
         //ge: greater than or equal to.
        .ge("age",30);  //Compare it with the Map we just learned
    userMapper.selectList(wrapper).forEach(System.out::println);

}

Test 2:

@Test
void test2() {
    //Query records with age equal to 18
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    //Note that you should add generics. You can't add conditions in a chain without adding generics!
    wrapper
        .eq("age",18);
    User user = userMapper.selectOne(wrapper);//Query a data. Multiple results appear. Use List or Map
    System.out.println(user);
}

Test 3:

@Test
void test3() {
    //Query the number of results aged between 18 and 50
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    //Note that you should add generics. You can't add conditions in a chain without adding generics!
    wrapper
        .between("age",18,50);
    Integer count = userMapper.selectCount(wrapper);
    System.out.println(count);
}

Test 4: remember to view the output SQL for analysis

//Fuzzy query
@Test
void test4() {
    //Note that you should add generics. You can't add conditions in a chain without adding generics!
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    //Right and left of like here mean '%' in right or left
    wrapper.likeRight("email","t")    //Query mailbox records starting with t
        .notLike("name","t");   //Name does not contain t
    List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);
    maps.forEach(System.out::println);
}
-- Execute SQL: 
    SELECT
        id,
        name,
        age,
        email,
        version,
        deleted,
        create_time,
        update_time 
    FROM
        user 
    WHERE
        deleted=0 
        AND email LIKE 't%' 
        AND name NOT LIKE '%t%'

Test 5:

//Subquery
@Test
void test5() {
    //Note that you should add generics. You can't add conditions in a chain without adding generics!
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    //id is found in the sub query
    wrapper.inSql("id","select id from user where id>3");
    List<Object> objects = userMapper.selectObjs(wrapper);
    objects.forEach(System.out::println);
}

result:

Execute SQL: 
    SELECT
        id,
        name,
        age,
        email,
        version,
        deleted,
        create_time,
        update_time 
    FROM
        user 
    WHERE
        deleted=0 
        AND id IN (
            select
                id 
            from
                user 
            where
                id>3
        )

Test 6:

//sort
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.orderByAsc("age");

There are still a lot of content about conditional constructors, such as grouping and so on, as after-school exercises

Code generator

Mapper, POJO, service and controller all write for you!

Use the following code:

Import the dependency of MybatisPlus, and the crazy God video uses version 3.0.5

In addition to the first time, you should also import template engine dependencies

<!--        mybatisPlus rely on-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.0.5</version>
</dependency>
<!--        MybatisPlus Default template engine dependency-->
<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity-engine-core</artifactId>
    <version>2.3</version>
</dependency>
public static void main(String[] args) {
//You need to build an automatic code generator object
    AutoGenerator mpg = new AutoGenerator();
    //Configuration policy
    //1 global configuration
    GlobalConfig gc = new GlobalConfig();
    //D:\java\idea\IntelliJ IDEA 2020.1\IDEACode\mybatisPlus\mybatis_plus
    String projectPath = System.getProperty("user.dir");//Represents the current path of the project
    gc.setOutputDir(projectPath+"/src/main/java");
    gc.setAuthor("yfm");
    gc.setOpen(false);//Open Explorer
    gc.setFileOverride(false); //Overwrite original code
    gc.setServiceName("%sService"); //I prefix to Service
    gc.setIdType(IdType.ID_WORKER);
    gc.setDateType(DateType.ONLY_DATE);
    gc.setSwagger2(true);
    mpg.setGlobalConfig(gc);

    //2. Data source configuration
    DataSourceConfig dsc = new DataSourceConfig();
    dsc.setUrl("jdbc:mysql://localhost:3306/db1?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8");
    dsc.setUsername("root");
    dsc.setPassword("onmyown123");
    dsc.setDriverName("com.mysql.cj.jdbc.Driver");
    dsc.setDbType(DbType.MYSQL);
    mpg.setDataSource(dsc);

    //3. Package settings
    PackageConfig pc= new PackageConfig();
    pc.setModuleName("blog");//Grinding block life
    pc.setParent("com.parent");//The result of the package name is com.parent.blog
    pc.setEntity("entity");//Entity class package name
    pc.setMapper("mapper");
    pc.setService("service");
    pc.setController("controller");
    mpg.setPackageInfo(pc);

    //4. Policy configuration, logical deletion, optimistic lock, etc
    StrategyConfig strategy = new StrategyConfig();
    strategy.setInclude("user");//Set the table name to map
    strategy.setNaming(NamingStrategy.underline_to_camel);
    strategy.setColumnNaming(NamingStrategy.underline_to_camel);
    strategy.setEntityLombokModel(true); //Automatic lombok
    //Logical deletion
    strategy.setLogicDeleteFieldName("deleted");
    //Optimistic lock
    strategy.setVersionFieldName("version");
    //Auto fill
    TableFill createTime =new TableFill("createTime", FieldFill.INSERT);
    TableFill updateTime =new TableFill("updateTime", FieldFill.INSERT_UPDATE);
    ArrayList<TableFill> tableFills = new ArrayList<>();
    tableFills.add(createTime);
    tableFills.add(updateTime);
    strategy.setTableFillList(tableFills);

    strategy.setRestControllerStyle(true);
    strategy.setControllerMappingHyphenStyle(true);  //localhost:8080/hello_id_2
    mpg.setStrategy(strategy);

    mpg.execute();//implement

}

Tags: MybatisPlus

Posted on Tue, 23 Nov 2021 13:08:50 -0500 by digitalecartoons