简单的通用查询设计

1.数据设计

写的不是很好,如果能对现在的你有一些启发或帮助,可以点个关注啊!

{
    // 表信息
    "tableInfo":{
        // 库名
        "dbName": "szxc_dc",
        // 表名称
        "tableName":"dig_customized_data",
        // 列名
        "column":"id, content, component_code, county_code",
        // 逻辑删除字段
        "logicDelete":"is_deleted",
         // 逻辑非删除值
        "logicValue":"N", 
         // json列
        "jsonColumn":"content",
         // 是否包含json列
        "hasJsonColumn":true
    },
    // 操作类型
    "optionalType":"SELECT",
    // 查询字段详情
    "columnDetails":[
        {
            // 是否必选参数
            "isMust":false, 
            // 参数名
            "paramName":"projectName", 
            // 参数类型
            "paramType":"string",
            // 比较类型
            "compareType":"like",
            // 是否为sql字段
            "isSqlConcat":true,
             // 数据库映射字段
            "mappingColumn":"projectName",
            // 是否为json列
            "isJsonColumn":true
        },
        {
            "isMust":false,
            "pattern":"-?[1-9]\d*",
            "paramName":"minTotal",
            "paramType":"number",
            "compareType":"ge",
            "isSqlConcat":true,
            "mappingColumn":"investment",
            "isJsonColumn":true
        },
        {
            "isMust":false,
            "pattern":"-?[1-9]\d*",
            "paramName":"maxTotal",
            "paramType":"number",
            "compareType":"le",
            "isSqlConcat":true,
            "mappingColumn":"investment",
            "isJsonColumn":true
        },
        {
            "isMust":false,
            "minLength":1,
            "paramName":"statusCode",
            "paramType":"string",
            "compareType":"eq",
            "isSqlConcat":true,
            "mappingColumn":"statusCode",
            "isJsonColumn":true
        },
        {
            "isMust":false,
            "minLength":1,
            "paramName":"projectType",
            "paramType":"string",
            "compareType":"locate",
            "isSqlConcat":true,
            "mappingColumn":"projectType",
            "isJsonColumn":true
        },
        {
            "isMust":true,
            "pattern":"-?[1-9]\d*",
            "minNumber":0,
            "paramName":"pageSize",
            "paramType":"number",
            "isSqlConcat":false
        },
        {
            "isMust":true,
            "pattern":"-?[1-9]\d*",
            "minNumber":0,
            "paramName":"pageNum",
            "paramType":"number",
            "isSqlConcat":false
        },
        {
            "isMust":true,
            "minNumber":0,
            "paramName":"componentCode",
            "paramType":"string",
            "compareType":"eq",
            "isSqlConcat":true,
            "mappingColumn":"component_code",
            "isJsonColumn":false
        },
        {
            "isMust":true,
            "minNumber":0,
            "paramName":"countyCode",
            "paramType":"number",
            "compareType":"eq",
            "isSqlConcat":true,
            "mappingColumn":"county_code",
            "isJsonColumn":false
        }
    ]
}

2.实体

@Builder
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
public class QueryWrapper {

    private TableInfo tableInfo;

    private String optionalType;

    private List<QueryValidatorData> columnDetails;
}

@Builder
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
public class TableInfo {
    
    private String dbName;

    private String tableName;

    private String column;

    private String logicDelete;

    private String logicValue;

    private String jsonColumn;

    private Boolean hasJsonColumn;
}

@Builder
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
public class QueryValidatorData {

    private String paramName;

    private String paramType;

    private Boolean isMust;

    private String pattern;

    private Boolean isSqlConcat;

    private String compareType;

    private Integer minLength;

    private Integer minNumber;

    private String mappingColumn;

    private Boolean isJsonColumn;



    public static String compareTypeParse(String compareType) {
        switch (compareType) {
            case "ge" :
                return ">=";
            case "gt" :
                return ">";
            case "le" :
                return "<=";
            case "lt" :
                return "<";
            case "eq" :
                return "=";
            case "like" :
                return "like";
            default:
                throw new IllegalArgumentException("非法参数类型");
        }
    }
}

3.参数校验

package org.example.universal;

import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

/**
 * 描述:
 *
 * @author : sunyubin
 * @date : 2020/8/10 09:29
 **/
public class UniversalSchemaValidator {

    /** 参数校验 */
    public static void validation(List<QueryValidatorData> queryParams, Map<String, Object> param) {
        if (CollectionUtils.isEmpty(queryParams)) {
            return;
        }
        // 遍历校验参数, 拼接sql查询条件
        for (QueryValidatorData validation : queryParams) {
            String paramName = validation.getParamName();
            Boolean isMust = validation.getIsMust();
            Object paramValue = param.get(paramName);
            String paramType = validation.getParamType();
            Assert.hasLength(paramType, "参数有误,请检查!");

            // 因查询接口使用到类型基本可以划分为 string 和 date 和 number(Integer, Long)
            // string 和 date 使用较多,其中date规则行较强
            // 且查询接口中除固定参数外,其他参数都为非必填且参数后续需拼接sql,
            // 所以先校验其是否必选,非必选且参数值为null或""时直接跳过
            if (isMust) {
                if (Objects.equals(paramType, UniversalDataConst.STRING_TYPE)
                        || Objects.equals(paramType, UniversalDataConst.DATE_TYPE)) {
                    if (StringUtils.isEmpty(paramValue)) {
                        throw new IllegalArgumentException("参数有误, 请检查!");
                    }
                }
                else if (Objects.equals(paramType, UniversalDataConst.NUMBER_TYPE)) {
                    if (Objects.isNull(paramValue)) {
                        throw new IllegalArgumentException("参数有误, 请检查!");
                    }
                }
            }
            // 预防空字符串
            if (StringUtils.isEmpty(Objects.isNull(paramValue) ? null : paramValue.toString())) {
                continue;
            }
            Boolean isSqlConcat = validation.getIsSqlConcat();
            Assert.notNull(isSqlConcat, "校验参数类型缺失");
            if (!isSqlConcat) {
                continue;
            }

            String compareType = validation.getCompareType();
            Assert.notNull(compareType, "校验参数类型缺失");

            String pattern = validation.getPattern();
            if (null != pattern && pattern.length() > 0) {
                if (Objects.equals(paramType, UniversalDataConst.NUMBER_TYPE)) {
                    // Integer Long 等number格式校验
                    boolean matches = Pattern.matches(pattern, paramValue.toString());
                    // 指定最小值比较
                    Integer minNumber = validation.getMinNumber();
                    boolean isGe = true;
                    if (Objects.nonNull(minNumber)) {
                       isGe = (Integer)paramValue >= minNumber;
                    }

                    if (!matches || !isGe) {
                        throw new PatternSyntaxException("数字格式有误", pattern, 0);
                    }
                }
                if (Objects.equals(paramType, UniversalDataConst.DATE_TYPE)) {
                    // 日期格式校验
                    boolean matches = Pattern.matches(pattern, paramValue.toString());
                    if (!matches) {
                        throw new PatternSyntaxException("日期格式有误", pattern, 0);
                    }
                }
                if (Objects.equals(paramType, UniversalDataConst.STRING_TYPE)) {
                    // string 校验
                    int length = paramValue.toString().length();
                    Integer minLength = validation.getMinLength();
                    if (length < minLength) {
                        throw new IllegalArgumentException("参数格式有误");
                    }
                }
            }

        }
    }


}

4.sql解析

package org.example.universal;

import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 描述:
 *
 * @author : sunyubin
 * @date : 2020/8/18 09:19
 **/
public class SqlParse {

    /** 解析参数为sql字符串 */
    public static String parse(QueryWrapper qw, Map<String, Object> param) {
        StringBuilder sb = new StringBuilder();
        String jsonColumn = null;
        TableInfo tableInfo = qw.getTableInfo();
        sb.append("SELECT ").append(tableInfo.getColumn()).append(" FROM ")
                .append(tableInfo.getTableName()).append(" ");
        List<QueryValidatorData> queryParams = qw.getColumnDetails();
        if (CollectionUtils.isEmpty(queryParams)) {
            return "";
        }

        if (tableInfo.getHasJsonColumn()) {
            jsonColumn = tableInfo.getJsonColumn();
        }

        int firstFlag = 0;
        // 根据映射数据获取参数中数据, 拼接字符串
        for (QueryValidatorData validation : queryParams) {
            String paramName = validation.getParamName();
            Object paramValue = param.get(paramName);
            String paramType = validation.getParamType();
            // 预防空字符串
            if (StringUtils.isEmpty(Objects.isNull(paramValue) ? null : paramValue.toString())) {
                continue;
            }
            Boolean isSqlConcat = validation.getIsSqlConcat();
            Assert.notNull(isSqlConcat, "校验参数类型缺失");
            if (!isSqlConcat) {
                continue;
            }

            String compareType = validation.getCompareType();
            Assert.notNull(compareType, "校验参数类型缺失");
            String compare = QueryValidatorData.compareTypeParse(compareType);
            String mappingColumn = validation.getMappingColumn();
            boolean isJsonColumn = validation.getIsJsonColumn();

            if (StringUtils.hasLength(tableInfo.getLogicDelete()) && 0 == firstFlag) {
               sb.append("WHERE ").append(tableInfo.getLogicDelete())
                      .append(" = '").append(tableInfo.getLogicValue()).append("' ");
                firstFlag++;
            }
            if (0 == firstFlag) {
                sb.append("WHERE ");
                if (isJsonColumn) {
                    if (Objects.equals(compare, UniversalDataConst.LOCATE)) {
                        sb.append(" LOCATE('").append(paramValue).append("', ").append(jsonColumn)
                                .append(" ->> '$.").append(mappingColumn).append("') ");
                    }
                    else {
                        sb.append(jsonColumn).append(" ->> '$.").append(mappingColumn).append("'");
                    }
                }
                else {
                    if (Objects.equals(compare, UniversalDataConst.LOCATE)) {
                        sb.append(" LOCATE('").append(paramValue).append("', ").append(mappingColumn).append("') ");
                    }
                    else {
                        sb.append(mappingColumn).append(" ");
                    }
                }
            }
            else {
                sb.append("AND ");
                if (isJsonColumn) {
                    if (Objects.equals(compare, UniversalDataConst.LOCATE)) {
                        sb.append(" LOCATE('").append(paramValue).append("', ").append(jsonColumn)
                                .append(" ->> '$.").append(mappingColumn).append("') ");
                    }
                    else {
                        sb.append(jsonColumn).append(" ->> '$.").append(mappingColumn).append("' ");
                    }
                }
                else {
                    if (Objects.equals(compare, UniversalDataConst.LOCATE)) {
                        sb.append(" LOCATE('").append(paramValue).append("', ").append(mappingColumn).append(") ");
                    }
                    else {
                        sb.append(mappingColumn).append(" ");
                    }
                }
            }
            firstFlag++;
            if (Objects.equals(compare, UniversalDataConst.RIGHT_LIKE)) {
                sb.append("LIKE ").append("'").append(paramValue).append("%' ");
            }
            else if (Objects.equals(compare, UniversalDataConst.LOCATE)) {
               continue;
            }
            else {
                sb.append(compare).append(" ");
                if (Objects.equals(paramType, "string") || Objects.equals(paramType, "date")) {
                    sb.append("'").append(paramValue).append("' ");
                }
                else if (Objects.equals(paramType, "number")) {
                    sb.append(" ").append(paramValue).append(" ");
                }
                else {
                    throw new IllegalArgumentException("非法参数");
                }
            }
        }
        return sb.toString();
    }


}

5.执行

1.参数

Map<String, Object> param = Maps.newHashMap();
        param.put("componentCode", "PROJECT");
        param.put("countyCode", 330108);
        param.put("pageNum", 1);
        param.put("pageSize", 10);
        param.put("projectName", "projectName测试1");
        param.put("minTotal", 30);
        param.put("maxTotal", 50);
        param.put("statusCode", "statusCode测试2");
        param.put("projectType", "projectType测试3");

2.解析后sql

SELECT id, content, component_code, county_code 
FROM dig_customized_data 
WHERE is_deleted = 'N' 
AND content ->> '$.projectName' LIKE  'projectName测试1%'
AND content ->> '$.investment' >= 30 
AND content ->> '$.investment' <= 50 
AND content ->>' $.statusCode' = 'statusCode测试2' 
AND content ->> '$.projectType' = 'projectType测试3'
AND componentCode = 'PROJECT'
AND countyCode = 330108
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值