欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

MyBatis-Plus的一些高级干货

程序员文章站 2022-07-13 09:55:44
...

MyBatis-Plus的一些高级干货

MyBatis Plus(简称 MP)是一个 MyBatis 的增强版,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

记住:MyBatis\color{red}{对MyBatis 只做增强不做改变,引入它不会对现有工程产生影响}

mp官方文档:https://mp.baomidou.com/guide/

1.MyBatis-Plus与MyBatis的区别

  1. 对于实体类而言,需要加一定的注解。
  2. MyBatis-Plus通过@TableName\color{blue}{@TableName}让实体类名与表名关联,@TableField\color{blue}{@TableField}与表字段关联。
  3. 通用性不同,MyBatis是外国人开发的,国内外皆通用,而MyBatis-Plus是由国内人士自发组织的开源,目前主要通用于国内;
  4. MyBatisPlusibatismybatis\color{green}{MyBatisPlus属于第三方扩展,学习一般都是学习基础,比如ibatis或者mybatis}\color{green}{而扩展是第三方提供的更高级面向开放的支持,这两者不能作为比较。}

注意:mybatis-plus会自动维护mybatis以及mybatis-spring的依赖,所以不需要引入后两者,避免发生版本冲突. 只需把mybatis的依赖换成mybatis-plus的依赖。

2.全局唯一id的生成

Mybatis plus 在调用插入方法时,会自动生成id,是一个全局唯一id,一起插入到数据表中。

mp默认新增的时候,产生的是全局唯一id,采用雪花算法生成,自3.3.0开始,默认使用雪花算法+UUID。(Twitter的snowflake算法又名雪花算法,生成long类型的唯一数字)

如图:

MyBatis-Plus的一些高级干货
而这个唯一id的生成,常常用来做我们的数据库字段的主键~!

3.主键生成策略:

在日常开发中,对于一些数据库中的主键,我们一般会采用 idUUIDrediszookeeper\color{#4285f4}{自增id,}\color{#ea4335}{UUID,}\color{#fbbc05}{雪花算法,}\color{#4285f4}{redis生成,}\color{#34a853}{zookeeper生成}\color{#ea4335}{} 等方式,

具体对比:分布式系统唯一ID生成方案

mp自3.3.0开始,默认使用雪花算法+UUID(不含中划线)。
主键生成策略必须使用INPUT。

设置生成的主键是自增样式:

(在mp生成id时,根据上一个id的数值,而自增)

实现过程:

3.1实体类上添加注解:

@TableId(type = IdType.INPUT) 
private Long id;

如图:
MyBatis-Plus的一些高级干货

3.2数据库字段设置为自增

MyBatis-Plus的一些高级干货

结果:
MyBatis-Plus的一些高级干货

Mp中其他的id属性的解释:
MyBatis-Plus的一些高级干货
一旦手动输入id之后,就需要自己配置id生成方法了!不然就可以用这些注解来生成id自动填入数据库。

4.自动填充:

开发中,一般数据库总有一些字段我们想要的是\color{red}{不需要修改,自动生成的}。比如创建时间,修改时间等字段!这些个操作一般都是自动化完成的,企业级都不希望手动更新。

(在阿里巴巴开发手册中就有写:所有的数据库表gmt_create,gmt_modified 字段几乎都要有,而自动化填入,便于追踪)

实现方式

4.1 方式一:数据库级别 (工作中不建议)

  1. 比如在表中新增字段create_time, update_time。只需要设置默认的填充方式即可。
    MyBatis-Plus的一些高级干货

结果:
MyBatis-Plus的一些高级干货

4.2 方法二:代码级别 (推荐)

  1. 删除数据库的该字段的默认值,更新操作等设置(上一种方式我设置了,我恢复一下而已)。
    (恢复如图下这样)
    MyBatis-Plus的一些高级干货

  2. 添加注解
    实体类上加个注解,一个是新增,一个是更新。

    // 设置填充字段的填充类型
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
    
  3. 编写处理器来处理这个注解,自定义实现类来实现implements MetaObjectHandler()

    配置类源码如下:

    @Slf4j
    @Component   // 一定不要忘记把处理器加入到IOC容器中。
    public class MyMetaObjectHandler implements MetaObjectHandler {
    
    // 插入时候的填充策略
    	@Override
        public void insertFill(MetaObject metaObject) {
            log.info("start insert fill ....");
            this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); //版本 3.3.0(推荐使用)
                       }
    // 更新时间的填充策略
        @Override
        public void updateFill(MetaObject metaObject) {
            log.info("start update fill ....");
            this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐使用)
    	}
    }
    

程序会通过反射去读取实体类中对应的注解,填入内容。

注意事项:
• 字段必须声明TableField注解,属性fill选择对应策略,该声明告知MybatisPlus需要预留注入SQL字段。
• 填充处理器MyMetaObjectHandler在SpringBoot中需要声明@Component注解或@Bean注入。

填充失效问题解决
把date 改为了LocalDateTime 类型,依然对应的是SQL中的datetiem或date类型。

结果:

  @Test
    public void testInsert(){
        User user =new User();
        user.setAge(23);
        int result = userMapper.insert(user); //自动生成id
        System.out.println(result);
    }

当执行插入或者更新操作时候,自动修改值。
MyBatis-Plus的一些高级干货

5. 乐观锁:

\color{red}{乐观锁:}顾名思义十分乐观,它总是认为程序不会出现问题,无论干什么都不去上锁!如果出了问题,就在测试加锁处理,再次更新值测试。

\color{red}{悲观锁:}顾名思义十分悲观,它总是认为程序会出现问题,无论干什么都去上锁!再去操作。

乐观锁原理机制:Versionnewversion\color{blue}{有一个Version字段 ,每次会new version ,每次操作都带有一个版本号,来相互验证。}

乐观锁实现方式:

  • 后端取记录数据时,先查询获取当前version版本。
  • 更新时,带上这个version
  • 执行更新时,set version = newVersion where version = oldVersion
  • 如果version不对,就不允许更新,失败操作。

主要适用场景:当要更新一条记录的时候,希望这条记录没有被别人更新。

5.1实现过程:

  1. 在数据库表中添加版本字段,version int ,设置默认值1.
    MyBatis-Plus的一些高级干货

  2. 实体类添加版本注解:

    @Version  //乐观锁注解
    private Integer version;
    

注解 @Version 必须有!

  1. 自定义一个配置类mpConfig:
    标识为mybatis配置类,注册乐观锁插件即可,

    源码如下:

    package com.zout.mapper;
    /**
     * @Description:
     * @Author: Zoutao
     * @Date: 2020/5/16
     */
    
    import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.transaction.annotation.EnableTransactionManagement;
    
    @EnableTransactionManagement
    @Configuration  //标示为mybatis配置类
    public class mpConfig {
        // 注册乐观锁插件
        @Bean
        public OptimisticLockerInterceptor optimisticLockerInterceptor() {
            return new OptimisticLockerInterceptor();
        }
    }
    

如图:

MyBatis-Plus的一些高级干货

  1. 测试方法:
    // 测试乐观锁 >>单线程时,方法可行
    @Test
    public void testInterceptor(){
        // 1.查询用户信息
        User user = userMapper.selectById(4L); 
        // 2.修改用户信息
        user.setName("王五");
        user.setAge(500);
        user.setEmail("aaa@qq.com");
        // 3. 执行更新操作
        int result = userMapper.updateById(user); 
        System.out.println(result);
    }

过程剖析:
首先查询version=1,然后更新以后,version=2了。

单线程下:
MyBatis-Plus的一些高级干货

对应SQL原理

update user set name = '王五',version = 2 where id = 100 and version = 1

结果,版本正常,更新成功。

多线程下:

    // 测试乐观锁 >> 多线程下,发现方法不可行
    @Test
    public void testInterceptor2(){
        // 模拟线程1
        User user1 = userMapper.selectById(4L);
        user1.setName("陈二");
        user1.setAge(500);
        // 模拟线程2,执行插队操作。跟线程1互相抢夺
        User user2 = userMapper.selectById(4L);
        user2.setName("王二麻子");
        user2.setAge(800);
        userMapper.updateById(user2); // 模拟线程2抢先操作
        // 执行更新操作
        userMapper.updateById(user1); //如果没有乐观锁,就会覆盖插队线程2的值。有乐观锁,则更新失败。
    }

发现两个线程都拿到了版本2,加锁。

MyBatis-Plus的一些高级干货

最后,因为我们有乐观锁存在,所以线程2的更新操作成功了,而线程1的更新则失败了,被限制。

MyBatis-Plus的一些高级干货
可以尝试自旋锁来尝试多次提交,依然可以避免同时被改写的问题。

6. 逻辑删除

  • 物理删除:从数据库直接删除数据。
  • \color{green}{逻辑删除}:在数据库中没有直接删除,而是通过一个变量让它失效,依旧保存在数据库中,只是不显示了。

应用场景:

  • 管理员可以查看被删除的记录,普通用户删除后,数据消失!这样做是防止数据的丢失,类似于回收站功能。

6.1实现过程:

  1. 比如在数据库表中新增一个字段 deleted。
    MyBatis-Plus的一些高级干货
    当(deleted=0 >> deleted=1 )时,表示该数据被删除了。

  2. 实体类字段上加上@TableLogic注解

    @TableLogic
    private Integer deleted;
    

说明:
• 字段支持所有数据类型(推荐使用 Integer,Boolean,LocalDateTime)
• 如果使用LocalDateTime,建议逻辑未删除值设置为字符串null,逻辑删除值只支持数据库函数例如now()

  1. 全局配置-全局逻辑删除(非必须)
    mp,3.1.1版本以上使用,0未删除,1已删除。

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

如图:
MyBatis-Plus的一些高级干货
注意:

使用此配置则不需要在实体类上添加 @TableLogic。
但如果实体类上有 @TableLogic 则以实体上的为准,忽略这个全局配置。( 即先查找注解再查找全局,都没有则此表没有逻辑删除。)

  1. 测试方法:
    //删除-逻辑删除
    @Test
    public void testDelById(){
        userMapper.deleteById(5L);
    }

原理:>>>\color{blue}{逻辑删除 >>>调用的是删除方法,但实际执行的是一个更新操作。 }
MyBatis-Plus的一些高级干货

效果:

MyBatis-Plus的一些高级干货

使用mp自带方法删除和查找都会附带逻辑删除功能 (自己写的xml不会)
删除:update user set deleted=1 where id =1 and deleted=0
查找:select * from user where deleted=0

再查询数据时:会自动加入逻辑未删除的标志语句来查。(自己写的SQL则加上这个条件即可。)
如图:
MyBatis-Plus的一些高级干货
记录依旧存在数据库,但是值已经改变了,所以普通用户查寻不到,admin可以。

7. 性能分析-执行 SQL 分析打印

在平时的开发中,会遇到一些慢SQL,一般是进行测试,druid监控等来找谁慢。MP提供自有插件来分析慢SQL ,如果超过设定时间,就停止运行。

(该插件 MP3.2.0 以上版本移除推荐使用第三方扩展p6spy ,执行SQL分析打印功能)

该功能依赖 p6spy 组件,完美的输出打印 SQL 及执行时长 ,MP 3.1.0 以上版本使用。

p6spy 依赖引入:

<!-- https://mvnrepository.com/artifact/p6spy/p6spy -->
<dependency>
    <groupId>p6spy</groupId>
    <artifactId>p6spy</artifactId>
    <version>3.8.7</version>
</dependency>

作用:用于输出每条 SQL 语句及其执行时间。

效果:SQL会格式化,且出现执行时间,如果超过指定时间,会提醒报错,自己修改优化SQL即可。

MyBatis-Plus的一些高级干货

注意:该插件有性能损耗,不建议生产环境使用,在springboot配置环境为dev或者test环境下使用就好了。


以上为使用 mybatis Plus 框架时的一些开发记录,也适用于以后的项目当中。