Mybatis-Plus-[easy to understand full explanation]

Mybatis-Plus

Introduction

MyBatis-Plus (opens new window) (MP for short) is an enhancement tool for MyBatis (opens new window). On the basis of MyBatis, only enhancements are made without changes, and it was born to simplify development and improve efficiency.
See the official website for details: https://baomidou.com/pages/24112f/#%E7%89%B9%E6%80%A7

quick start

  1. build table
DROP TABLE IF EXISTS user;

CREATE TABLE user
(
    id BIGINT(20) NOT NULL COMMENT '主键ID',
    name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
    age INT(11) NULL DEFAULT NULL COMMENT '年龄',
    email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
    PRIMARY KEY (id)
);

DELETE FROM user;

INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, '[email protected]'),
(2, 'Jack', 20, '[email protected]'),
(3, 'Tom', 28, '[email protected]'),
(4, 'Sandy', 21, '[email protected]'),
(5, 'Billie', 24, '[email protected]');
  1. Create an empty Spring Boot project
  2. Add dependencies MySQL, lombok, mybatis_plus
<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<version>8.0.28</version>
</dependency>
<dependency>
	<groupId>org.projectlombok</groupId>
	<artifactId>lombok</artifactId>
	<version>1.18.22</version>
</dependency>
<dependency>
	<groupId>com.baomidou</groupId>
	<artifactId>mybatis-plus-boot-starter</artifactId>
	<version>3.5.1</version>
</dependency>
  1. Write a configuration file and connect to the database
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

5. Traditional way pojo-dao (connect mybatis, configure mapper.xml file)-service-controller
5. After using mybatis-plus ( inherit the BaseMapper interface, omitting the mapper.xml file )

  • poo
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    
    
    private Long id;
    private String name;
    private Integer age;
    private String email;
}
  • mapper interface
//在对应的mapper上面继承基本接口就可以 BaseMapper
@Repository//代表持久层
public interface UserMapper extends BaseMapper<User> {
    //所有的CRUD操作都已经编写完成了
    //你不需要像以前一样配置一大堆文件了
}
  • Note that we need to scan all interfaces under our mapper package on the main startup class@MapperScan("com.kuang.mapper")
//扫描我们的mapper文件夹
@MapperScan("com.kuang.mapper")
@SpringBootApplication
public class MybatisPlusApplication {
    
    

    public static void main(String[] args) {
    
    
        SpringApplication.run(MybatisPlusApplication.class, args);
    }

}
  • test class test
@SpringBootTest
class MybatisPlusApplicationTests {
    
    
    //继承了BaseMapper,所有方法都来自父类,我们也可以编写自己的拓展方法
    @Autowired
    private UserMapper userMapper;

    @Test
    void contextLoads() {
    
    
        //这里的参数是一个Wrapper,条件构造器,这里我们先不用,将其设为null
        //查询全部用户
        List<User> userList = userMapper.selectList(null);
        userList.forEach(System.out::println);
    }
}

think about the problem

  1. Who wrote SQL for us? MyBatis-Plus
  2. Where did the method come from? MyBatis-Plus

configuration log

All our sql is now invisible, we want to know how it is executed, so we have to see the log!

# 配置日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

insert image description here

After configuring the log, you need to pay attention to this automatically generated SQL in the following studies, and you will like Mybatis-Plus!

CRUD extension

insert operation

insert insert

    @Test//测试插入
    public void textInsert() {
    
    
        User user = new User();
        user.setName("lala啦");
        user.setAge(3);
        user.setEmail("[email protected]");
        int result = userMapper.insert(user);//帮我们自动生成id
        System.out.println(result);//受影响的行数
        System.out.println(user);//发现,id会自动回填
    }

insert image description here

The default value of the id inserted into the database is: global unique id

primary key generation strategy

Default ASSIGN_ID globally unique id

Distributed system unique id generation: https://www.cnblogs.com/haoxinyue/p/5208136.html
Snowflake algorithm:
snowflake is Twitter's open source distributed ID generation algorithm, and the result is a long ID. The core idea is: use 41bit as the number of milliseconds, 10bit as the ID of the machine (5 bits are the data center, 5 bits for the machine ID), and 12bit as the serial number within milliseconds (meaning that each node can generate 4096 IDs), there is a sign bit at the end, which is always 0. Guaranteed to be almost unique in the world!

primary key auto increment

We need to configure primary key auto-increment:

  1. entity class field@TableId(type = IdType.AUTO)
  2. Database fields must be self-increasing!
    insert image description here
  3. Just test the insertion again, and the increment is 1!

Source code explanation of the rest of the IdType

public enum IdType {
    
    
    AUTO(0),//数据库id自增
    NONE(1),//未设置主键
    INPUT(2),//手动输入,就需要自己配置id了
    ASSIGN_ID(3),//默认的全局唯一id 
    ASSIGN_UUID(4);//全局唯一id

update operation

 @Test
    //测试更新
    public void testUpdate(){
    
    
        User user = new User();
        //通过条件自动拼接动态sql
        user.setId(6L);
        user.setName("lala嘿嘿");
        user.setAge(3);
        user.setEmail("[email protected]");
        //注意:userById 但是参数是一个对象
        int i = userMapper.updateById(user);
        System.out.println(i);
    }

insert image description here
All sql is automatically configured dynamically for you!

autofill

Creation time, modification time! These operations are all done automatically, we do not want to manually update!
Alibaba Development Manual: All database tables: gmt_create, gmt_modified Almost all tables must be configured! And it needs to be automated!

Method 1: Database level (you are not allowed to modify the database during work)

  1. Add new fields create_time, update_time to the table
    insert image description here
  2. To test the insert method again, we need to synchronize the entity class first!
private Date createTime;
private Date updateTime;
  1. Update again to view the results
    insert image description here
    problem appear: In addition to inserting records, the rest of the creation time and update time are null
    Solve the problem: When creating a field, it needs to be set that the field cannot be empty
    insert image description here

Method 2: code level

  1. Delete the default values ​​of the two fields create_time and update_time in the database, update_time update operation
    insert image description here
  2. Annotations need to be added to entity class field attributes
    //字段添加填充内容
    //插入的时候填充字段
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    //插入和更新的时候填充字段
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
  1. Just write a processor to handle this annotation!
@Slf4j//对日志依赖的引用
@Component//一定不要忘记把处理器增加到IOC容器中!
public class MyMetaObjectHandler implements MetaObjectHandler {
    
    

    //插入时的填充策略
    @Override
    public void insertFill(MetaObject metaObject) {
    
    
        log.info("start insert fill......");
        this.setFieldValByName("createTime",new Date(),metaObject);
        //MetaObject[反射对象类]是Mybatis的工具类,通过MetaObject获取和设置对象的属性值。
        this.setFieldValByName("updateTime",new Date(),metaObject);
    }

    //更新时的填充策略
    @Override
    public void updateFill(MetaObject metaObject) {
    
    
        log.info("start update fill.....");
        this.setFieldValByName("updateTime",new Date(),metaObject);
    }
}
  1. test insertion
  2. Just test the update and observe the time
  • summary: But one thing, I found that other records did not create the two fields of create_time and update_time by default. Only the fields that have been inserted or updated will be filled.

optimistic lock

Optimistic lock: As the name suggests, it is very optimistic, it always thinks that there will be no problems, no matter what it does, it will not be locked! If there is a problem, it will be locked, and the update will fail.
Pessimistic lock: As the name suggests, it is very pessimistic. It always thinks that there will be a problem, and it will lock it no matter what it does! to operate again

We mainly explain the optimistic locking mechanism here!
Optimistic lock implementation

  • When fetching records, get the current version
  • When updating, bring this version
  • When performing an update, set version = newVersion where version = oldVersion
  • If the version is wrong, the update will fail
    [in short, it is continuous update, when the version is found to be wrong, that is, when a problem is found, it will be locked and the update will fail]
//乐观锁:1.先查询,获得版本号 version = 1
--A
update user set name = "kuangshen" ,version = version + 1
where id = 2 and version = 1
--B 线程抢先完成,这个时候 version = 2,会导致 A 修改失败!
update user set name = "kuangshen" ,version = version + 1
where id = 2 and version = 1

Test the optimistic lock plugin of Mybatis-Plus

  1. Add a version field to the database!
    insert image description here
  2. Our entity class needs to add corresponding fields
	@Version//乐观锁Version注解
	private Integer version;
  1. register component
//扫描我们的mapper文件夹
@MapperScan("com.kuang.mapper")
@EnableTransactionManagement//自动管理事务开启,默认也是开启的
@Configuration//这是一个配置类
public class MybatisPlusConfig {
    
    
    //注册乐观锁插件
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
    
    
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }
}
  1. have a test
    //测试乐观锁成功
    @Test
    public void testOptimisticLocker(){
    
    
        //1.查询
        User user = userMapper.selectById(1L);
        //2.修改,它很乐观它觉得这个用户没有问题,,进行修改
        user.setName("呀嘿");
        user.setEmail("[email protected]");
        user.setAge(2);
        //3.执行更新操作
        userMapper.updateById(user);
    }

    //测试乐观锁失败
    @Test
    public void testOptimisticLocker2(){
    
    

        //线程一
        User user = userMapper.selectById(1L);
        user.setName("yiyi嘿11");
        user.setEmail("[email protected]");

        //模拟另外一个线程执行了插队操作
        User user2 = userMapper.selectById(1L);
        user2.setName("yi嘿哈22");
        user2.setEmail("[email protected]");

        userMapper.updateById(user2);
		//自旋锁来多次尝试提交!
        userMapper.updateById(user);//如果没有乐观锁就会覆盖插队线程的值!
    }

Spinlock (extended)

"Spin" means that you keep looping here until the goal is achieved. Unlike ordinary locks, if the lock cannot be acquired, it will enter blocking

query operation

    //测试批量查询!
    @Test
    public void testSelectByBatchId(){
    
    
        List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
        users.forEach(System.out::println);
    }

    //按条件查询之一使用 map 操作
    @Test
    public void testSelectByBatchIds() {
    
    
        HashMap<String, Object> map = new HashMap<>();
        //自定义要查询
        map.put("name","Jack");
        List<User> users = userMapper.selectByMap(map);
        users.forEach(System.out::println);
    }

Paging query

Pagination is used a lot on the website!

  1. The original limit for pagination
  2. pageHelper third-party plugin
  3. In fact, MP also has built-in pagination plug-ins!

how to use!

  1. Just configure the interceptor component
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
    
    
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();    
        //分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
        //乐观锁插件
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }
  1. Just use the Page object directly!
    //测试分页查询
    @Test
    public void testPage(){
    
    
        //参数一:当前页
        //参数二:页面大小
        //使用了分页插件后,所有的分页操作也变得简单了
        IPage page = new Page<>(2, 5);
        userMapper.selectPage(page,null);
        page.getRecords().forEach(System.out::println);
        System.out.println(page.getTotal());
    }

delete operation

Basic delete operation

//测试删除
    //通过id
    @Test
    public void testDeleteById(){
    
    
        userMapper.deleteById(1518117539816615937L);
    }

    //通过id批量删除
    @Test
    public void testDeleteBatchIds(){
    
    
        userMapper.deleteBatchIds(Arrays.asList(1518117539816615938L,8L));
    }

    //通过map删除
    @Test
    public void testDeleteMap(){
    
    
        HashMap<String, Object> map = new HashMap<>();
        map.put("name","yi嘿哈22");
        userMapper.deleteByMap(map);
    }

We will encounter some problems at work: logical deletion!

Tombstone

Physical deletion: directly removed from the database
Logical deletion: not removed from the database, but invalidated by a variable! delete = 0>>delete=1

Administrators can view deleted records! Prevent data loss, similar to recycle bin!
have a test:

  1. Add a delete field to the data table
    insert image description here
  2. Add attribute to entity class
    @TableLogic//逻辑删除
    private Integer deleted;
  1. configuration!
    Example: application.yml
mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: flag  # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不进行@TableLogic注解)
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
  1. Test the deletion.
    The update operation is performed instead of the delete operation.
    insert image description here
    The data is not deleted, but the value of the deleted field is marked as 1.
    insert image description here
    When querying, deleted will be automatically spliced, and the condition of deleted=0 will be added.
    insert image description here

CRUD summary

We must be proficient in all the above CRUD operations and extremely extended operations! It will greatly improve the efficiency of your work and writing projects!
Note: The above operations are for reference only. According to different versions and faster updates, the content is mainly based on the official website of Mybatis-Plus, and its content is also easy to read.

Performance Analysis Plugin

Function: performance analysis interceptor, used to output each SQL statement and its execution time.
If it exceeds this time, it will stop running.
Since this version of MP is relatively new, the official website has no MP performance analysis plug-in, so a third-party p6spy plug-in is used here

1.导入pom相关依赖
<!--mybatis-plus-->
<dependency>
	<groupId>com.baomidou</groupId>
	<artifactId>mybatis-plus-boot-starter</artifactId>
	<version>3.5.1</version>
</dependency>
<!--执行 SQL 分析打印插件-->
<dependency>
	<groupId>p6spy</groupId>
	<artifactId>p6spy</artifactId>
	<version>3.8.1</version>
</dependency>
<!--mybatis-plus代码生成器-->
<dependency>
	<groupId>com.baomidou</groupId>
	<artifactId>mybatis-plus-generator</artifactId>
	<version>3.5.1</version>
</dependency>
 
2.修改 application.yml
### 默认配置如下
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatis_plus?useUnicode=true&characterEncoding=utf8&useSSL=false&useLegacyDatetimeCode=false&serverTimezone=GMT%2b8&tinyInt1isBit=true
    username: root
    password: 123456
### 修改配置如下
spring:
  datasource:
  driver-class-name: com.p6spy.engine.spy.P6SpyDriver
  url: jdbc:p6spy:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8&useSSL=false&useLegacyDatetimeCode=false&serverTimezone=GMT%2b8&tinyInt1isBit=true
  
3.在项目的resources下添加"spy.properties" p6spy的位置文件
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
# 自定义日志打印
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
#日志输出到控制台
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
# 使用日志系统记录 sql
#appender=com.p6spy.engine.spy.appender.Slf4JLogger
# 设置 p6spy driver 代理
deregisterdrivers=true
# 取消JDBC URL前缀
useprefix=true
# 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
excludecategories=info,debug,result,commit,resultset
# 日期格式
dateformat=yyyy-MM-dd HH:mm:ss
# 实际驱动可多个
#driverlist=org.h2.Driver
# 是否开启慢SQL记录
outagedetection=true
# 慢SQL记录标准 2 秒
outagedetectioninterval=2
 
4.执行查询接口
控制台会显示如下代码
 Consume Time:20 ms 2020-06-30 09:26:25
 Execute SQL:SELECT ID, NAME, AGE, SEX, date_format( INPUT_DATE, '%Y-%c-%d %h:%i:%s' ) inputDate FROM student where 1=1 LIMIT 10 

Using performance analysis plug-ins can help us improve efficiency!

conditional constructor

It is very important.
We can use it to replace 1. Test 1 by writing some complex SQL
. Remember to check the output SQL for analysis.

@Test
void contextLoads(){
    
    
    //查询name不为空,邮箱不为空的用户,年龄大于等于24
    QueryWrapper<User> wrapper = new QueryWrapper<>();//和mapper很像,但是wrapper是一个对象,不用put,而是去用方法
    wrapper
            .isNotNull("name")
            .isNotNull("email")
            .ge("age",24);
    userMapper.selectList(wrapper).forEach(System.out::println);
}

insert image description here
2. Test 2, remember to view the output SQL for analysis

@Test
void contextLoads2(){
    
    
     //查询名字为Tom的
     QueryWrapper<User> wrapper = new QueryWrapper<>();
     wrapper.eq("name","Tom");
     User user = userMapper.selectOne(wrapper);//查询一个数据,出现多个结果使用List或者Map
     System.out.println(user);
 }

3. Test 3, remember to view the output SQL for analysis

@Test
void contextLoads3(){
    
    
    //查询年龄在20-30之间的用户
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.between("age",20,30);//区间,不包括20和30
    Long count = userMapper.selectCount(wrapper);//查询结果数量
    System.out.println(count);
}

4. Test 4, remember to view the output SQL for analysis

//模糊查询
@Test
void contextLoads4(){
    
    
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    //t在% 左和右
    wrapper
            .notLike("name","a")//名字中没有a和A
            .likeRight("email","t");//邮箱以字母t开头
    userMapper.selectMaps(wrapper).forEach(System.out::println);
}

5. Test five, remember to view the output SQL for analysis

@Test
void contextLoads5(){
    
    
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    // 在子查询中查出来
    wrapper.inSql("name","select name from user where id < 5");
    List<Object> objects = userMapper.selectObjs(wrapper);
    objects.forEach(System.out::println);
}

6. Test Six

@Test
void contextLoads6(){
    
    
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    //通过id进行降序排序 ASC升序
    wrapper.orderByDesc("id");
    List<User> users = userMapper.selectList(wrapper);
    users.forEach(System.out::println);
}

automatic code generator

dao, pojo, service, controller are all written automatically!
1. Write dependencies first

<!--代码中设置freemarker了模板,故在此加依赖,默认模板为velocity模板-->
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.31</version>
</dependency>
<!--mybatis-plus代码生成器-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.5.1</version>
</dependency>

2. Run the code

package com.kuang;

import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert;
import com.baomidou.mybatisplus.generator.config.querys.MySqlQuery;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.baomidou.mybatisplus.generator.keywords.MySqlKeyWordsHandler;

import java.util.Collections;

public class lqc {
    
    
    public static void main(String[] args) {
    
    
        String projectPath = System.getProperty("user.dir");//user.dir	用户当前工作目录
        FastAutoGenerator.create("jdbc:p6spy:mysql://localhost:3306/mybatis_plus?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8", "root", "123456")
                //1.全局配置
                .globalConfig(builder -> {
    
    
                    builder.author("lqc") // 设置作者
                            .enableSwagger() // 开启 swagger 模式
                            .fileOverride() // 覆盖已生成文件
                            .outputDir(projectPath+"/src/main/java");// 指定输出目录

                })
                //3.包的配置
                .packageConfig(builder -> {
    
    
                    builder.parent("com.kuang") // 设置父包名
                            .moduleName("blog") // 设置父包模块名
                            .pathInfo(Collections.singletonMap(OutputFile.mapperXml, projectPath+"/src/main/java")); // 设置mapperXml生成路径
                })
                //4.策略配置
                .strategyConfig(builder -> {
    
    
                    builder.addInclude("student") // 设置需要生成的表名
                            .addTablePrefix("t_", "c_"); // 设置过滤表前缀
                })
                .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
                .execute();
        //2.数据库的配置
        new DataSourceConfig.
                Builder("jdbc:p6spy:mysql://localhost:3306/mybatis_plus?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8","root","123456")
                .dbQuery(new MySqlQuery())
                .schema("mybatis_plus")
                .typeConvert(new MySqlTypeConvert())
                .keyWordsHandler(new MySqlKeyWordsHandler())
                .build();
    }
}

3. The file is automatically
insert image description here
generated

–Most of the content comes from Kuangshenshuo JAVA

Guess you like

Origin blog.csdn.net/qq_55293923/article/details/124382040