首页 > 其他分享 >mybatis-plus实用增强小工具

mybatis-plus实用增强小工具

时间:2024-03-11 22:15:40浏览次数:25  
标签:String private 实用 plus mybatis import null po Movement

一、背景

不知道各位在开发中是否遇到了这样的问题,就是一般在做一些查询功能的时候,特别是一些繁琐的信息采集类或者erp类系统,前段要传一大批查询条件,我们需要在代码里一遍遍的判空,然后在讲条件进去组合进去查询。

假设我们有以下实体类,前端将给我们传过来这个类,然后我们需要按照这个类的字段去数据库里查询相关数据。


import com.baomidou.mybatisplus.annotation.*;
import com.linzi.pitpat.base.utils.BeanUtil;
import lombok.Data;
import com.lz.mybatis.plugin.annotations.AS;

import java.math.BigDecimal;
import java.util.Date;import java.util.Date;

@Data
public class Movement implements java.io.Serializable {

    private Integer id;
    //动作名称
    private String movementName;
    //动作英文名称
    private String movementEnName;
    //动作难度
    private String movementLevel;
    //适用性别
    private Integer applicableGender;
    //动作类型
    private Integer movementType;
    //锻炼部位
    private String exercisePart;
    //锻炼肌肉
    private String exerciseMuscle;
    //所需器械
    private String requireMachine;
    //预计消耗热量
    private Integer heatConsumption;
    //动作视频
    private String video;
    //视频时长
    private String videoDuration;
    //封面 
    private String cover;
    //动作介绍
    private String movementIntroduction;
    //备注描述
    private String remark;
    //
    private Integer isDelete;
    //
    private Date gmtModified;
    //
    private Date gmtCreate;
    
}
      LambdaQueryWrapper<Movement> wrapper = new LambdaQueryWrapper<>();
        wrapper.like(StringUtils.isNotBlank(po.getMovementName()), Movement::getMovementName, po.getMovementName())
                .like(StringUtils.isNotBlank(po.getMovementEnName()), Movement::getMovementEnName, po.getMovementEnName())
                .eq(StringUtils.isNotBlank(po.getMovementLevel()), Movement::getMovementLevel, po.getMovementLevel())
                .eq(po.getApplicableGender() != null, Movement::getApplicableGender, po.getApplicableGender())
                .eq(po.getMovementType() != null, Movement::getMovementType, po.getMovementType())
                .ge(po.getExercisePart() != null, Movement::getExercisePart, po.getExercisePart())
                .ge(po.getExerciseMuscle() != null, Movement::getExerciseMuscle, po.getExerciseMuscle())
                .ge(po.getRequireMachine() != null, Movement::getRequireMachine, po.getRequireMachine())
                .ge(po.getVideo() != null, Movement::getVideo, po.getVideo())
                .ge(po.getCover() != null, Movement::getCover, po.getCover())
                .like(po.getMovementIntroduction() != null, Movement::getMovementIntroduction, po.getMovementIntroduction())
                .like(po.getRemark() != null, Movement::getRemark, po.getRemark())
                .le(po.getGmtCreate() != null, Movement::getGmtCreate, po.getGmtCreate())
                .orderByDesc(Movement::getGmtCreate);
 Page<Movement> page = new Page<>(po.getPageNum(), po.getPageSize());
        movementService.page(page, wrapper);

当做一些查询时,我们要写这种大量像抹布一样的代码,就姑且称之为抹布代码吧。其实仔细分析这些代码和我们要的业务需求,不难发现其实我们要的很简单,就是根据一些指定的字段按照指定的比较或者排序方式向数据库查询而已,而且大多数场景下这种查询是单表的。

当我们抽象出我们需要的东西后,事情就变得简单的。实体类我们有,那能不能有一种快捷的方式去指定查询实体类上每个字段的比较方式呢,是等值比较,还是模糊查询,或者说是大于小于,还有就是跟表里的那个字段比较,是否一定要比较呢?

很自然的,我们会想到用注解去实现这一idea,我们可以去写一个自定义注解,将其放在我们实体类的字段上,并表明这个字段需要跟表中的那个字段对应以及他的比较方式。

二、实现

import java.lang.annotation.*;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target( ElementType.FIELD)
public @interface Compare {
    String columnName() default "default";
    String compareType();

}

首先,我们拥有了自己的一个标记注解,接下来的工作就是让这个注解生效了。由于我们项目中使用的orm工具是mybatis-plus,所以下面我给一个基于mybatis-plus实现。


import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.linzi.pitpat.data.annotation.Compare;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.List;

public class MpUtil {


    public static <type> QueryWrapper<type> generateWrapper(Type type, Object source){
        return generateWrapper(type, source, null);
    }

    public static <type> QueryWrapper<type> generateWrapper(Type type, Object source, List<String> ignoreFields)  {
        QueryWrapper<type> wrapper = new QueryWrapper<>();
        List<signNode> nodes = getSignNodes(source);
        if (CollectionUtils.isNotEmpty(nodes)) {
            start(wrapper, nodes, ignoreFields);
        }
        return wrapper;
    }

    //启动组装wrapper
    private static <T> void start(QueryWrapper<T> wrapper, List<signNode> nodes, List<String> ignoreFields) {

        for (signNode node : nodes) {
            String column = node.getColumn();
            Object value = node.getValue();
            String symbol = node.getSymbol();

            //存在忽略集
            if (CollectionUtils.isNotEmpty(ignoreFields)) {
                //当前是否是忽略字段
                boolean isIgnoreField = ignoreFields.contains(column) || ignoreFields.contains(StrUtil.toCamelCase(column));
                if (isIgnoreField)
                    continue;
            }
            try {
                Method method = wrapper.getClass().getSuperclass().getDeclaredMethod(symbol, boolean.class, Object.class, Object.class);
                method.setAccessible(true);
                method.invoke(wrapper, true, column, value);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }


    }

    private static List<signNode> getSignNodes(Object source)  {
        ArrayList<signNode> list = new ArrayList<>();
        Class<?> cls = source.getClass();
        Field[] fields = cls.getDeclaredFields();

        for (Field field : fields) {
            field.setAccessible(true);
            // String不应该!=null做判空
            Object value = null;
            try {
                value = field.get(source);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            Compare compare = field.getAnnotation(Compare.class);

            if (notNull(value) && compare != null) {
                String columnName = compare.columnName();
                String compareType = compare.compareType();
                if ("default".equals(columnName)) {
                    columnName = StrUtil.toUnderlineCase(field.getName());
                }
                signNode node = new signNode();
                node.setColumn(columnName);
                node.setValue(value);
                node.setSymbol(compareType);
                list.add(node);
            }
        }
        return list;
    }

    private static boolean notNull(Object value) {
        return value instanceof String ? StringUtils.isNotBlank((CharSequence) value) : value != null;
    }

原理也很简单,我们首先通过字段上的注解拿到我们要去比较的字段,将其包装为node,然后看他是否为空,注意,在这里各个字段判空的条件不同,比如String类型用的是StringUtils.isNotBlank(),这个由开发者自己把握。然后我们解析获取这个字段要去表中对应着那个字段,根据注解上的columnName获取,默认情况下代表驼峰转下划线。接着,我们再拿到这个字段和表中字段的比较类型,是等于呢或者是大于小于。下一步我们在判断是否有忽略字段,因为在一些情况下,我们不希望某些字段进入筛选条件中,我们只需排除他即可。

最后就easy了,我们只要薅一层mybatis-plus的羊毛即可,利用反射的方式,将相关参数天好,返回一个可以被执行的QueryWrapper。至此,我们轻轻松松拿到了我们之前写了半天抹布代码的QueryWrapper,剩下的直接用或者进一步操作就看具体的需求咯。

最爽的是这种薅mybatis-plus羊毛方式的做法扩展性极强,但凡它支持的比较符号我基本都能支持,以后哪怕他怎么更新也是万变不离其宗,任尔东西南北风,我自微风清清。

下面给一个使用实例。

@Data
public class MovementPo extends BasePagePo {

    //动作名称
    @Compare(compareType = "like")
    private String movementName;
    //动作英文名称
    private String movementEnName;
    //动作难度
    private String movementLevel;
    //适用性别 性别,1:男,2:女,0:通用
    @Compare(compareType = "eq")
    private Integer applicableGender;
    //动作类型 0:无氧,1:有氧
    private Integer movementType;

    //起始时间
    private Date startTime;
    //结束时间
    @Compare(compareType = "le",columnName = "gmt_create")
    private Date endTime;

}
    @Test
    public void test() throws Exception {
        //构造条件
        MovementPo po = new MovementPo();
        po.setMovementName("高抬腿");
        po.setApplicableGender(0);
        po.setEndTime(new Date());

        //忽略比较的字段,可不传
        ArrayList<String> ignores = new ArrayList<>();
        ignores.add("applicable_gender");

        //生成wrapper
        QueryWrapper<Movement> wrapper = MpUtil.generateWrapper(Movement.class, po, ignores);
        List<Movement> list = movementService.list(wrapper);
        list.forEach(System.out::println);

    }
Movement{,id=62,movementName=高抬腿,movementLevel=3,applicableGender=1,movementType=1,exercisePart=[10,9,8],exerciseMuscle=[[1,12],[2,15],[2,16],[2,17],[3,18],[3,19],[3,20],[4,21],[4,22],[5,23],[5,24],[5,25],[5,42],[6,26],[6,27],[6,28],[6,29],[6,30]],requireMachine=[30,29,28],heatConsumption=12,video=12,videoDuration=12,cover=34,movementIntroduction=<h1>21</h1>,remark=null,isDelete=0,gmtModified=Thu Jun 29 09:44:34 CST 2023,gmtCreate=Sat Jun 17 16:52:55 CST 2023}
Movement{,id=63,movementName=高抬腿,movementLevel=3,applicableGender=1,movementType=1,exercisePart=[10,9,8],exerciseMuscle=[[1,12],[2,15],[2,16],[2,17],[3,18],[3,19],[3,20],[4,21],[4,22],[5,23],[5,24],[5,25],[5,42],[6,26],[6,27],[6,28],[6,29],[6,30]],requireMachine=[30,29,28],heatConsumption=12,video=12,videoDuration=12,cover=34movementIntroduction=<h1>21</h1>,remark=null,isDelete=0,gmtModified=Thu Jun 29 09:44:34 CST 2023,gmtCreate=Sat Jun 17 16:52:55 CST 2023}

有兴趣的老少年们可以自己去体验一下,代码不多,用的很爽。

 

 

标签:String,private,实用,plus,mybatis,import,null,po,Movement
From: https://www.cnblogs.com/fix200/p/18067188

相关文章

  • mybatis02 - 第一个Mybatis程序
     1、开发思路2、搭建环境2.1、数据库--创建数据库CREATEDATABASE`mybatis`;--切换数据库USE`mybatis`;--创建表CREATETABLEIFNOTEXISTS`user`(`id`INT(20)NOTNULLCOMMENT'序号',`name`VARCHAR(30)DEFAULTNULLCOMMENT'账号',`pwd`......
  • t04_mybatisplus
    一、快速入门准备数据DROPTABLEIFEXISTSuser;CREATETABLEuser(idBIGINT(20)NOTNULLCOMMENT'主键ID',nameVARCHAR(30)NULLDEFAULTNULLCOMMENT'姓名',ageINT(11)NULLDEFAULTNULLCOMMENT'年龄',emailVARCHAR(50)......
  • mybatis01 - 简介
      1.1、什么是Mybatis?MyBatis是一款优秀的持久层框架。它支持定制化SQL、存储过程以及高级映射。MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。MyBatis可以使用简单的XML或注解来配置和映射原生类型、接口和Java的POJO(PlainOldJavaObject,普通老式Ja......
  • Mybatis
      配置数据库连接信息(在springboot工程的application.properties文件中配置)*注意项:将数据库名字和密码改成自己的  @SpringBootTest: 是springboot整合单元测试的注解在接口上加@Mapper注解,在运行时,会自动生成该接口的实现类对象(代理对象),并且将该对象交给IOC容器......
  • 狂神说Java——Mybatis学习笔记
    前言:配合狂神老师的教学视频使用效果更佳:https://www.bilibili.com/video/BV1NE411Q7Nx/?spm_id_from=333.1007.top_right_bar_window_custom_collection.content.click&vd_source=4c3c519d33c113799489c7417a0a4c0e1、简介环境说明:jdk8+MySQL5.7.19maven-3.6.......
  • 三、MyBatis基础配置之映射文件Mapper.xml(均为单表查询示例)
    一)动态if需求:多条件组合查询。  二)动态foreach需求:多值查询。  三)动态抽取......
  • Mybatis逆向工程
    Mybatis逆向工程mybatis逆向工程是一个可以快速根据数据库表帮我们生成pojo实体类和mapper接口和mapper映射文件的一个插件,需要下载该项目。注意:只支持单表操作(单表的增删改查等sql可以帮助我们生成),关联查询需要自己写。建议查阅tk-mybatis更加好用的工具。引入依赖<!--mybat......
  • catch mybatis 异常
    Spring的DAO异常层次异常                                何时抛出CleanupFailureDataAccessException 一项操作成功地执行,但在释放数据库资源时发生异常(例如,关闭一个Connection)DataAccessRe......
  • Python正则表达式的语句具体的实用例子_2
    importres="""<divclass='jay'><spanid='1'>周杰伦</span></div><divclass='augus'><spanid='2'>马宁</span></div><divclass='tom'><s......
  • Java 日期和时间 API:实用技巧与示例 - 轻松处理日期和时间
    Java用户输入(Scanner)简介Scanner类用于获取用户输入,它位于java.util包中。使用Scanner类要使用Scanner类,请执行以下步骤:导入java.util.Scanner包。创建一个Scanner对象,并将其初始化为System.in。使用Scanner对象的方法读取用户输入。示例importjava.ut......