赞
踩
乐观锁是相对悲观锁而言的,乐观锁假设数据一般情况不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果冲突,则返回给用户异常信息,让用户决定如何去做。
乐观锁适用于读多写少的场景,这样可以提高程序的吞吐量。
乐观锁采取了更加宽松的加锁机制。也是为了避免数据库幻读、业务处理时间过长等原因引起数据处理错误的一种机制,但乐观锁不会刻意使用数据库本身的锁机制,而是依据数据本身来保证数据的正确性。
乐观锁的实现:
2️⃣说明 乐观并发控制相信事务之间的数据竞争(data race)的概率是比较小的,因此尽可能直接做下去,直到提交的时候才去锁定,所以不会产生任何锁和死锁。
官网描述
OptimisticLockerInnerInterceptor 是 MyBatis Plus 提供的一个内置拦截器,用于实现乐观锁机制。
在并发环境下,乐观锁可以有效防止因并发更新导致的数据不一致问题
当要更新一条记录的时候,希望这条记录没有被别人更新 乐观锁实现方式:
取出记录时,获取当前version
更新时,带上这个version
执行更新时, set version = newVersion where version = oldVersion
如果version不对,就更新失败
实现步骤:
a. 配置插件
- package com.wdzl.config;
-
- import com.baomidou.mybatisplus.annotation.DbType;
- import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
- import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
- import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
- import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
- import com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize;
- import org.mybatis.spring.annotation.MapperScan;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
-
- //Spring boot方式
- @Configuration
- public class MybatisPlusConfig {
- /**
- * 新版
- */
- @Bean
- public MybatisPlusInterceptor mybatisPlusLockInterceptor() {
- MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
- // OptimisticLockerInnerInterceptor 是 MyBatis Plus 提供的一个内置拦截器
- mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
- return mybatisPlusInterceptor;
- }
- }
b. 修改表结构
增加一列,用来表示记录的版本。类型必须是数值型的
- create table book(
- bookid int primary key,
- bookname varchar(200),
- price float,
- pubdate datetime,
- author varchar(20),
- version int
- );
说明:
newVersion = oldVersion + 1
newVersion
会回写到 entity
中updateById(id)
与 update(entity, wrapper)
方法update(entity, wrapper)
方法下, wrapper
不能复用!!!c. 修改实体类
增加版本属性,并使用注解 @Vesion 标注。
注意:属性名命名没有要求。
- @Version
- private Integer version;
d. 测试
- /**
- * UPDATE book SET bookName=?, price=?, pubdate=?, author=?, version=? WHERE bookid=? AND version=?
- */
- @Test
- public void lock(){
- Book book = bookDao.selectById(22);
- book.setPrice(book.getPrice()-5);
-
-
- Book book2 = bookDao.selectById(22);
- book2.setPrice(book.getPrice()-60);
-
- bookDao.updateById(book); //按主键修改
-
- //修改失败
- bookDao.updateById(book2);
-
- }
- }
注意:配置好乐观锁插件和实体类注解后,再次修改,发现 SQL 语句的变化。添加了对于 version 版本字段的添加和修改
UPDATE book SET bookName=?, price=?, pubdate=?, author=?, version=? WHERE bookid=? AND version=?
逻辑删除是数据库管理中的一种策略,它并不真正从数据库物理层面删除数据,而是通过更新数据表中的某个字段来标记这条记录为“已删除”状态。这样做的好处是可以保留历史数据,避免因误删导致的数据丢失,并且在需要时可以查询到被逻辑删除的记录。
在 MyBatis Plus 中,逻辑删除功能已经内置并提供了便捷的实现方式
官网说明:
说明:
只对自动注入的sql起效:
例如:
update user set deleted=1 where id = 1 and deleted=0
select id,name,deleted from user where deleted=0
a) 修改表添加字段
表中添加一个列,用来表示删除状态的列
- create table book(
- bookid int primary key,
- bookname varchar(200),
- price float,
- pubdate datetime,
- author varchar(20),
- version int,
- deleted int
- );
b) 对应修改实体类,增加状态属性。
实体类字段上加上@TableLogic
注解
- @TableLogic
- private Integer deleted;
c) 提前规定好删除标志的值和正常的值
可以在application.yml 配置状态值
- mybatis-plus:
- global-config:
- db-config:
- logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
- logic-delete-value: 1 # 逻辑已删除值(默认为 1)
- logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
d) 测试:
- @Test
- void logicdel(){
- //SELECT bookid,bookName,price,pubdate,author,version,state FROM book WHERE state=0
- // List<Book> list = bookDao.selectList(null);
-
- //删除时 UPDATE book SET state=1 WHERE bookid=? AND state=0
- bookDao.deleteById(27);
- }
注意:在做select 查询,update 修改,删除操作时,执行的 SQL 都会有所变化。结合version来操作
实现要求:
- -- 按作者分组,查询每个作者初版的书数量及平均价格
- SELECT author, AVG(price) 平均价格,COUNT(*) 数量
- FROM book
- WHERE state=0
- GROUP BY author
- HAVING COUNT(*)>2
代码实现:
- void group(){
- QueryWrapper<Book> queryWrapper = new QueryWrapper<>();
- queryWrapper.select("author","count(*) bookCount","avg(price) pavg")
- .groupBy("author")
- .having("count(*)>2");
- List<Map<String, Object>> maps = bookDao.selectMaps(queryWrapper);
- for (Map<String, Object> map : maps) {
- String author = (String)map.get("author");
- Long count = (Long)map.get("bookCount");
- Double pavg = (Double)map.get("pavg");
- System.out.println(author+"==="+count+"==="+pavg);
- }
- }
- /**
- * order by
- */
- public void order(){
- QueryWrapper<Book> queryWrapper = new QueryWrapper<>();
- // queryWrapper.orderByAsc("price");
- queryWrapper
- .orderByDesc("price")
- .orderByAsc("pubDate")
- .le("price",34)
- .or()
- .gt("pubDate",new Date())
- .and(t->t.notIn("bookid",34,45,56))
- .gt("price",56);
-
- bookDao.selectList(queryWrapper);
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。