MyBatis-Plus的一些高级干货
MyBatis-Plus的一些高级干货
MyBatis Plus(简称 MP)是一个 MyBatis 的增强版,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
记住:
mp官方文档:https://mp.baomidou.com/guide/
1.MyBatis-Plus与MyBatis的区别
- 对于实体类而言,需要加一定的注解。
- MyBatis-Plus通过让实体类名与表名关联,与表字段关联。
- 通用性不同,MyBatis是外国人开发的,国内外皆通用,而MyBatis-Plus是由国内人士自发组织的开源,目前主要通用于国内;
- ,
注意:mybatis-plus会自动维护mybatis以及mybatis-spring的依赖,所以不需要引入后两者,避免发生版本冲突. 只需把mybatis的依赖换成mybatis-plus的依赖。
2.全局唯一id的生成
Mybatis plus 在调用插入方法时,会自动生成id,是一个全局唯一id,一起插入到数据表中。
mp默认新增的时候,产生的是全局唯一id,采用雪花算法生成,自3.3.0开始,默认使用雪花算法+UUID。(Twitter的snowflake算法又名雪花算法,生成long类型的唯一数字)
如图:
而这个唯一id的生成,常常用来做我们的数据库字段的主键~!
3.主键生成策略:
在日常开发中,对于一些数据库中的主键,我们一般会采用 等方式,
具体对比:分布式系统唯一ID生成方案。
mp自3.3.0开始,默认使用雪花算法+UUID(不含中划线)。
主键生成策略必须使用INPUT。
设置生成的主键是自增样式:
(在mp生成id时,根据上一个id的数值,而自增)
实现过程:
3.1实体类上添加注解:
@TableId(type = IdType.INPUT)
private Long id;
如图:
3.2数据库字段设置为自增
结果:
Mp中其他的id属性的解释:
一旦手动输入id之后,就需要自己配置id生成方法了!不然就可以用这些注解来生成id自动填入数据库。
4.自动填充:
开发中,一般数据库总有一些字段我们想要的是。比如创建时间,修改时间等字段!这些个操作一般都是自动化完成的,企业级都不希望手动更新。
(在阿里巴巴开发手册中就有写:所有的数据库表gmt_create,gmt_modified 字段几乎都要有,而自动化填入,便于追踪)
实现方式
4.1 方式一:数据库级别 (工作中不建议)
- 比如在表中新增字段create_time, update_time。只需要设置默认的填充方式即可。
结果:
4.2 方法二:代码级别 (推荐)
-
删除数据库的该字段的默认值,更新操作等设置(上一种方式我设置了,我恢复一下而已)。
(恢复如图下这样) -
添加注解
实体类上加个注解,一个是新增,一个是更新。// 设置填充字段的填充类型 @TableField(fill = FieldFill.INSERT) private LocalDateTime createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private LocalDateTime updateTime;
-
编写处理器来处理这个注解,自定义实现类来实现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);
}
当执行插入或者更新操作时候,自动修改值。
5. 乐观锁:
顾名思义十分乐观,它总是认为程序不会出现问题,无论干什么都不去上锁!如果出了问题,就在测试加锁处理,再次更新值测试。
顾名思义十分悲观,它总是认为程序会出现问题,无论干什么都去上锁!再去操作。
乐观锁原理机制:
乐观锁实现方式:
- 后端取记录数据时,先查询获取当前version版本。
- 更新时,带上这个version
- 执行更新时,set version = newVersion where version = oldVersion
- 如果version不对,就不允许更新,失败操作。
主要适用场景:当要更新一条记录的时候,希望这条记录没有被别人更新。
5.1实现过程:
-
在数据库表中添加版本字段,version int ,设置默认值1.
-
实体类添加版本注解:
@Version //乐观锁注解 private Integer version;
注解 @Version 必须有!
-
自定义一个配置类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(); } }
如图:
- 测试方法:
// 测试乐观锁 >>单线程时,方法可行
@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了。
单线程下:
对应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,加锁。
最后,因为我们有乐观锁存在,所以线程2的更新操作成功了,而线程1的更新则失败了,被限制。
可以尝试自旋锁来尝试多次提交,依然可以避免同时被改写的问题。
6. 逻辑删除
- 物理删除:从数据库直接删除数据。
- :在数据库中没有直接删除,而是通过一个变量让它失效,依旧保存在数据库中,只是不显示了。
应用场景:
- 管理员可以查看被删除的记录,普通用户删除后,数据消失!这样做是防止数据的丢失,类似于回收站功能。
6.1实现过程:
-
比如在数据库表中新增一个字段 deleted。
当(deleted=0 >> deleted=1 )时,表示该数据被删除了。 -
实体类字段上加上@TableLogic注解
@TableLogic private Integer deleted;
说明:
• 字段支持所有数据类型(推荐使用 Integer,Boolean,LocalDateTime)
• 如果使用LocalDateTime,建议逻辑未删除值设置为字符串null,逻辑删除值只支持数据库函数例如now()
-
全局配置-全局逻辑删除(非必须)
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
如图:
注意:
使用此配置则不需要在实体类上添加 @TableLogic。
但如果实体类上有 @TableLogic 则以实体上的为准,忽略这个全局配置。( 即先查找注解再查找全局,都没有则此表没有逻辑删除。)
- 测试方法:
//删除-逻辑删除
@Test
public void testDelById(){
userMapper.deleteById(5L);
}
原理:
效果:
使用mp自带方法删除和查找都会附带逻辑删除功能 (自己写的xml不会)
删除:update user set deleted=1 where id =1 and deleted=0
查找:select * from user where deleted=0
再查询数据时:会自动加入逻辑未删除的标志语句来查。(自己写的SQL则加上这个条件即可。)
如图:
记录依旧存在数据库,但是值已经改变了,所以普通用户查寻不到,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即可。
注意:该插件有性能损耗,不建议生产环境使用,在springboot配置环境为dev或者test环境下使用就好了。
以上为使用 mybatis Plus 框架时的一些开发记录,也适用于以后的项目当中。