mybatis 乐观锁_MybatisPlus新特性之逻辑删除、乐观锁、自动填充
MP特性
公共字段的自动填充功能
自动更新全局属性,比如创建的时间修改的时间,这样就不用每执行一次插入更新操作都带上一个set大大节省了很多效率,从而也避免为了因为时间格式的不统一问题。
为了输出日志到控制台引入日志的依赖:
org.slf4j slf4j-api 1.7.25org.slf4j slf4j-log4j12 1.7.25
下面是两种更新的操作方式:
- 传统的通过插入时间操作的:
private Date createTime; private Date updateTime; user.setCreateTime(new Date()); user.setUpdateTime(new Date());
- 现代的通过配置全局字段来进行自动更新操作(MyBatis-Plus的特性-自动填充)
官方文档:https://baomidou.gitee.io/mybatis-plus-doc/#/auto-fill
1.实现接口:com.baomidou.mybatisplus.mapper.IMetaObjectHandler实现方法
public class MyMetaObjectHandler implements MetaObjectHandler { //插入时的时间填充 @Override public void insertFill(MetaObject metaObject) { } //更新时的时间填充 @Override public void updateFill(MetaObject metaObject) { } }
2.注解填充字段 @TableField(.. fill = FieldFill.INSERT) 生成器策略部分也可以配置!
// 注意!这里需要标记为填充字段 @TableField(.. fill = FieldFill.INSERT) private String fillField;
3.完整代码
@Slf4j//log打印日志,>>>>放弃你的System.out.println() @Component//申明是一个配置类 public class MyMetaObjectHandler implements MetaObjectHandler { //插入时的时间填充 @Override public void insertFill(MetaObject metaObject) { log.info("start insert Fill"); this.fillStrategy(metaObject,"createTime",new Date()); this.fillStrategy(metaObject,"updateTime",new Date()); } //更新时的时间填充 @Override public void updateFill(MetaObject metaObject) { log.info("start update Fill"); this.fillStrategy(metaObject,"updateTime",new Date()); //下面的setFieldValByName已经过时 //this.setFieldValByName("updateTime",new Date(), metaObject); } }
测试:
@Test public void insertUser(){ System.out.println(("----- insertUser method test ------")); User user = new User(); user.setName("自动填充"); user.setEmail("自动填充p@email.com"); user.setPassword("自动填充"); user.setPhoneNum("12341234123"); int num = userMapper.insert(user); if (num>0){ System.out.println("插入成功!!!"); }} @Test public void updateUser(){ System.out.println(("----- updateUser method test ------") ); User user = new User(); user.setId(6); user.setEmail("mp@email.com"); user.setPassword("修改mp123"); int num = userMapper.updateById(user); if (num>0){ System.out.println("修改成功!!!"); } }
此时在数据库中查看会发现在没有set creatTime和updateTime依然能正常操作。
乐观锁
那么说到乐观了肯定就会想到悲观,那么你想对了,还有悲观锁,简单描述一下两种锁:
- 乐观锁:乐观锁( Optimistic Locking)其实是一种思想。相对悲观锁而言,乐观锁假设认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让返回用户错误的信息,让用户决定如何去做。上面提到的乐观锁的概念中其实已经阐述了他的具体实现细节:主要就是两个步骤:冲突检测和数据更新。其实现方式有一种比较典型的就是Compare and Swap(CAS)。
- 悲观锁:总是假设最坏的情况,每次取数据时都认为其他线程会修改,所以都会加锁(读锁、写锁、行锁等),当其他线程想要访问数据时,都需要阻塞挂起。可以依靠数据库实现,如行锁、读锁和写锁等,都是在操作之前加锁,在Java中,synchronized的思想也是悲观锁。这样注定会有一个问题就是性能损耗,所以很少用。
这样就延申出了CAS的一个ABA的问题,简述一下:
当A和B同时操作一个数据,而B的线程抢先一步完成,那么A在修改,这样造成数据不统一。
下面就是解决这个问题的办法。
适应场景
意图:
当要更新一条记录的时候,希望这条记录没有被别人更新
乐观锁实现方式:
- 取出记录时,获取当前version
- 更新时,带上这个version
- 执行更新时, set version = yourVersion+1 where version = yourVersion
- 如果version不对,就更新失败
乐观锁配置
1.配置插件
spring xml配置
spring boot配置
@Bean public OptimisticLockerInterceptor optimisticLockerInterceptor() { return new OptimisticLockerInterceptor(); }
完整代码:
@MapperScan("com.cms.mapper")//扫描Mapper文件 @EnableTransactionManagement//事务注解 @Configuration//申明此类为配置类 public class MyBatisPlusConfig { //配置乐观锁插件 @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor() { return new OptimisticLockerInterceptor(); } }
2.注解实体字段 @Version
public class User { @Version private Integer version; }
特别说明:仅支持int,Integer,long,Long,Date,Timestamp
3.数据库中也增加相应的字段。
示例
如果系统没有并发的话那就不考虑乐观锁了,单点测试(一条一条数据的操作)就忽略,事实上系统中并发是必然存在的
单线程测试
@Test public void OptimisticLockerInterceptorTest(){ System.out.println(("----- OptimisticLockerInterceptorTest method test ------")); User user = new User(); user = userMapper.selectById(3); user.setName("乐观"); user.setEmail("乐观@email.com"); user.setPassword("乐观"); int num = userMapper.updateById(user); if (num>0){ System.out.println("修改成功!!!"); } }
多线程测试
- 没有加锁
@Test public void NoOptimisticLockerInterceptorTest(){ System.out.println(("----- NoOptimisticLockerInterceptorTest method test ------")); User user1 = userMapper.selectById(3); user1.setName("乐观1"); user1.setEmail("乐观1@email.com"); user1.setPassword("乐观1"); User user2 = userMapper.selectById(3); user2.setName("乐观2"); user2.setEmail("乐观2@email.com"); user2.setPassword("乐观2"); //模拟线程2先执行 --此时我们先去掉乐观锁的注解 /* //@Version private Integer version; */ userMapper.updateById(user2); userMapper.updateById(user1);}
数据库中:
此时会发现:user1会覆盖user2的值。
- 加锁
@Test public void LockOptimisticLockerInterceptorTest(){ System.out.println(("----- LockOptimisticLockerInterceptorTest method test ------")); User user1 = userMapper.selectById(3); user1.setName("乐观1"); user1.setEmail("乐观1@email.com"); user1.setPassword("乐观1"); User user2 = userMapper.selectById(3); user2.setName("乐观2"); user2.setEmail("乐观2@email.com"); user2.setPassword("乐观2"); //模拟线程2先执行 --加上乐观锁 /* @Version private Integer version; */ userMapper.updateById(user2); userMapper.updateById(user1);//加锁之后会发现user1并没有覆盖user2的值 }
此时会发现vserion的版本由1变成2,user1执行update操作失败,并没有覆盖user2的值。
示例SQL原理
update table set x=x+1, version=version+1 where id=#{id} and version=#{version};
version方式:
一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加一。当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到的version值为当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。而MybatisPlus的乐观锁插件就是使用version的方式。
CAS操作方式:
即compare and swap 或者 compare and set,涉及到三个操作数,数据所在的内存值,预期值,新值。当需要更新时,判断当前内存值与之前取到的值是否相等,若相等,则用新值更新,若失败则重试,一般情况下是一个自旋操作,即不断的重试。
逻辑删除
适用场景:
- 逻辑删除是为了方便数据恢复和保护数据本身价值等等的一种方案,但实际就是删除。
- 如果你需要再查出来就不应使用逻辑删除,而是以一个状态去表示。
如:员工离职,账号被锁定等都应该是一个状态字段,此种场景不应使用逻辑删除。
- 若确需查找删除数据,如老板需要查看历史所有数据的统计汇总信息,请单独手写sql。
SpringBoot 配置方式:
- application.yml 加入配置(如果你的默认值和mp默认的一样,该配置可无):
mybatis-plus: global-config: db-config: logic-delete-field: flag #全局逻辑删除字段值 3.3.0开始支持,详情看下面。 logic-delete-value: 1 # 逻辑已删除值(默认为 1) logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
- 实体类字段上加上@TableLogic注解
@TableLogic private Integer deleted;
- 效果: 使用mp自带方法删除和查找都会附带逻辑删除功能 (自己写的xml不会)
example 删除 update user set deleted=1 where id =1 and deleted=0 查找 select * from user where deleted=0
- 全局逻辑删除: begin 3.3.0如果公司代码比较规范,比如统一了全局都是flag为逻辑删除字段。使用此配置则不需要在实体类上添加 @TableLogic。但如果实体类上有 @TableLogic 则以实体上的为准,忽略全局。即先查找注解再查找全局,都没有则此表没有逻辑删除。
mybatis-plus: global-config: db-config: logic-delete-field: flag #全局逻辑删除字段值
示例演示:
//逻辑删除 @Testpublic void deletedTest(){ //随便删除一个用户 int count = userMapper.deleteById("5"); if (count==0){ System.out.println("删除失败!!!"); } }
SQL语句:
数据库中的变化:
此时如果你在查询,如果不带条件(deleted=0)的话,肯定是查不到ID为5的用户,如果后期项目需要做统计就可以找到这种数据。
如果没有逻辑删除的话,那就直接把用户从数据库中移除,也就没有后期。
mybatis 乐观锁_MybatisPlus新特性之逻辑删除、乐观锁、自动填充相关推荐
- MyBatis-Plus 乐观锁 防止超卖、逻辑删除、自动填充、Id自增
MyBatis-Plus 乐观锁 防止超卖.逻辑删除.自动填充 Day3 前面的简单的讲了一下mybatis-plus的使用 当然有很多不足 我写博客就是想促进大家一起学习 也想让这些内容更简单一些. ...
- mybatis-plus 初始化项目 主键自增策略 自动填充 逻辑删除 乐观锁 复杂查询 分页查询
一创建数据库(添加数据) 二 创建springboot工程 导入工程所需要的依赖(mybatis-plus,mysql-connector-java,lombok) <dependency> ...
- java5新特性静态引用、foreach、自动装箱和泛型枚举以及可变参数的总结
静态引用 导入(import)表示去找哪一个类,去哪一个包下去找哪些被使用到的类. 在java中有一个语言核心包:java.lang. 使用java.lang包中的API,不需要引用,直接能找到,但是 ...
- SpringBoot 配置 generator代码生成+knife4j接口文档(2种模板设置、逻辑删除、字段填充 含代码粘贴可用)保姆级教程(注意事项+建表SQL+代码生成类封装+测试类)
保姆级教程,逻辑删除及字段自动填充设置,特别要说明的是本次用的是MySQL数据库,如果使用Oracle数据库是,数据库配置需要改变,数据库表一定要大写,否则无法生成代码. 数据库表 CREATE TA ...
- 我要学ASP.NET MVC 3.0(一): MVC 3.0 的新特性
摘要 MVC经过其1.0和2.0版本的发展,现在已经到了3.0的领军时代,随着技术的不断改进,MVC也越来越成熟.使开发也变得简洁人性化艺术化. 园子里有很多大鸟都对MVC了如指掌,面对问题犹同孙悟空 ...
- JDK5.0新特性系列---目录
JDK5.0新特性系列---目录 JDK5.0新特性系列---1.自动装箱和拆箱 JDK5.0新特性系列---2.新的for循环 JDK5.0新特性系列---3.枚举类型 JDK5.0新特性系列--- ...
- Oracle 11g 新特性简介
Oracle 11g于2007年7月11日美国东部时间11时(北京时间11日22时)正式发布,11g是甲骨文公司30年来发布的最重要的数据库版本,根据用户的需求实现了信息生命周期管理(Informat ...
- stringBuffer、StringBuilder、排序、Arrays、Jdk1.5新特性(java基础知识十三)
1.StringBuffer类的概述 * A:StringBuffer类概述 * 通过JDK提供的API,查看StringBuffer类的说明 * 1.线程安全的可变字符序列. * 2.可将字符串缓冲 ...
- ASP.NET MVC 3.0(一): MVC 3.0 的新特性 摘要
ASP.NET MVC 3.0(一): MVC 3.0 的新特性 摘要 ASP.NET MVC 3.0(二): MVC的概念及MVC 3.0开发环境 ASP.NET MVC 3.0(三): 初识MVC ...
最新文章
- 社会学专业喜欢计算机怎么办,考研困惑我是计算机专业的学生可是我喜欢文学想考河北师范大学的研究 爱问知识人...
- TCP/IP / 三次握手之状态转换图和原因
- 腾讯运维技术专家集结,揭秘高效智能运维 | 沙龙报名中
- 简单易懂,ThreadPoolExecutor参数说明
- python函数-函数进阶
- 什么是代码调试(debugging)?进行代码调试的基本方法有哪些?
- 文件上传input简便美化方案
- matlab两张图片合成一张_二次曝光合成手机照片剪影照并不难,这样用snapseed轻松实现...
- python 异步 生产者 消费者_python 生产者消费者模式 - 刘江的python教程
- SSH三大框架的概述
- 如何利用 Visual Studio 自带工具提高开发效率
- Java进阶:GIT
- java提高篇(四)-----抽象类与接口
- chrome安装测试打包插件
- 深度linux运行浏览器中毒,使用深度Deepin系统的用户可在商店中安装360安全浏览器正式版...
- 悟空CRM(PHP版本)安装教程
- matlab 平滑曲线拟合散点
- Mycat全局序列号失效的诡异事件
- matlab中options,[转载]Matlab优化函数中options选项的修改
- 3、按键扫描检测处理
热门文章
- NMAP输出结果中CPE的含义
- swift语言注册非免费苹果账号iOS游戏框架Sprite Kit基础教程
- python实现高校教务管理系统_python+mysql实现教务管理系统
- malloc 初始化_在C语言中,请一定记得初始化局部变量!
- c++ 回调函数_Java中的回调机制,这篇给你整的明明白白的
- php获取date前1分钟hour,php时间轴函数,刚、1分钟前、1小时前、一天前
- a*算法matlab代码_蚁群算法(含MATLAB代码)
- python 网页上显示数据_用Python实现网页数据抓取
- 清华、北邮等高校研究人员实现具有 160 个目标的基于 SSVEP 的免校准 BCI 系统...
- 微软687亿美元收购动视暴雪成第三大游戏公司,网友:米哈游只有收购索尼才能抗衡了...