MyBatis Plus(简称 MP)是一个 MyBatis 的增强版,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
记住: 对 M y B a t i s 只 做 增 强 不 做 改 变 , 引 入 它 不 会 对 现 有 工 程 产 生 影 响 \color{red}{对MyBatis 只做增强不做改变,引入它不会对现有工程产生影响} 对MyBatis只做增强不做改变,引入它不会对现有工程产生影响
mp官方文档:https://mp.baomidou.com/guide/
注意:mybatis-plus会自动维护mybatis以及mybatis-spring的依赖,所以不需要引入后两者,避免发生版本冲突. 只需把mybatis的依赖换成mybatis-plus的依赖。
Mybatis plus 在调用插入方法时,会自动生成id,是一个全局唯一id,一起插入到数据表中。
mp默认新增的时候,产生的是全局唯一id,采用雪花算法生成,自3.3.0开始,默认使用雪花算法+UUID。(Twitter的snowflake算法又名雪花算法,生成long类型的唯一数字)
如图:
在日常开发中,对于一些数据库中的主键,我们一般会采用 自 增 i d , U U I D , 雪 花 算 法 , r e d i s 生 成 , z o o k e e p e r 生 成 \color{#4285f4}{自增id,}\color{#ea4335}{UUID,}\color{#fbbc05}{雪花算法,}\color{#4285f4}{redis生成,}\color{#34a853}{zookeeper生成}\color{#ea4335}{} 自增id,UUID,雪花算法,redis生成,zookeeper生成 等方式,
具体对比:分布式系统唯一ID生成方案。
mp自3.3.0开始,默认使用雪花算法+UUID(不含中划线)。
主键生成策略必须使用INPUT。
(在mp生成id时,根据上一个id的数值,而自增)
实现过程:
@TableId(type = IdType.INPUT)
private Long id;
Mp中其他的id属性的解释:
一旦手动输入id之后,就需要自己配置id生成方法了!不然就可以用这些注解来生成id自动填入数据库。
开发中,一般数据库总有一些字段我们想要的是 不 需 要 修 改 , 自 动 生 成 的 \color{red}{不需要修改,自动生成的} 不需要修改,自动生成的。比如创建时间,修改时间等字段!这些个操作一般都是自动化完成的,企业级都不希望手动更新。
(在阿里巴巴开发手册中就有写:所有的数据库表gmt_create,gmt_modified 字段几乎都要有,而自动化填入,便于追踪)
实现方式
添加注解
实体类上加个注解,一个是新增,一个是更新。
// 设置填充字段的填充类型
@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);
}
乐 观 锁 : \color{red}{乐观锁:} 乐观锁:顾名思义十分乐观,它总是认为程序不会出现问题,无论干什么都不去上锁!如果出了问题,就在测试加锁处理,再次更新值测试。
悲 观 锁 : \color{red}{悲观锁:} 悲观锁:顾名思义十分悲观,它总是认为程序会出现问题,无论干什么都去上锁!再去操作。
乐观锁原理机制: 有 一 个 V e r s i o n 字 段 , 每 次 会 n e w v e r s i o n , 每 次 操 作 都 带 有 一 个 版 本 号 , 来 相 互 验 证 。 \color{blue}{有一个Version字段 ,每次会new version ,每次操作都带有一个版本号,来相互验证。} 有一个Version字段,每次会newversion,每次操作都带有一个版本号,来相互验证。
乐观锁实现方式:
主要适用场景:当要更新一条记录的时候,希望这条记录没有被别人更新。
注解 @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("[email protected]");
// 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的更新则失败了,被限制。
可以尝试自旋锁来尝试多次提交,依然可以避免同时被改写的问题。
应用场景:
比如在数据库表中新增一个字段 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);
}
原理: 逻 辑 删 除 > > > 调 用 的 是 删 除 方 法 , 但 实 际 执 行 的 是 一 个 更 新 操 作 。 \color{blue}{逻辑删除 >>>调用的是删除方法,但实际执行的是一个更新操作。 } 逻辑删除>>>调用的是删除方法,但实际执行的是一个更新操作。
效果:
使用mp自带方法删除和查找都会附带逻辑删除功能 (自己写的xml不会)
删除:update user set deleted=1 where id =1 and deleted=0
查找:select * from user where deleted=0
再查询数据时:会自动加入逻辑未删除的标志语句来查。(自己写的SQL则加上这个条件即可。)
如图:
记录依旧存在数据库,但是值已经改变了,所以普通用户查寻不到,admin可以。
在平时的开发中,会遇到一些慢SQL,一般是进行测试,druid监控等来找谁慢。MP提供自有插件来分析慢SQL ,如果超过设定时间,就停止运行。
(该插件 MP3.2.0 以上版本移除推荐使用第三方扩展p6spy ,执行SQL分析打印功能)
该功能依赖 p6spy 组件,完美的输出打印 SQL 及执行时长 ,MP 3.1.0 以上版本使用。
p6spy 依赖引入:
<dependency>
<groupId>p6spygroupId>
<artifactId>p6spyartifactId>
<version>3.8.7version>
dependency>
作用:用于输出每条 SQL 语句及其执行时间。
效果:SQL会格式化,且出现执行时间,如果超过指定时间,会提醒报错,自己修改优化SQL即可。
注意:该插件有性能损耗,不建议生产环境使用,在springboot配置环境为dev或者test环境下使用就好了。
以上为使用 mybatis Plus 框架时的一些开发记录,也适用于以后的项目当中。