前言
在前面的文章中,荔枝梳理了一个MyBatis-Plus的基本使用、配置和通用Service接口,我们发现在MyBatis-Plus的辅助增强下我们不再需要通过配置xml文件中的sql语句来实现基本的sql操作了,不愧是最佳搭档!在这篇文章中,荔枝会着重梳理有关MyBatis-Plus的两个知识点:条件构造器、分页插件和乐观锁插件,希望对有需要的小伙伴有帮助~~~
文章目录
前言
一、条件构造器
1.1 组装查询条件
1.2 组装排序条件
1.3 组装删除条件
1.4 使用QueryWrapper实现修改功能
1.5 条件优先级
1.6 子查询
1.7 使用UpdateWrapper实现修改功能
1.8 使用Condition组装条件
1.9 LambdaQueryWrapper
1.10 LambdaUpdateWrapper
二、分页插件
2.1 基本使用
2.2 自定义分页插件
三、乐观锁插件
3.1 乐观锁和悲观锁
3.2 乐观锁插件
总结
一、条件构造器
条件构造器,顾名思义就是用来封装当前我们用来查询的条件的,条件构造器的最顶层的接口是Mapper,被AbstractWrapper继承,其下由三个子类分别是:AbstractLambdaWrapper、UpdateWrapper和QueryWrapper。
1.1 组装查询条件
//条件构造器组装查询条件
@Test
public void testWrapper(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("user_name","crj")
.between("age",20,30)
.isNotNull("email");
List<User> list = userMapper.selectList(queryWrapper);
list.forEach(System.out::println);
}
1.2 组装排序条件
//组装排序条件
@Test
public void test1(){
//查询用户信息按照年龄的降序排序,若年龄相同则按照id升序排序
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.orderByDesc("age")
.orderByAsc("uid");
List<User> list = userMapper.selectList(queryWrapper);
list.forEach(System.out::println);
}
1.3 组装删除条件
//组装删除条件
@Test
public void test2(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.isNull("email");
int result = userMapper.delete(queryWrapper);
System.out.println("受影响函数"+result);
}
1.4 使用QueryWrapper实现修改功能
//实现修改功能
@Test
public void test3(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//把年龄大于20且姓名为crj或者是邮箱为null的用户信息进行修改
queryWrapper.gt("age",20)
.like("user_name","crj")
.or()
.isNull("email");
User user = new User();
user.setName("CRJ");
user.setEmail("123456@123.com");
int result = userMapper.update(user,queryWrapper);
System.out.println(result);
}
1.5 条件优先级
在and()和or()中通过Lambda表达式实现优先级操作,其中Lambda表达式中的条件优先执行。
//条件优先级
@Test
public void test4(){
//将用户名中含有crj并且(年龄大于20或邮箱为null)的用户信息修改
//lambda中的条件优先执行
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("user_name","crj")
.and(i->i.gt("age",20).or().isNull("email"));
User user = new User();
user.setName("CRJ");
user.setEmail("123456@123.com");
int result = userMapper.update(user,queryWrapper);
System.out.println(result);
}
1.6 子查询
//子查询
@Test
public void test5(){
//查询id小于等于100的用户信息
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.inSql("uid","select uid from t_user where uid<=100");
List<User> list = userMapper.selectList(queryWrapper);
}
1.7 使用UpdateWrapper实现修改功能
//使用UpdateWrapper实现修改功能
//将用户名中含有crj并且(年龄大于20或邮箱为null)的用户信息修改
@Test
public void test6(){
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.like("user_name","crj")
.and(i->i.gt("age",20).or().isNull("email"));
updateWrapper.set("user_name","CRJ");
userMapper.update(null,updateWrapper);
}
1.8 使用Condition组装条件
1.9 LambdaQueryWrapper
@Test
public void test8(){
String username = "a";
Integer ageBegin = null;
Integer ageEnd = 30;
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.like(StringUtils.isNotBlank(username),User::getName,username)
.ge(ageBegin!=null,User::getAge,ageBegin)
.le(ageEnd!=null,User::getAge,ageEnd);
List<User> list = userMapper.selectList(queryWrapper);
list.forEach(System.out::println);
}
1.10 LambdaUpdateWrapper
@Test
public void test9(){
LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.like(User::getName,"crj")
.and(i->i.gt(User::getAge,20).or().isNull(User::getEmail));
updateWrapper.set(User::getName,"CRJ");
userMapper.update(null,updateWrapper);
}
二、分页插件
2.1 基本使用
分页插件的配置类
package com.crj.mybatisplus_test.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
//配置类,配置MyBatisPlus的插件功能
@Configuration
@MapperScan("com.crj.mybatisplus_test.mapper")
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
测试类
package com.crj.mybatisplus_test;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.crj.mybatisplus_test.mapper.UserMapper;
import com.crj.mybatisplus_test.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class MyBatisPlusPluginsTest {
@Autowired
private UserMapper userMapper;
@Test
public void test1(){
Page<User> page = new Page<>(1,3);
userMapper.selectPage(page,null);
System.out.println(page);
}
}
page对象的几个方法:
- page.getRecords(): 获取当前页数据
- page.getCurrent():获取当前页的页码
- page.getSize():获取每页显示的条数
- page.getPages(): 获取总页数
- page.getTotal(): 获取总记录数
- page.hasNext(): 查看有没有下一页
- page.hasPrevious():查看有没有上一页
配置类型别名:
mybatis-plus:
type-aliases-package:全路径
2.2 自定义分页插件
之前借助条件构造器来实现分页的操作,通过查看源码知晓,selectPage要求两个参数,返回值和第一个参数都是IPage类型的,而IPage类型的接口是被Page类对象实现的,因此第一个参数一定是page对象。我们需要在userMapper接口中手写一个方法替代原来的selectPage,同时分页插件的配置文件保持不变,配置好MyBatisPlus的插件功能
UserMapper.java
package com.crj.mybatisplus_test.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.crj.mybatisplus_test.pojo.User;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
@Repository
//继承MyBatis-Plus的BaseMapper接口
public interface UserMapper extends BaseMapper<User> {
/**
* 根据年龄查询用户信息并分页
* @param page mybatis-plus提供的分页对象,必须放在第一个参数中
* @param age
* @return
*/
Page<User> selectPageVo(@Param("page") Page<User> page,@Param("age") Integer age);
}
UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.crj.mybatisplus_test.mapper.UserMapper">
<!-- Page<User> selectPageVo(@Param("page") Page<User> page,@Param("age") Integer age);-->
<select id="selectPageVo" resultType="User">
select uid,name,age from t_user where age > #{age}
</select>
</mapper>
测试类
@Test
public void testPageVo(){
Page<User> page = new Page<>(1,3);
userMapper.selectPageVo(page,20);
}
三、乐观锁插件
3.1 乐观锁和悲观锁
说到乐观锁和悲观锁,我们经常通过一个场景来理解:我们需要对一个值为100的数进行+10操作再进行-30操作,这两步使用多线程执行。A和B线程同时取一个值为100的数C,A对C进行+10操作,B对取出来的值进行-30的操作,如果没有加锁控制,那么A处理的值D不能被B拿到且会被B覆盖。对于加锁这里简单归纳两种:乐观锁和悲观锁,悲观锁会格外注重线程安全,只有等A操作完后才能由B取值;而乐观锁则是通过版本控制的方式来检测是否C被修改了。
未加锁的场景模拟
实体类
package com.crj.mybatisplus_test.pojo;
import lombok.Data;
@Data
public class Product {
private Long id;
private String name;
private Integer price;
private Integer version;
}
mapper接口
package com.crj.mybatisplus_test.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.crj.mybatisplus_test.pojo.Product;
import org.springframework.stereotype.Service;
@Service
public interface ProductMapper extends BaseMapper<Product> {
}
测试类
package com.crj.mybatisplus_test;
import com.crj.mybatisplus_test.mapper.ProductMapper;
import com.crj.mybatisplus_test.pojo.Product;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
//乐观锁插件使用
@SpringBootTest
public class MyBatisLockTest {
@Autowired
private ProductMapper productMapper;
//模拟线程场景
@Test
public void test1(){
Product productA = productMapper.selectById(1);
System.out.println("A查询的商品价格"+productA.getPrice());
Product productB = productMapper.selectById(1);
System.out.println("B查询的商品价格"+productB.getPrice());
productA.setPrice(productA.getPrice()+10);
productMapper.updateById(productA);
productB.setPrice(productB.getPrice()-30);
productMapper.updateById(productB);
//最后结果
Product productC = productMapper.selectById(1);
System.out.println("A查询的商品价格"+productC.getPrice());
}
}
3.2 乐观锁插件
前面知道乐观锁实现需要加上版本号来控制,因此实体类需要进行通过@Version来设置版本号。
实体类
package com.crj.mybatisplus_test.pojo;
import com.baomidou.mybatisplus.annotation.Version;
import lombok.Data;
@Data
public class Product {
private Long id;
private String name;
private Integer price;
@Version //标识乐观锁版本号字段
private Integer version;
}
MyBatis-Plus插件配置类
需要在配置类中配置好乐观锁插件方法OptimisticLockerInnerInterceptor()
package com.crj.mybatisplus_test.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
//配置类,配置MyBatisPlus的插件功能
@Configuration
@MapperScan("com.crj.mybatisplus_test.mapper")
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//配置分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
//配置乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
测试类
需要注意的是B修改数据失败后需要重试即可完成任务需求。
package com.crj.mybatisplus_test;
import com.crj.mybatisplus_test.mapper.ProductMapper;
import com.crj.mybatisplus_test.pojo.Product;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
//乐观锁插件使用
@SpringBootTest
public class MyBatisLockTest {
@Autowired
private ProductMapper productMapper;
//模拟线程场景
@Test
public void test1(){
Product productA = productMapper.selectById(1);
System.out.println("A查询的商品价格"+productA.getPrice());
Product productB = productMapper.selectById(1);
System.out.println("B查询的商品价格"+productB.getPrice());
productA.setPrice(productA.getPrice()+10);
productMapper.updateById(productA);
productB.setPrice(productB.getPrice()-30);
int result = productMapper.updateById(productB);
//由于加入了版本号控制,因此需要对修改失败的操作进行重试
if(result==0){
//失败重试
Product productNew = productMapper.selectById(1);
productNew.setPrice(productNew.getPrice()-30);
productMapper.updateById(productNew);
}
//最后结果
Product productC = productMapper.selectById(1);
System.out.println("A查询的商品价格"+productC.getPrice());
}
}
总结
通过条件构造器的几种基本用法使用示例,荔枝对wrapper类的使用有了一个比较直观的理解,同时荔枝觉得更需要注意的是两种插件的使用。接下来的文章中荔枝会对MyBatis-Plus的相关基础知识收尾,同时尝试整合到学习的项目中,跟荔枝一起期待一波吧哈哈哈哈哈~~~
今朝已然成为过去,明日依然向往未来!我是小荔枝,在技术成长的路上与你相伴,码文不易,麻烦举起小爪爪点个赞吧哈哈哈~~~ 比心心♥~~~