一文带你掌握MyBatis-Plus的plus高级功能点如何使用

作者:微信小助手

发布时间:2023-11-18T14:38:55

今天接着之前总结的入门教程分析:MyBatis-Plus最详细的入门教程,首先还是同样地需要准备一张表tb_user:

CREATE TABLE `tb_user` (
  `id` bigint(20NOT NULL AUTO_INCREMENT COMMENT '主键',
  `user_no` varchar(255NOT NULL COMMENT '编号',
  `nickname` varchar(255DEFAULT NULL COMMENT '昵称',
  `email` varchar(255DEFAULT NULL COMMENT '邮箱',
  `phone` varchar(255NOT NULL COMMENT '手机号',
  `gender` tinyint(4NOT NULL DEFAULT '0' COMMENT '性别  0:男生   1:女生',
  `birthday` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '出生日期',
  `is_delete` tinyint(4NOT NULL DEFAULT '0' COMMENT '删除标志 0:否  1:是',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  `create_by` bigint(20DEFAULT NULL COMMENT '创建人',
  `update_by` bigint(20DEFAULT NULL COMMENT '更新人',
  `address` varchar(1024DEFAULT NULL COMMENT '地址',
  PRIMARY KEY (`id`USING BTREE
ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;

在项目服务中对应的实体类User:

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@TableName(value = "tb_user")
public class User {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String userNo;
    private String nickname;
    private String email;
    private String phone;
    private Integer gender;
    private Date birthday;
    private Integer isDelete;
    private Date createTime;
    private Date updateTime;
}

下面高级功能的示例都是基于上面的表进行展开的。

1.批量插入

我们在日常开发中知道大批量插入数据可能造成性能瓶颈,所以需要格外关注。在之前的入门教程讲过mp(MyBatis-Plus简称,下文都用简称)对数据库的CRUD操作提供了service层和mapper层的接口方法封装,两者的一大区别就是service CRUD接口提供了批量保存的操作,下面就分别来看看批量保存1000条user数据,然后对执行时间进行统计对比和性能评估,这里我连接的数据库是4核8G10M轻量服务器部署的,不同的数据库服务环境配置,执行效率是不一样的,所以下面的执行时间仅供对比参考。

mapper层的CRUD接口

    /**
     * mapper层的crud接口方法批量插入
     */

    @Test
    public void testMapperBatchAdd() {
        List<User> users = new ArrayList<>();
        for(long i = 1; i <= 1000; i++) {
            User user = User.builder()
                    .id(i)
                    .userNo("No-" + i)
                    .nickname("哈哈")
                    .phone("12345678901")
                    .email("shepherd_123@qq.com")
                    .birthday(new Date())
                    .gender(0)
                    .isDelete(0)
                    .build();
            users.add(user);
        }
        long start = System.currentTimeMillis();
        users.forEach(user -> {
            userDAO.insert(user);
        });
        long end = System.currentTimeMillis();
        System.out.println("执行时长:" + (end-start) + "毫秒");
    }

控制台输出如下:

执行时长:42516毫秒

service层CRUD接口

    /**
     * service层的crud接口方法批量插入
     */

    @Test
    public void testServiceBatchAdd() {
        List<User> users = new ArrayList<>();
        for(long i = 1; i <= 1000; i++) {
            User user = User.builder()
                    .id(i)
                    .userNo("No-" + i)
                    .nickname("哈哈")
                    .phone("12345678901")
                    .email("shepherd_123@qq.com")
                    .birthday(new Date())
                    .gender(0)
                    .isDelete(0)
                    .build();
            users.add(user);
        }
        long start = System.currentTimeMillis();
        userService.saveBatch(users);
        long end = System.currentTimeMillis();
        System.out.println("执行时长:" + (end-start) + "毫秒");
    }

执行结果:

执行时长:19385毫秒

可以看出,使用service层提供的批量保存接口userService.saveBatch(users)虽然快了很多,却仍需要19s,耗时比起mapper层的一条一条地插入快了23s,但是对于服务接口响应来说还是不可接受的。那为什么批量插入保存还是比较慢呢?

MySQL 的 JDBC 连接的 url 中要加 rewriteBatchedStatements 参数,并保证 5.1.13 以上版本的驱动,才能实现高性能的批量插入。

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: root
    url: jdbc:mysql://ip:3306/db_test?&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF8&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true&rewriteBatchedStatements=true

再次执行上面的service层crud接口批量插入方法,执行结果如下:

执行时长:1364毫秒

可以看到 jdbcurl 添加了 rewriteBatchedStatements=true 参数后,批量操作的执行耗时已经只有 1364 毫秒快的飞起,具体缘由分析请看:https://juejin.cn/post/7295688187752562751

2.逻辑删除

现今互联网系统数据安全越发重要,逻辑删表是指在删除表中数据时,并不是直接将数据从表中删除,而是将数据的状态标记为已删除。这种方式被称为逻辑删除,与之相对的是物理删除。逻辑删除可以保留数据的完整性,同时也方便数据恢复。

添加配置如下:

mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: isDelete # 全局逻辑删除的实体字段名
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)

测试样例:

    /**
     * 逻辑删除
     */

    @Test
    public void testLogicDelete() {
        userDAO.deleteById(1L);
    }

控制台输出如下:

2023-11-16 17:01:47.656 DEBUG 10649 --- [           main] c.s.m.demo.dao.UserDAO.deleteById        : ==>  Preparing: UPDATE tb_user SET is_delete=1 WHERE id=? AND is_delete=0
2023-11-16 17:01:47.702 DEBUG 10649 --- [           main] c.s.m.demo.dao.UserDAO.deleteById        : ==> Parameters: 1(Long)
2023-11-16 17:01:47.744 DEBUG 10649 --- [           main] c.s.m.demo.dao.UserDAO.deleteById        : <==    Updates: 1

从日志可以看出做了更新,这就是逻辑删除,接�