在开发项目阶段中为了方便往后的维护与灵活使用,做一个通用的查询功能比较便捷。在mybatis里面动态的拼接sql完成多条件查询,一些特殊字符可以采用一些其他代码代替。
封装格式如下
[
{
"arrInt": [
0
],
"arrStr": [
"string"
],
"cond": "string",
"fieldName": "string",
"intFieldValue1": 0,
"intFieldValue2": 0,
"isAndConn": false,
"isEndOr": false,
"isStartOr": false,
"isStr": false,
"relation": "string",
"strFieldValue1": "string",
"strFieldValue2": "string"
}
]
cond条件: (主要是由于小于符号需要换别的标识代替转义)
SQL语句中的查询条件 | 传值时对应的标识代替 |
---|---|
< | lt |
<= | ltAndEq |
> | > |
>= | >= |
= | = |
!= | != |
like | like(like查询时,前端拼接好 百分号) |
in | in (查询时,前端拼接好(1,2), 也可以改为传数组) |
between | between |
isNull | isNull |
isNotNull | isNotNull |
示例:
[{
"fieldName": "docStatus", //字段名称是docStatus
"cond": "=", //条件是等于
"isStr": 1, //是字符串
"relation": "and", //连接条件是and
"strFieldValue1": "0" //字段值
}]
1.建立一个参数接受实体类
package com.gy.data.entity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.*;
import java.util.List;
@Data
@Builder
@ApiModel(value = "QueryParamBean" ,description = "通用查询")
public class QueryParamBean {
@ApiModelProperty(value = "字段名")
private String fieldName;
@ApiModelProperty(value = "判断条件符号(等于,小于,大于。。等一系列)")
private String cond;
@ApiModelProperty(value = "字段值1___存放字符串类型")
private String strFieldValue1;
@ApiModelProperty(value = "字段值2___存放字符串类型")
private String strFieldValue2;
@ApiModelProperty(value = "是否是字符串")
private Boolean isStr;
@ApiModelProperty(value = "是否是AND连接符号 只有 AND 与 OR")
private Boolean isAndConn;
@ApiModelProperty(value = "字段值1(存放数字类型)")
private Long intFieldValue1;
@ApiModelProperty(value = "字段值2(存放数字类型)")
private Long intFieldValue2;
@ApiModelProperty(value = "字符串数组,(cond = in时,配合 in 条件使用)")
private List<String> arrStr;
@ApiModelProperty(value = "数字类型数组,(cond = in时,配合 in 条件使用)")
private List<Integer> arrInt;
@ApiModelProperty(value = "是否是前半括号(与前端无关属性)")
private Boolean isStartOr;
@ApiModelProperty(value = "是否是后半括号(与前端无关属性)")
private Boolean isEndOr;
}
}
2.接受参数时,是一个数组接受
package com.gy.data.controller;
import com.google.common.base.Preconditions;
import com.gy.data.annotation.QueryParamMatchParenthesis;
import com.gy.data.entity.QueryParamBean;
import com.gy.data.mapper.gyv50read.GYV50ReadMapper;
import com.gy.data.mapper.gyv50read.GyV50OutStockMapper;
import com.gy.data.service.V50OutStockService;
import com.zlcloud.basedata.domain.OutStockMasterAndDetailDTO;
import com.zlcloud.basedata.domain.outofstockmaster.entity.OutOfStockMasterDTO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
/**
* 订单库存查询
*
* @Author SUN JIE
* @Date 8:39 2020-08-04
* @return
**/
@RequestMapping(value = "/V50OutStockController")
@RestController
@Api(value = "/V50OutStockController",tags = "成品出库单查询")
public class V50OutStockController {
@Autowired
private GyV50OutStockMapper gyV50OutStockMapper;
@QueryParamMatchParenthesis
@ApiOperation(value = "测试", httpMethod = "POST")
@PostMapping(value = "/test", consumes = "application/json", produces = "application/json;charset=UTF-8")
public List<Map> test(@RequestBody List<QueryParamBean> queryParamBeans) {
int a = 100;
return gyV50OutStockMapper.test(queryParamBeans);
}
}
3.自定义注解(配置方面也可以用其他接口实现与继承的方法进行封装)
package com.gy.data.annotation;
import java.lang.annotation.*;
/**
* 通用查询参数 括号匹配配置
*
* @Author SUN JIE
* @Date 15:16 2020-08-13
* @param null
* @return
**/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface QueryParamMatchParenthesis {
//这里可以设置一个接受类,继续做配置,使用注解时传入一个配置类
//例: Class value();
String value() default "";
}
2.注解的工具方法,使用切面方式解析注解,重新拼接小括号
package com.gy.data.aop;
import com.gy.data.entity.QueryParamBean;
import lombok.extern.java.Log;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Configuration;
import java.util.*;
/**
* 该注解是针对 拼接 WHERE 条件后, and 与 or条件分离
* 加上括号的处理。如果一般的查询不需要 or条件,该注解不用加上
* @Author SUN JIE
* @Date 15:16 2020-08-13
* @param null
* @return
**/
@Aspect
@Configuration
@Log
public class QueryParamMatchParenthesisAspect {
@Around("execution(public * *(..)) && @annotation(com.gy.data.annotation.QueryParamMatchParenthesis)")
public Object interceptor(ProceedingJoinPoint pjp) throws Throwable {
List<QueryParamBean> queryParamBean = new ArrayList<>();
// 参数值
Object[] args = pjp.getArgs();
for (int k = 0; k < args.length; k++) {
Object arg = args[k];
queryParamBean = castList(arg, QueryParamBean.class);
queryParamBean = setConnectSymbol(queryParamBean);
args[k] = queryParamBean;
}
return pjp.proceed(args);
}
public static <T> List<T> castList(Object obj, Class<T> clazz)
{
List<T> result = new ArrayList<T>();
if(obj instanceof List<?>)
{
for (Object o : (List<?>) obj)
{
result.add(clazz.cast(o));
}
return result;
}
return null;
}
public List<QueryParamBean> setConnectSymbol(List<QueryParamBean> queryParamBeans){
//判断前端是否有放状态值,没放默认为1
boolean isStart = false;
boolean isEnd = true;
List<QueryParamBean> newParamBeans = new ArrayList<>();
for(int i=0;i<queryParamBeans.size();i++){
//如果是最后一个参数则结束循环
QueryParamBean queryParamBean = QueryParamBean.builder().build();
BeanUtils.copyProperties(queryParamBeans.get(i),queryParamBean);
if(queryParamBeans.size()-1==i){
if(isStart){
queryParamBean.setIsEndOr(true);
}
queryParamBean.setIsAndConn(true);
newParamBeans.add(queryParamBean);
break;
}
//如果是结束的,并且是or则拼接
if(isEnd && !queryParamBeans.get(i).getIsAndConn()){
isStart = true;
isEnd = false;
queryParamBean.setIsStartOr(true);
}
//如果是已经开始or的,则非or就结束
if(isStart && !queryParamBeans.get(i).getIsAndConn()){
isStart = false;
isEnd = true;
queryParamBean.setIsEndOr(true);
}
newParamBeans.add(queryParamBean);
}
return newParamBeans;
}
}
3.这里的mapper接口因为多数据源的原因我用到多态,一般是不需要使用两个mapper接口
package com.gy.data.mapper.v50common;
import com.gy.data.entity.QueryParamBean;
import com.zlcloud.basedata.domain.outofstockdetail.entity.OutOfStockDetailDTO;
import com.zlcloud.basedata.domain.outofstockmaster.entity.OutOfStockMasterDTO;
import org.apache.ibatis.annotations.Param;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
import java.util.Map;
/**
* @author jayson
* @version 2.0
* @date 2020-08-10 11:03
*/
public interface V50OutStockCommMapper {
List<Map> test(@Param("queryParamBeans") List<QueryParamBean> queryParamBeans);
}
4.继承父类的mapper
package com.gy.data.mapper.gyv50read;
import com.gy.data.mapper.v50common.V50OutStockCommMapper;
/**
* @author jayson
* @version 2.0
* @date 2020-08-10 11:02
*/
public interface GyV50OutStockMapper extends V50OutStockCommMapper {
}
5.已经拼接好的条件,其他查询语句都可以使用
<sql id="Query_Model_ByCond">
<foreach collection="queryParamBeans" item="item" index="index">
<if test="item.cond == 'lt'.toString()">
<if test="item.isStartOr">(</if>
${item.fieldName} <![CDATA[ < ]]>
<choose>
<when test="item.isStr">#{item.strFieldValue1}</when>
<otherwise>#{item.intFieldValue1}</otherwise>
</choose>
<if test="item.isEndOr">)</if>
<if test="queryParamBeans.size()-1 != index">
<choose>
<when test="item.isAndConn"> AND </when>
<otherwise> OR </otherwise>
</choose>
</if>
</if>
<if test="item.cond == 'ltAndEq'.toString()">
<if test="item.isStartOr">(</if>
${item.fieldName} <![CDATA[ <= ]]>
<choose>
<when test="item.isStr">#{item.strFieldValue1}</when>
<otherwise>#{item.intFieldValue1}</otherwise>
</choose>
<if test="item.isEndOr">)</if>
<if test="queryParamBeans.size()-1 != index">
<choose>
<when test="item.isAndConn"> AND </when>
<otherwise> OR </otherwise>
</choose>
</if>
</if>
<if test="item.cond == '>'.toString()">
<if test="item.isStartOr">(</if>
${item.fieldName} >
<choose>
<when test="item.isStr">#{item.strFieldValue1}</when>
<otherwise>#{item.intFieldValue1}</otherwise>
</choose>
<if test="item.isEndOr">)</if>
<if test="queryParamBeans.size()-1 != index">
<choose>
<when test="item.isAndConn"> AND </when>
<otherwise> OR </otherwise>
</choose>
</if>
</if>
<if test="item.cond == '>='.toString()">
<if test="item.isStartOr">(</if>
${item.fieldName} >=
<choose>
<when test="item.isStr">#{item.strFieldValue1}</when>
<otherwise>#{item.intFieldValue1}</otherwise>
</choose>
<if test="item.isEndOr">)</if>
<if test="queryParamBeans.size()-1 != index">
<choose>
<when test="item.isAndConn"> AND </when>
<otherwise> OR </otherwise>
</choose>
</if>
</if>
<if test="item.cond == '='.toString()">
<if test="item.isStartOr">(</if>
${item.fieldName} =
<choose>
<when test="item.isStr">#{item.strFieldValue1}</when>
<otherwise>#{item.intFieldValue1}</otherwise>
</choose>
<if test="item.isEndOr">)</if>
<if test="queryParamBeans.size()-1 != index">
<choose>
<when test="item.isAndConn"> AND </when>
<otherwise> OR </otherwise>
</choose>
</if>
</if>
<if test="item.cond == '!='.toString()">
<if test="item.isStartOr">(</if>
${item.fieldName} <![CDATA[ <> ]]>
<choose>
<when test="item.isStr">#{item.strFieldValue1}</when>
<otherwise>#{item.intFieldValue1}</otherwise>
</choose>
<if test="item.isEndOr">)</if>
<if test="queryParamBeans.size()-1 != index">
<choose>
<when test="item.isAndConn"> AND </when>
<otherwise> OR </otherwise>
</choose>
</if>
</if>
<if test="item.cond == 'like'.toString()">
<if test="item.isStartOr">(</if>
${item.fieldName} LIKE #{item.strFieldValue1}
<if test="item.isEndOr">)</if>
<if test="queryParamBeans.size()-1 != index">
<choose>
<when test="item.isAndConn"> AND </when>
<otherwise> OR </otherwise>
</choose>
</if>
</if>
<if test="item.cond == 'in'.toString()">
<if test="item.isStartOr">(</if>
${item.fieldName} in
<foreach collection="item.arr" item="a" separator="," open="(" close=")">
#{a}
</foreach>
<if test="item.isEndOr">)</if>
<if test="queryParamBeans.size()-1 != index">
<choose>
<when test="item.isAndConn"> AND </when>
<otherwise> OR </otherwise>
</choose>
</if>
</if>
<if test="item.cond == 'between'.toString()">
<if test="item.isStartOr">(</if>
${item.fieldName} between
<choose>
<when test="item.isStr"> #{item.strFieldValue1} AND #{item.strFieldValue2} </when>
<otherwise> #{item.intFieldValue1} AND #{item.intFieldValue2} </otherwise>
</choose>
<if test="item.isEndOr">)</if>
<if test="queryParamBeans.size()-1 != index">
<choose>
<when test="item.isAndConn"> AND </when>
<otherwise> OR </otherwise>
</choose>
</if>
</if>
<if test="item.cond == 'isNull'.toString()">
<if test="item.isStartOr">(</if>
IFNULL(${item.fieldName},'0')='0'
<if test="item.isEndOr">)</if>
<choose>
<when test="item.isAndConn"> AND </when>
<otherwise> OR </otherwise>
</choose>
</if>
<if test="item.cond == 'isNotNull'.toString()">
<if test="item.isStartOr">(</if>
IFNULL(${item.fieldName},'0')<![CDATA[ <> ]]> '0'
<if test="item.isEndOr">)</if>
<if test="queryParamBeans.size()-1 != index">
<choose>
<when test="item.isAndConn"> AND </when>
<otherwise> OR </otherwise>
</choose>
</if>
</if>
</foreach>
</sql>
6:引入通用的查询sql条件
<select id="selectOrderByCond" parameterType="java.util.List">
select
a.*
FROM
test a
WHERE 1=1
<include refid="Query_Model_ByCond"></include>
</select>
结语:一个简单使用通用查询的工具得以实现,配合前端使用。在前端搜索框的条件进行调整时,后端不用继续调整。后端也可用作自己查询时使用该方法
package com.gy.newoms.utils;
import io.swagger.annotations.ApiModelProperty;
/**
* @author jayson
* @version 2.0
* @date 2020-08-13 12:30
*/
public class QueryParamBeanConfig {
//小于
public static String COND_LT = "lt";
//小于等于
public static String COND_LT_AND_EQ = "ltAndEq";
//大于
public static String COND_GT = ">";
//大于等于
public static String COND_GT_AND_EQ = ">=";
//等于
public static String COND_EQ = "=";
//不等于
public static String COND_NOT_EQ = "!=";
//模糊查询( 值自己加上百分号)
public static String COND_LIKE = "like";
//in 配合数组使用
public static String COND_IN = "in";
//在什么范围之间
public static String COND_BETWEEN = "between";
//是null值
public static String COND_IS_NULL = "isNull";
//不是null值
public static String COND_IS_NOT_NULL = "isNotNull";
//连接下一个条件(or)
public static String RELATION_OR = "OR";
//连接下一个条件(and)
public static String RELATION_AND = "AND";
}
private void setAcceptWhere(List<QueryParamBean> queryParamBeans, boolean b) {
QueryParamBean queryParamBean = QueryParamBean.builder().fieldName(OrderMasterFieldNameModel.FIELD_NAME_DATA_STATE)
.cond(QueryParamBeanConfig.COND_EQ)
.intFieldValue1(-1L)
.isAndConn(true)
.isStr(false)
.isStartOr(true).build();
queryParamBeans.add(queryParamBean);
QueryParamBean queryParamBean2 = QueryParamBean.builder().fieldName(OrderMasterFieldNameModel.FIELD_NAME_IS_CANCEL)
.cond(QueryParamBeanConfig.COND_EQ)
.intFieldValue1(0L)
.isAndConn(true)
.isStr(false)
.isEndOr(true).build();
queryParamBeans.add(queryParamBean2);
}