前言
在前面的文章中,荔枝梳理了一个MyBatis-Plus的基本使用、配置和通用Service接口,我们发现在MyBatis-Plus的辅助增强下我们不再需要通过配置xml文件中的sql语句来实现基本的sql操作了,不愧是最佳搭档!在这篇文章中,荔枝会着重梳理有关MyBatis-Plus的两个知识点:条件构造器、分页插件和乐观锁插件,希望对有需要的小伙伴有帮助~~~
文章目录
[1.1 组装查询条件](#1.1 组装查询条件)
[1.2 组装排序条件](#1.2 组装排序条件)
[1.3 组装删除条件](#1.3 组装删除条件)
[1.4 使用QueryWrapper实现修改功能](#1.4 使用QueryWrapper实现修改功能)
[1.5 条件优先级](#1.5 条件优先级)
[1.6 子查询](#1.6 子查询)
[1.7 使用UpdateWrapper实现修改功能](#1.7 使用UpdateWrapper实现修改功能)
[1.8 使用Condition组装条件](#1.8 使用Condition组装条件)
[1.9 LambdaQueryWrapper](#1.9 LambdaQueryWrapper)
[1.10 LambdaUpdateWrapper](#1.10 LambdaUpdateWrapper)
[2.1 基本使用](#2.1 基本使用)
[2.2 自定义分页插件](#2.2 自定义分页插件)
[3.1 乐观锁和悲观锁](#3.1 乐观锁和悲观锁)
[3.2 乐观锁插件](#3.2 乐观锁插件)
一、条件构造器
条件构造器,顾名思义就是用来封装当前我们用来查询的条件的,条件构造器的最顶层的接口是Mapper,被AbstractWrapper继承,其下由三个子类分别是:AbstractLambdaWrapper、UpdateWrapper和QueryWrapper。
1.1 组装查询条件
java
//条件构造器组装查询条件
@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 组装排序条件
java
//组装排序条件
@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 组装删除条件
java
//组装删除条件
@Test
public void test2(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.isNull("email");
int result = userMapper.delete(queryWrapper);
System.out.println("受影响函数"+result);
}
1.4 使用QueryWrapper实现修改功能
java
//实现修改功能
@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表达式中的条件优先执行。
java
//条件优先级
@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 子查询
java
//子查询
@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实现修改功能
java
//使用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
java
@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
java
@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 基本使用
分页插件的配置类
java
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;
}
}
测试类
java
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
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
<?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>
测试类
java
@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被修改了。
未加锁的场景模拟
实体类
java
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接口
java
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> {
}
测试类
java
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来设置版本号。
实体类
java
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()
java
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修改数据失败后需要重试即可完成任务需求。
java
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的相关基础知识收尾,同时尝试整合到学习的项目中,跟荔枝一起期待一波吧哈哈哈哈哈~~~
今朝已然成为过去,明日依然向往未来!我是小荔枝,在技术成长的路上与你相伴,码文不易,麻烦举起小爪爪点个赞吧哈哈哈~~~ 比心心♥~~~