MyBatis-Plus深入 —— 条件构造器与插件管理

前言

在前面的文章中,荔枝梳理了一个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的相关基础知识收尾,同时尝试整合到学习的项目中,跟荔枝一起期待一波吧哈哈哈哈哈~~~

今朝已然成为过去,明日依然向往未来!我是小荔枝,在技术成长的路上与你相伴,码文不易,麻烦举起小爪爪点个赞吧哈哈哈~~~ 比心心♥~~~

相关推荐
路在脚下@40 分钟前
spring boot的配置文件属性注入到类的静态属性
java·spring boot·sql
森屿Serien43 分钟前
Spring Boot常用注解
java·spring boot·后端
苹果醋32 小时前
React源码02 - 基础知识 React API 一览
java·运维·spring boot·mysql·nginx
Hello.Reader2 小时前
深入解析 Apache APISIX
java·apache
菠萝蚊鸭2 小时前
Dhatim FastExcel 读写 Excel 文件
java·excel·fastexcel
旭东怪3 小时前
EasyPoi 使用$fe:模板语法生成Word动态行
java·前端·word
007php0073 小时前
Go语言zero项目部署后启动失败问题分析与解决
java·服务器·网络·python·golang·php·ai编程
∝请叫*我简单先生3 小时前
java如何使用poi-tl在word模板里渲染多张图片
java·后端·poi-tl
ssr——ssss3 小时前
SSM-期末项目 - 基于SSM的宠物信息管理系统
java·ssm
一棵星3 小时前
Java模拟Mqtt客户端连接Mqtt Broker
java·开发语言