java srpint boot 2.2.1 第二部份,乐观锁机制, 构造复杂查询条件,分页查询 相关内容,删除与软删除
第二部份,引起锁机制的原理和解决方案:
测试环境搭建
第一步先建一个数据库表用于模拟商品购买。
CREATE TABLE product (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
stock INT NOT NULL,
version INT NOT NULL DEFAULT 0
);
第二步,新建一个商品类,用于等于mapper 产生方法,如果用插件lombok, 可以在类上面加上@Data 自动生产 自动生成get set 方法
import com.baomidou.mybatisplus.annotation.*;
@TableName("product")
public class Product {
@TableId(type = IdType.AUTO)
private Integer id;
private String name;
private Integer stock;
private Integer version;
// 省略Getter和Setter方法
}
第三步,编写 mapper 继承baseMapper, mapper 文件下面新建ProductMapper 接口
public interface ProductMapper extends BaseMapper<Product> {
}
最后编写测试用例:
public void testConcurrentUpdate() {
//1、小李获取数据
Product p1 = productMapper.selectById(1L);
System.out.println("小李取出的价格:" + p1.getPrice());
//2、小王获取数据
Product p2 = productMapper.selectById(1L);
System.out.println("小王取出的价格:" + p2.getPrice());
//3、小李加了50,存入数据库
p1.setPrice(p1.getPrice() + 50);
productMapper.updateById(p1);
p2.setPrice(p2.getPrice()-30);
productMapper.updateById(p2);
//最后的结果
//用户看到的商品价格
Product p3 = productMapper.selectById(1L);
System.out.println("最后的结果:" + p3.getPrice());
}
运行结果:明明正确的 业务逻辑 应该是120 结果双方都取出数据来修改,后面修改的覆盖了前面的,

解决方法一
直接锁住这个数据 实际上,在MyBatis中,并不直接使用Java代码来加数据库锁,而是通过编写特定的SQL语句来实现。使用数据库的 for update语句可以进行行级锁定,这通常是在SQL查询中进行,例如:
SELECT * FROM product WHERE id = 1 FOR UPDATE;这将锁定id为1的商品记录,直到事务结束才会释放锁。
在MyBatis中可以使用如下方式来执行带有for update的SQL语句:
@Select("SELECT * FROM product WHERE id = #{id} FOR UPDATE") Product lockProductForUpdate(Long id);
注意,FOR UPDATE关键字的使用可能因数据库类型而异,你需要根据使用的具体数据库来调整相应的SQL语句。另外,在实际业务中,请留意事务的控制和锁的释放时机,避免出现死锁等问题。并且要手工编写sql 比较麻烦我们后面乐观锁,只需要配置可以直接自动应用,以下是悲观锁的流程示例,属于伪代码
@Test
public void testConcurrentUpdateWithPessimisticLock() {
Product p = productMapper.selectById(1L); // 加悲观锁
productMapper.lockProductForUpdate(1L); // 修改价格
p.setPrice(p.getPrice() + 50);
productMapper.updateById(p); // 解锁 操作完后自动解锁 // 获取最终结果
Product finalProduct = productMapper.selectById(1L);
System.out.println("Final price: " + finalProduct.getPrice());
}
解决方二 乐观锁
核心代码思路为以下截图:如果更新失败需要重新业务

操作流程:
第一步:加入乐观锁插件‘
在配置 文件中增加以下配置,这个配置文件由于要写很多配置 ,所以单独搞一个config 目录,下面新建一个配置文件,这个文件写类,然后加入配置 的注释,第统会自动识别为配置
说细配置如下:

package com.example.demo.config; import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement; @EnableTransactionManagement
@Configuration
//@MapperScan("com.example.demo.mapper")
public class MyBatisPlusConfig {
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor(){
return new OptimisticLockerInterceptor();
} @Bean
public PaginationInterceptor paginationInterceptor(){
return new PaginationInterceptor();
}
}
乐观锁配置
第二步

第三步,在类上写上版本注解“ 到此为这经配置好 ,在没试的时候如果操作失败重新插入或更新就好,

测试代码:

public void testConcurrentUpdate() {
//1、小李获取数据
Product p1 = productMapper.selectById(1L);
System.out.println("小李取出的价格:" + p1.getPrice());
//2、小王获取数据
Product p2 = productMapper.selectById(1L);
System.out.println("小王取出的价格:" + p2.getPrice());
//3、小李加了50,存入数据库
p1.setPrice(p1.getPrice() + 50);
productMapper.updateById(p1);
p2.setPrice(p2.getPrice() - 30);
int result = productMapper.updateById(p2);
if (result == 0) {
System.out.println("小王更新失败");
//发起重试
p2 = productMapper.selectById(1L);
p2.setPrice(p2.getPrice() - 30);
productMapper.updateById(p2);
}
//最后的结果
//用户看到的商品价格
Product p3 = productMapper.selectById(1L);
System.out.println("最后的结果:" + p3.getPrice());
}
乐观锁使用测试
备注,由于我专门建了配置类,所以在主类的上配置都 可以迁过去,比如@MapperScan

package com.example.demo.config; import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement; @EnableTransactionManagement
@Configuration
//@MapperScan("com.example.demo.mapper")
public class MyBatisPlusConfig {
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor(){
return new OptimisticLockerInterceptor();
} @Bean
public PaginationInterceptor paginationInterceptor(){
return new PaginationInterceptor();
}
}
详细的增删改测试用例

//查询多条
@Test
public void testSelectProductList() {
QueryWrapper<Product> queryWrapper = new QueryWrapper<>();
queryWrapper.gt("price", 50);
List<Product> productList = productMapper.selectList(queryWrapper);
productList.forEach(product -> System.out.println(product.getId() + " - " + product.getName() + " - " + product.getPrice()));
}
//更新多条
@Test
public void testUpdateProductList() {
UpdateWrapper<Product> updateWrapper = new UpdateWrapper<>();
updateWrapper.set("price", 200).ge("price", 100);
int rows = productMapper.update(new Product(), updateWrapper);
System.out.println("Updated rows: " + rows);
}
更新单条
@Test
public void testUpdateSingleProduct() {
Product product = new Product();
product.setId(1L);
product.setName("New Name");
int rows = productMapper.updateById(product);
System.out.println("Updated rows: " + rows);
}
//复杂查询
@Test
public void testSelectProductByComplexCondition() {
QueryWrapper<Product> queryWrapper = new QueryWrapper<>();
queryWrapper.like("name", "Product").or().eq("price", 100);
List<Product> productList = productMapper.selectList(queryWrapper);
productList.forEach(product -> System.out.println(product.getId() + " - " + product.getName() + " - " + product.getPrice()));
}
//联表查询
@Test
public void testSelectProductWithCategory() {
List<Map<String, Object>> productList = productMapper.selectMaps(new QueryWrapper<Product>()
.select("p.id as productId", "p.name as productName", "p.price", "c.name as categoryName")
.leftJoin("category c on c.id = p.category_id")
.orderByAsc("p.id")
);
productList.forEach(item -> {
System.out.println("Product ID: " + item.get("productId") + ", Product Name: " + item.get("productName") +
", Price: " + item.get("price") + ", Category Name: " + item.get("categoryName"));
});
}

高极查询构建器
mybtis-plus 虽然提供了查询 id 删除这些方法,但是涉及到or 条件 等于 多个值这样的查询时需要构造查询构器
wapper构造器蓝图

MyBatis-Plus是一个优秀的MyBatis增强工具,在它的条件构造器中,主要包括以下几个对象:QueryWrapper(普通查询条件构造器)和UpdateWrapper(更新条件构造器)。这两个对象都继承自AbstractWrapper,其中AbstractWrapper提供了一些基础的方法 。以下是 MyBatis-Plus 中常用的构建器及其条件方法:
QueryWrapper 和 UpdateWrapper:
- 这两个构建器都继承自
AbstractWrapper,因此它们共享许多条件方法。 - 常用条件方法:
eq(String column, Object val): 等于条件ne(String column, Object val): 不等于条件gt(String column, Object val): 大于条件ge(String column, Object val): 大于等于条件lt(String column, Object val): 小于条件le(String column, Object val): 小于等于条件between(String column, Object val1, Object val2): 之间条件notBetween(String column, Object val1, Object val2): 不在之间条件like(String column, Object val): 模糊查询条件(使用%val%)notLike(String column, Object val): 模糊查询的否定条件in(String column, Collection<?> valList): 在集合中的条件notIn(String column, Collection<?> valList): 不在集合中的条件isNull(String column): 为空条件isNotNull(String column): 不为空条件- ... 以及其他多个方法。
- 这两个构建器都继承自
LambdaQueryWrapper 和 LambdaUpdateWrapper:
- 这两个构建器与
QueryWrapper和UpdateWrapper类似,但它们支持 Lambda 表达式,使代码更加简洁且类型安全。 - 条件方法与上述类似,但可以通过 Lambda 表达式直接引用实体类的属性,而不是使用字符串列名。
- 这两个构建器与
QueryChainWrapper:
- 这是一个链式查询构建器,允许你通过链式方法调用来构建查询条件。
- 条件方法与
QueryWrapper类似,但可以通过链式调用来组合多个条件。
EntityWrapper(在某些版本中可能已被弃用):
- 这是一个较早的构建器,用于包装实体类对象,并提供了一系列的条件查询方法。
- 条件方法与上述类似,但主要是基于实体类属性进行设置。
请注意,随着 MyBatis-Plus 版本的更新,某些构建器或方法可能会被弃用或替换。因此,建议查阅官方文档以获取最新和最准确的信息。
总的来说,MyBatis-Plus 提供了丰富的构建器和条件方法,以满足各种查询和更新需求。你可以根据项目需求选择适合的构建器和方法来简化数据库操作
示例代码:
// 创建QueryWrapper对象
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// 构建查询条件
queryWrapper.eq("age", 25)
.like("name", "Jack")
.in("status", Arrays.asList(1, 2, 3)); // 查询数据库
List<User> userList = userMapper.selectList(queryWrapper);
UpdateWrapper对象:
功能:用于构建更新条件的条件构造器。
常见构建条件的方式:
a) set(String column, Object value):设置更新字段值
b) eq(String column, Object value):等于
c) ne(String column, Object value):不等于
d) gt(String column, Object value):大于
e) lt(String column, Object value):小于
f) ge(String column, Object value):大于等于
g) le(String column, Object value):小于等于
示例代码: // 创建UpdateWrapper对象
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
// 构建更新条件
updateWrapper.eq("age", 25)
.set("status", 1); // 更新数据
int rows = userMapper.update(user, updateWrapper);
以下是一个复杂的Lambda表达式加上一个复杂的查询示例




分页插件操作流程
第一步引入插件:
@Bean
public PaginationInterceptor paginationInterceptor(){
return new PaginationInterceptor();
}
以下是官方图参考

编写第一个测试用例:
@Test
public void testSelectPage(){
Page<User> page = new Page<>(1,5);
Page<User> userPage = userMappter.selectPage(page, null);
List<User> records = userPage.getRecords();
// records.forEach(System.out::println);
System.out.println("总页数"+ userPage.getPages());
System.out.println("总计录"+ userPage.getTotal());
System.out.println("当前页"+ userPage.getCurrent());
System.out.println("每页大小"+ userPage.getSize());
System.out.println("有没有下页"+ userPage.hasNext());
System.out.println("有没有上文"+ userPage.hasPrevious());
}
工作中有时候我们只要查一二例字段,会多一些无用的字段具为空,如下图的结果,解决方法用带map 的方法来构建

解决方法:
public void testSelectMapsPage() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.select("id", "name");
Page<Map<String, Object>> page = new Page<>(1, 5);
Page<Map<String, Object>> pageParam = userMappter.selectMapsPage(page, queryWrapper);
List<Map<String, Object>> records = pageParam.getRecords();
records.forEach(System.out::println);
}

最后引出删除与软删除
先说一下传统怎么山删除。但是我们不建议删除数据,后期有用的数据可以用来做数据分析,所以最下面我们会采用软删除。
//删除一条
public void testDeleteByid(){
int result = userMappter.deleteById(1l);
System.out.println("删除了"+ result + "行");
}
//删除多条
public void testDeleteBatchIds(){
int result = userMappter.deleteBatchIds(Arrays.asList(10,11,12));
System.out.println("删除了"+ result + "行");
} //删除根据条件
@Test
public void testDeleteByMap(){
HashMap<String, Object> map = new HashMap<>();
map.put("name","Hello");
map.put("age",18);
int result = userMappter.deleteByMap(map);
System.out.println("删隆了"+ result+ "行");
}
}
软删除步聚餐
第一步 数据库存新增delect 字段 类型为: tinyint
第二步 在对象上新增注解
@Data
public class User {
// @TableId(type = IdType.AUTO)
private Long id;
private String name;
private Integer age;
private String email; @TableField(fill = FieldFill.INSERT)
private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime; @TableLogic
private Integer deleted;
}

java srpint boot 2.2.1 第二部份,乐观锁机制, 构造复杂查询条件,分页查询 相关内容,删除与软删除的更多相关文章
- 【java】spring-data-jpa 集成hibernate实现多条件分页查询
初次接触spring-data-jpa,实现多条件分页查询. 基础环境 Spring Boot+spring-data-jpa+hibernate+mysql 1.接口 要继承这个接口,这个接口提供了 ...
- Java并发编程(05):悲观锁和乐观锁机制
本文源码:GitHub·点这里 || GitEE·点这里 一.资源和加锁 1.场景描述 多线程并发访问同一个资源问题,假如线程A获取变量之后修改变量值,线程C在此时也获取变量值并且修改,两个线程同时并 ...
- posix第二篇-----linux 锁机制
1 简介 锁机制(lock) 是多线程编程中最常用的同步机制,用来对多线程间共享的临界区(Critical Section) 进行保护. Pthreads提供了多种锁机制,常见的有: 1) Mutex ...
- Java 多线程与并发【原理第二部分笔记】
Java 多线程与并发[原理第二部分笔记] 什么是Java内存模型中的happens-before Java内存模型,即JMM,本身是一种抽象的概念,并不是真实存在的,他描述的是一组规则或者说是一种规 ...
- 五分钟学会悲观乐观锁-java vs mysql vs redis三种实现
1 悲观锁乐观锁简介 乐观锁( Optimistic Locking ) 相对悲观锁而言,乐观锁假设认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果 ...
- Java并发 行级锁/字段锁/表级锁 乐观锁/悲观锁 共享锁/排他锁 死锁
原文地址:https://my.oschina.net/oosc/blog/1620279 前言 锁是防止在两个事务操作同一个数据源(表或行)时交互破坏数据的一种机制. 数据库采用封锁技术保证并发操作 ...
- Java Hibernate中的悲观锁和乐观锁的实现
锁(locking) 业务逻辑的实现过程中,往往需要保证数据访问的排他性.如在金融系统的日终结算 处理中,我们希望针对某个cut-off时间点的数据进行处理,而不希望在结算进行过程中 (可能是几秒种, ...
- 对Java提供的锁机制的一些思考
Java的数据会在CPU.Register.Cache.Heap和Thread stack之间进行复制操作,而前面四个都是在Java Threads之间共享,因此Java的锁机制主要用于解决Racin ...
- Java Spring Boot VS .NetCore (五)MyBatis vs EFCore
Java Spring Boot VS .NetCore (一)来一个简单的 Hello World Java Spring Boot VS .NetCore (二)实现一个过滤器Filter Jav ...
- Java Spring Boot VS .NetCore (七) 配置文件
Java Spring Boot VS .NetCore (一)来一个简单的 Hello World Java Spring Boot VS .NetCore (二)实现一个过滤器Filter Jav ...
随机推荐
- k8s——kubctl命令基础
语法 kubevtl [command] [type] [name] [flags] command: 指定要对一个或多个资源执行的操作,例如,`create`,`get`,`describe`,`d ...
- 夜莺监控 V7 第二个 beta 版本发布,内置集成故障自愈能力,简化部署
经过一个半月的打磨改进,夜莺监控 V7 第二个 beta 版本发布了,本次发布的主要亮点是内置集成故障自愈能力,简化架构,同时做了其他 19 项改进.一些重要的改进如下: feat: 集成故障自愈的能 ...
- web框架及Django简介
WEB框架 Web框架是一种开发框架,用来支持动态网站.网络应用和网络服务的开发.这大多数的web框架提供了一套开发和部署网站的方式,也为web行为提供了一套通用的方法. web框架已经实现了很多功能 ...
- 什么是JDBC的最佳实践?
a.数据库资源是非常昂贵的,用完了应该尽快关闭它.Connection, Statement, ResultSet等JDBC对象都有close方法,调用它就好了. b.养成在代码中显式关闭掉 ...
- java线程的park unpark方法
标签(空格分隔): 多线程 park 和 unpark的使用 park和unpark并不是线程的方法,而是LockSupport的静态方法 暂停当前线程 LockSupport.park();//所在 ...
- vue中手写table的升降序
有些时候,我们总是无可避免的需要自己去手撸一些东西,因为需求总是在不断的变化.例如,最开始的需求,我们只是在首页展示一个数据列表,此时,我们可能直接就自己手写了一个table,后来,突然增加了一个需求 ...
- Oracle 数据库 命令行安装
Oracle 数据库 命令行安装 1. 准备工作 关闭 防火墙,关闭 SElinux 2. 安装相关依赖包 yum -y install binutils compat-libcap1 compat- ...
- 防止unordered_map 被卡方法
codeforces 上看到的,mark 一下代码.原作者:neal,原链接:https://codeforces.com/blog/entry/62393 struct custom_hash { ...
- Ubuntu 18.04 安装OneDrive自动同步
Ubuntu 18.04 安装OneDrive自动同步 Windows10系统已经自带了OneDrive的自动同步功能,对于多设备用户而言已经成为了一个非常方便传输保存文件的途径,在Ubuntu下也有 ...
- Linux驱动中的等待队列与休眠
Linux驱动中的等待队列与休眠 原文:https://blog.csdn.net/mengluoxixiang/article/details/46239523?spm=1001.2014.3001 ...