mybaties-plus实现复杂的多分组多条件且支持多字段和json字段多属性的查询

mybaties-plus实现复杂的多分组多条件且支持多字段和json字段多属性的查询

image

实现方案说明

  1. 简单场景使用map集合接收参数,以BaseEntity为例(BaseEntity开源框架中很常见)
    @TableField(exist = false)
    private Map<String, Object> params = new HashMap<>();
  1. 复杂场景使用嵌套分组方案实现复杂查询,将BaseEntity实体下新增如下字段(新增是为了兼容params
    @TableField(exist = false)
    private QueryParameter queryParameter;
  1. 实际上好的框架应该代码优雅并保持绝的的低耦合、通用性和扩展性,所以分三步构建查询条件:
    3.1 使用Wrappers.lambdaQuery()构建实体相关的LambdaQueryWrapper实列,以实现基本查询
    3.2 使用LambdaQueryWrapper实列构建params的较复杂的支持多字段和json字段多属性的查询语句
    3.3 继续使用LambdaQueryWrapper实列构建复杂的多分组多条件且支持多字段和json字段多属性的查询语句

支持常见的操作符(比较运算符和逻辑运算符)

  • 在构建查询条件时,常见的操作符(比较运算符和逻辑运算符)可以非常丰富,以下是完整的常用操作符分类整理,本工具实现了其中大部分:

  1. 基础比较运算符
    | 操作符 | SQL 等价 | 说明 | 示例 |
    |--------------|-------------------|--------------------------|-------------------------------|
    | eq | = | 等于 | age = 18 |
    | ne | !=<> | 不等于 | status != 0 |
    | gt | > | 大于 | price > 100 |
    | lt | < | 小于 | score < 60 |
    | ge | >= | 大于等于 | quantity >= 10 |
    | le | <= | 小于等于 | age <= 30 |

  1. 模糊匹配与字符串操作
    | 操作符 | SQL 等价 | 说明 | 示例 |
    |----------------|------------------------|--------------------------|-------------------------------|
    | like | LIKE | 模糊匹配 | name LIKE '%张%' |
    | notLike | NOT LIKE | 模糊不匹配 | title NOT LIKE '%test%' |
    | startsWith | LIKE 'value%' | 开头匹配 | code LIKE 'A00%' |
    | endsWith | LIKE '%value' | 结尾匹配 | path LIKE '%/home' |
    | contains | LIKE '%value%' | 包含(同 like) | desc LIKE '%重要%' |
    | in | IN (...) | 在列表中 | id IN (1, 2, 3) |
    | notIn | NOT IN (...) | 不在列表中 | status NOT IN (0, 1) |

  1. 空值判断
    | 操作符 | SQL 等价 | 说明 | 示例 |
    |--------------|-------------------|--------------------------|-------------------------------|
    | isNull | IS NULL | 为 NULL | address IS NULL |
    | notNull | IS NOT NULL | 不为 NULL | phone IS NOT NULL |
    | isEmpty | = ''IS NULL | 空字符串或 NULL | comment = '' OR comment IS NULL |
    | isNotEmpty | != ''IS NOT NULL | 非空且不为 NULL | comment != '' AND comment IS NOT NULL |

  1. 范围查询
    | 操作符 | SQL 等价 | 说明 | 示例 |
    |--------------|------------------------|--------------------------|-------------------------------|
    | between | BETWEEN x AND y | 介于范围 | age BETWEEN 18 AND 30 |
    | notBetween | NOT BETWEEN x AND y | 不在范围内 | price NOT BETWEEN 50 AND 100|

  1. 集合操作
    | 操作符 | SQL 等价 | 说明 | 示例 |
    |--------------|------------------------|--------------------------|-------------------------------|
    | in | IN (...) | 在集合内 | id IN (1, 2, 3) |
    | notIn | NOT IN (...) | 不在集合内 | category NOT IN ('A', 'B') |

  1. JSON/数组字段操作
    (适用于 MySQL、PostgreSQL 等支持 JSON 的数据库)
    | 操作符 | SQL 等价 | 说明 | 示例 |
    |----------------|-----------------------------------|--------------------------|-------------------------------|
    | jsonContains | JSON_CONTAINS(field, 'value') | JSON 包含值 | tags JSON_CONTAINS '"VIP"' |
    | jsonExists | JSON_EXISTS(field, '$.path') | JSON 路径存在 | profile JSON_EXISTS '$.address' |

  1. 逻辑组合运算符
    | 操作符 | 说明 | 示例 |
    |--------------|--------------------------|-------------------------------|
    | AND | 逻辑与(默认) | status = 1 AND price > 100 |
    | OR | 逻辑或 | name LIKE '%张%' OR age < 18|
    | NOT | 逻辑非 | NOT (deleted = 1) |

  1. 特殊场景操作符
    | 操作符 | 说明 | 示例 |
    |----------------|--------------------------|-------------------------------|
    | regexp | 正则匹配(数据库支持时) | name REGEXP '^A.*' |
    | exists | 子查询存在 | EXISTS (SELECT 1 FROM orders) |
    | notExists | 子查询不存在 | NOT EXISTS (SELECT 1 FROM logs) |

  1. 示例(JSON 请求格式)
{
  "conditions": [
    { "field": "age", "operator": "ge", "value": 18 },
    { "field": "name", "operator": "like", "value": "张%" },
    { 
      "logic": "OR",
      "conditions": [
        { "field": "vip", "operator": "eq", "value": true },
        { "field": "points", "operator": "ge", "value": 1000 }
      ]
    }
  ]
}

简单场景使用map集合接收参数,如BaseEntity.java

package com.xxx.entity;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;

import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * Entity基类
 */
@Data
public class BaseEntity implements Serializable {

    @Serial
    private static final long serialVersionUID = 1L;

    // 其他需求字段

    /**
     * 请求参数
     */
    @TableField(exist = false)
    private Map<String, Object> params = new HashMap<>();

    /**
     * 请求参数,支持分组
     */
    @TableField(exist = false)
    private QueryParameter queryParameter;

}

新建一个条件嵌套参数QueryParameter.java

  • 该类使用说明:
  1. 同一级别的多个条件conditions以及多个组groups之间由该级别的logic属性决定连接关系
  2. 每个组内group的多个子条件group.conditions和子组group.groups被视为平级,由组的group.logic属性决定连接关系
  3. 嵌套组自动添加括号,非嵌套条件不添加额外括号。如果是OR链接可能会由多个括号,因为OR链接时每个部分都会调用wrapper.or(part)
package com.xxx.entity;

import lombok.Data;

import java.util.List;

@Data
public class QueryParameter {
    /**
     * 逻辑操作符(AND/OR/NOT)
     */
    private String logic;

    /**
     * 条件列表
     */
    private List<Condition> conditions;

    /**
     * 嵌套的逻辑组
     */
    private List<QueryParameter> groups;

    @Data
    public final static class Condition {
        /**
         * 字段名
         */
        private String field;

        /**
         * 操作符(eq/ne/gt/ge/lt/le/like等)
         */
        private String operator;

        /**
         * 值
         */
        private Object value;
    }
}

新建LambdaQuery构建工具类MixedQueryWrapper.java

package org.dromara.product.util;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.dromara.common.mybatis.core.domain.QueryParameter;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Field;
import java.util.*;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 查询条件构建工具类
 * 支持实体字段和JSON属性的混合查询
 */
@Slf4j
public class MixedQueryWrapper {


    private static final Pattern PARAM_PATTERN = Pattern.compile("^(gt|lt|eq|ge|le|contains|like)([A-Z].*)$");

    private static final Map<String, OperatorProcessor> OPERATOR_HANDLERS = new HashMap<>();
    private static final Pattern RANGE_PATTERN = Pattern.compile("^(.+)~(.+)$");

    // 操作符处理器接口
    @FunctionalInterface
    interface OperatorProcessor {
        void apply(LambdaQueryWrapper<?> lqw, String column, Object value) throws IllegalArgumentException;
    }

    static {
        // ========== 基础比较运算符 ==========
        putOperator("eq", (lqw, c, v) -> lqw.apply(c + " = {0}", v));
        putOperator("ne", (lqw, c, v) -> lqw.apply(c + " != {0}", v));
        putOperator("gt", (lqw, c, v) -> lqw.apply(c + " > {0}", v));
        putOperator("lt", (lqw, c, v) -> lqw.apply(c + " < {0}", v));
        putOperator("ge", (lqw, c, v) -> lqw.apply(c + " >= {0}", v));
        putOperator("le", (lqw, c, v) -> lqw.apply(c + " <= {0}", v));

        // ========== 模糊匹配运算符 ==========
        putOperator("like", (lqw, c, v) -> lqw.apply(c + " LIKE {0}", "%" + v + "%"));
        putOperator("notLike", (lqw, c, v) -> lqw.apply(c + " NOT LIKE {0}", "%" + v + "%"));
        putOperator("startsWith", (lqw, c, v) -> lqw.apply(c + " LIKE {0}", v + "%"));
        putOperator("endsWith", (lqw, c, v) -> lqw.apply(c + " LIKE {0}", "%" + v));
        putOperator("contains", (lqw, c, v) -> lqw.apply(c + " LIKE {0}", "%" + v + "%"));

        // ========== 范围查询运算符 ==========
        putOperator("between", (lqw, c, v) -> {
            if (v instanceof String && RANGE_PATTERN.matcher((String) v).matches()) {
                String[] range = ((String) v).split("~");
                lqw.apply(c + " BETWEEN {0} AND {1}", range[0], range[1]);
            } else if (v instanceof Object[] range && range.length == 2) {
                lqw.apply(c + " BETWEEN {0} AND {1}", range[0], range[1]);
            } else {
                throw new IllegalArgumentException("Between operator requires 'min~max' format or 2-element array");
            }
        });

        // ========== 集合运算符 ==========
        putOperator("in", (lqw, c, v) -> {
            if (v instanceof Iterable<?> iterable) {
                String placeholders = String.join(",", Collections.nCopies(
                    size(iterable), "?"));
                lqw.apply(c + " IN (" + placeholders + ")", toArray(iterable));
            } else if (v instanceof Object[] array) {
                String placeholders = String.join(",", Collections.nCopies(array.length, "?"));
                lqw.apply(c + " IN (" + placeholders + ")", array);
            } else {
                throw new IllegalArgumentException("IN operator requires collection or array");
            }
        });

        putOperator("notIn", (lqw, c, v) -> {
            if (v instanceof Iterable<?> iterable) {
                String placeholders = String.join(",", Collections.nCopies(
                    size(iterable), "?"));
                lqw.apply(c + " NOT IN (" + placeholders + ")", toArray(iterable));
            } else if (v instanceof Object[] array) {
                String placeholders = String.join(",", Collections.nCopies(array.length, "?"));
                lqw.apply(c + " NOT IN (" + placeholders + ")", array);
            } else {
                throw new IllegalArgumentException("NOT IN operator requires collection or array");
            }
        });

        // ========== 空值判断运算符 ==========
        putOperator("isNull", (lqw, column, value) -> lqw.apply("{0} IS NULL", column));
        putOperator("isNotNull", (lqw, column, value) -> lqw.apply("{0} IS NOT NULL", column));
        putOperator("isEmpty", (lqw, c, v) -> lqw.apply(c + " IS NULL OR " + c + " = ''"));
        putOperator("isNotEmpty", (lqw, c, v) -> lqw.apply(c + " IS NOT NULL AND " + c + " != ''"));

        // ========== JSON/特殊类型运算符 ==========
        putOperator("jsonContains", (lqw, c, v) -> lqw.apply("JSON_CONTAINS(" + c + ", {0})", v));
        putOperator("jsonExists", (lqw, c, v) -> lqw.apply("JSON_EXISTS(" + c + ", {0})", v));
    }

    private static void putOperator(String key, OperatorProcessor processor) {
        OPERATOR_HANDLERS.put(key.toLowerCase(), processor);
        // 添加别名
        switch (key) {
            case "eq":
                OPERATOR_HANDLERS.put("=", processor);
                break;
            case "ne":
                OPERATOR_HANDLERS.put("!=", processor);
                break;
            case "gt":
                OPERATOR_HANDLERS.put(">", processor);
                break;
            case "lt":
                OPERATOR_HANDLERS.put("<", processor);
                break;
            case "ge":
                OPERATOR_HANDLERS.put(">=", processor);
                break;
            case "le":
                OPERATOR_HANDLERS.put("<=", processor);
                break;
        }
    }

    private static int size(Iterable<?> iterable) {
        if (iterable instanceof Collection) {
            return ((Collection<?>) iterable).size();
        }
        int size = 0;
        for (Object ignored : iterable) {
            size++;
        }
        return size;
    }

    private static Object[] toArray(Iterable<?> iterable) {
        if (iterable instanceof Collection) {
            return ((Collection<?>) iterable).toArray();
        }
        List<Object> list = new ArrayList<>();
        for (Object item : iterable) {
            list.add(item);
        }
        return list.toArray();
    }

    public static <T> LambdaQueryWrapper<T> buildMixedQueryWrapper(
        Class<T> entityClass,
        QueryParameter queryParameter,
        LambdaQueryWrapper<T> lqw) {

        if (queryParameter == null ||
            (CollectionUtils.isEmpty(queryParameter.getConditions()) &&
                CollectionUtils.isEmpty(queryParameter.getGroups()))) {
            return lqw;
        }

        // 获取实体类的所有字段名(驼峰式)
        Set<String> entityFields = Arrays.stream(entityClass.getDeclaredFields())
            .map(Field::getName)
            .map(StringUtils::uncapitalize)
            .collect(Collectors.toSet());

        // 处理当前层条件
        Consumer<LambdaQueryWrapper<T>> currentConsumer = wrapper -> {
            String currentLogic = StringUtils.defaultIfBlank(queryParameter.getLogic(), "AND");
            boolean isOrLogic = "OR".equalsIgnoreCase(currentLogic);

            // 1. 处理直接条件
            if (!CollectionUtils.isEmpty(queryParameter.getConditions())) {
                for (QueryParameter.Condition condition : queryParameter.getConditions()) {
                    if (isOrLogic) {
                        wrapper.or(w -> addCondition(w, entityClass, condition, entityFields));
                    } else {
                        addCondition(wrapper, entityClass, condition, entityFields);
                    }
                }
                // if (isOrLogic) {
                //     // OR逻辑时,所有条件用OR连接
                //     for (QueryParameter.Condition condition : queryParameter.getConditions()) {
                //         wrapper.or(w -> addCondition(w, entityClass, condition, entityFields));
                //     }
                // } else {
                //     // AND逻辑时,直接添加条件
                //     for (QueryParameter.Condition condition : queryParameter.getConditions()) {
                //         addCondition(wrapper, entityClass, condition, entityFields);
                //     }
            }

            // 2. 处理嵌套组
            if (!CollectionUtils.isEmpty(queryParameter.getGroups())) {
                for (QueryParameter group : queryParameter.getGroups()) {
                    String groupLogic = StringUtils.defaultIfBlank(group.getLogic(), "AND");
                    boolean isGroupOr = "OR".equalsIgnoreCase(groupLogic);

                    if (isOrLogic) {
                        wrapper.or(groupWrapper ->
                            processGroup(groupWrapper, entityClass, group, entityFields, isGroupOr)
                        );
                    } else {
                        wrapper.and(groupWrapper ->
                            processGroup(groupWrapper, entityClass, group, entityFields, isGroupOr)
                        );
                    }
                }
            }
        };

        currentConsumer.accept(lqw);
        return lqw;
    }

    private static <T> void processGroup(
        LambdaQueryWrapper<T> wrapper,
        Class<T> entityClass,
        QueryParameter group,
        Set<String> entityFields,
        boolean isGroupOr) {

        // 处理组内条件
        if (!CollectionUtils.isEmpty(group.getConditions())) {
            for (QueryParameter.Condition condition : group.getConditions()) {
                if (isGroupOr) {
                    wrapper.or(w -> addCondition(w, entityClass, condition, entityFields));
                } else {
                    addCondition(wrapper, entityClass, condition, entityFields);
                }
            }
        }

        // 递归处理嵌套组
        if (!CollectionUtils.isEmpty(group.getGroups())) {
            for (QueryParameter subGroup : group.getGroups()) {
                String subLogic = StringUtils.defaultIfBlank(subGroup.getLogic(), "AND");
                boolean isSubOr = "OR".equalsIgnoreCase(subLogic);

                if (isGroupOr) {
                    wrapper.or(subWrapper ->
                        processGroup(subWrapper, entityClass, subGroup, entityFields, isSubOr)
                    );
                } else {
                    wrapper.and(subWrapper ->
                        processGroup(subWrapper, entityClass, subGroup, entityFields, isSubOr)
                    );
                }
            }
        }
    }

    private static <T> void addCondition(
        LambdaQueryWrapper<T> wrapper,
        Class<T> entityClass,
        QueryParameter.Condition condition,
        Set<String> entityFields) {

        if (condition == null ||
            StringUtils.isBlank(condition.getField()) ||
            condition.getValue() == null) {
            return;
        }

        String fieldName = condition.getField();
        String operator = StringUtils.defaultIfBlank(condition.getOperator(), "eq");

        // 1. 处理实体字段查询
        if (entityFields.contains(fieldName)) {
            handleEntityFieldQuery(wrapper, entityClass, operator, fieldName, condition.getValue());
        }
        // 2. 处理JSON属性查询
        else if (fieldName.contains(".")) {
            String[] parts = fieldName.split("\\.", 2);

            if (parts.length == 2 &&
                !StringUtils.isBlank(parts[0]) &&
                !StringUtils.isBlank(parts[1])) {

                String snakeColumn = camelToSnake(parts[0]);
                String propKey = parts[1];

                // 转换蛇形命名到驼峰式
                String camelJsonField = snakeToCamel(snakeColumn);

                if (entityFields.contains(camelJsonField)) {
                    handleJsonPropertyQuery(wrapper, snakeColumn, operator, propKey, condition.getValue());
                }
            }
        }
    }

    /**
     * 单条件处理(保持兼容)
     */
    public static <T> LambdaQueryWrapper<T> buildMixedQueryWrapper(Class<T> entityClass, Map<String, Object> params, LambdaQueryWrapper<T> lqw) {
        // 获取参数Map
        if (params == null || params.isEmpty()) {
            return lqw;
        }
        // 获取实体类的所有字段名(小写驼峰)
        Set<String> entityFields = Arrays.stream(entityClass.getDeclaredFields())
            .map(Field::getName)
            .map(StringUtils::uncapitalize)
            .collect(Collectors.toSet());
        // 处理查询参数
        params.forEach((paramKey, paramValue) -> {
            if (paramValue == null || StringUtils.isBlank(paramValue.toString())) {
                return;
            }
            String operator = "eq";  // 默认操作符
            String fieldName = paramKey;
            // 改进后的正则匹配(允许小写字母开头)
            Matcher matcher = PARAM_PATTERN.matcher(paramKey);
            if (matcher.matches()) {
                operator = matcher.group(1).toLowerCase(); // 统一小写
                fieldName = StringUtils.uncapitalize(matcher.group(2));
            }
            // 判断是实体字段还是JSON属性
            if (entityFields.contains(fieldName)) {
                handleEntityFieldQuery(lqw, entityClass, operator, fieldName, paramValue);
            }
            // 处理JSON属性查询(带点号分隔符)
            else if (fieldName.contains(".")) {
                String[] parts = fieldName.split("\\.", 2);
                // 确保分割有效且非空
                if (parts.length == 2 && !parts[0].isEmpty() && !parts[1].isEmpty()) {
                    String jsonFieldName = camelToSnake(parts[0]);
                    String propKey = parts[1];
                    // 转换蛇形命名到驼峰式(根据实际需要)
                    String camelJsonField = snakeToCamel(jsonFieldName);
                    // 验证JSON字段名是否有效
                    if (entityFields.contains(camelJsonField)) {
                        handleJsonPropertyQuery(lqw, camelJsonField, operator, propKey, paramValue);
                    } else {
                        log.warn("Invalid JSON field: {}", jsonFieldName);
                    }
                }
            }
        });
        return lqw;
    }

    /**
     * 处理实体字段查询
     */
    private static <T> void handleEntityFieldQuery(LambdaQueryWrapper<T> lqw, Class<T> entityClass, String operator, String fieldName, Object value) {
        if (StringUtils.isBlank(fieldName) || value == null) {
            return;
        }
        // 获取字段的数据库列名(假设使用 MyBatis-Plus 的 @TableField 注解)
        String columnName = getColumnName(entityClass, fieldName);
        if (columnName == null) {
            return;
        }
        OperatorProcessor handler = OPERATOR_HANDLERS.get(operator.toLowerCase());
        if (handler != null) {
            try {
                handler.apply(lqw, columnName, value);
            } catch (Exception e) {
                log.error("Apply operator failed: {}{}{}", columnName, operator, value, e);
            }
        }
    }

    /**
     * 处理JSON属性查询
     */
    private static <T> void handleJsonPropertyQuery(LambdaQueryWrapper<T> lqw, String snakeColumn, String operator, String propKey, Object value) {
        if (StringUtils.isBlank(propKey) || value == null) {
            return;
        }
        // 构建JSON提取表达式(兼容不同数据库)
        String jsonExtract = buildJsonExtractExpression(snakeColumn, propKey, operator);
        // 获取对应的操作处理器
        OperatorProcessor handler = OPERATOR_HANDLERS.get(operator.toLowerCase());
        if (handler != null) {
            try {
                // 统一处理:使用JSON提取表达式作为列名
                handler.apply(lqw, jsonExtract, value);
            } catch (Exception e) {
                log.error("Apply JSON operator failed: {}{}{}", propKey, operator, value, e);
            }
        }
    }

    /**
     * 构建适合不同数据库的JSON提取表达式
     */
    private static String buildJsonExtractExpression(String jsonFieldName, String propKey, String operator) {
        // 基础JSON提取(MySQL风格)
        String jsonPath = "$." + propKey;
        String jsonExtract = String.format("JSON_UNQUOTE(JSON_EXTRACT(%s, '%s'))", jsonFieldName, jsonPath);
        // 根据操作符调整表达式
        return switch (operator.toLowerCase()) {
            case "like", "notlike", "startswith", "endswith" ->
                // 这些操作需要确保比较的是字符串
                jsonExtract;
            default ->
                // 其他操作直接使用JSON提取值(可能包含类型转换)
                String.format("JSON_EXTRACT(%s, '%s')", jsonFieldName, jsonPath);
        };
    }

    private static <T> String getColumnName(Class<T> entityClass, String fieldName) {
        try {
            Field field = entityClass.getDeclaredField(fieldName);
            TableField tableField = field.getAnnotation(TableField.class);
            // 如果字段有 @TableField 注解,并且指定了列名,则使用它
            if (tableField != null && StringUtils.isNotBlank(tableField.value())) {
                return tableField.value();
            }
            // 否则默认使用字段名(驼峰转下划线)
            return camelToSnake(fieldName);
        } catch (NoSuchFieldException e) {
            log.error("Field not found: {}", fieldName, e);
        }
        return null;
    }

    /**
     * 实现的蛇形命名转驼峰命名
     */
    private static String snakeToCamel(String str) {
        if (StringUtils.isBlank(str)) {
            return str;
        }
        String[] parts = str.split("_");
        StringBuilder result = new StringBuilder(parts[0].toLowerCase());
        for (int i = 1; i < parts.length; i++) {
            result.append(StringUtils.capitalize(parts[i].toLowerCase()));
        }
        return result.toString();
    }

    /**
     * 驼峰转蛇形工具方法
     */
    private static String camelToSnake(String str) {
        return str.replaceAll("([a-z])([A-Z])", "$1_$2").toLowerCase();
    }

}

如何使用呢

后端service

        // propertyData是json字段,knownPropKeys是json对应的key集合, bo.getParams()是map参数集合,bo.getQueryParameter()是更复杂的参数
        LambdaQueryWrapper<Product> lqw = Wrappers.lambdaQuery();
        // 1 使用*Wrappers.lambdaQuery()*构建实体相关的*LambdaQueryWrapper*实列,以实现基本查询
        lqw.eq(StringUtils.isNotBlank(bo.getProductPropertyTemplate()), Product::getProductPropertyTemplate, bo.getProductPropertyTemplate());
        // 2 使用*LambdaQueryWrapper*实列构建*params*的较复杂的支持多字段和json字段多属性的查询语句
        lqw = MixedQueryWrapper.buildMixedQueryWrapper(Product.class, bo.getParams(), lqw);
        // 3 继续使用*LambdaQueryWrapper*实列构建复杂的多分组多条件且支持多字段和json字段多属性的查询语句
        lqw = MixedQueryWrapper.buildMixedQueryWrapper(Product.class, bo.getQueryParameter(), lqw);

前端参数示例

  1. 请求示例:/product/list?pageNum=1&pageSize=10&queryParameter%5Blogic%5D=AND&queryParameter%5Bconditions%5D=*****
  2. 参数如下:
{
  "productPropertyTemplate": "product_property_template_001",
  "queryParam": {
    "logic": "AND",
    "conditions": [
      {
        "field": "status",
        "operator": "eq",
        "value": "0"
      }
    ],
    "groups": [
      {
        "logic": "OR",
        "conditions": [
          {
            "field": "nameSpec",
            "operator": "like",
            "value": "test"
          },
          {
            "field": "approveStatus",
            "operator": "eq",
            "value": "123"
          }
        ]
      }
    ]
  }
}
queryParameter.logic: AND
queryParameter.conditions[0].field: productPropertyTemplate
queryParameter.conditions[0].operator: eq
queryParameter.conditions[0].value: 432
queryParameter.conditions[1].field: originalProductId
queryParameter.conditions[1].operator: eq
queryParameter.conditions[1].value: 6456
queryParameter.groups[0].logic: OR
queryParameter.groups[0].conditions[0].field: nameSpec
queryParameter.groups[0].conditions[0].operator: like
queryParameter.groups[0].conditions[0].value: cs
queryParameter.groups[0].conditions[1].field: originalProductId
queryParameter.groups[0].conditions[1].operator: eq
queryParameter.groups[0].conditions[1].value: 44
queryParameter.groups[1].logic: AND
queryParameter.groups[1].conditions[0].field: nameSpec
queryParameter.groups[1].conditions[0].operator: eq
queryParameter.groups[1].conditions[0].value: dd
queryParameter.groups[1].conditions[1].field: status
queryParameter.groups[1].conditions[1].operator: startsWith
queryParameter.groups[1].conditions[1].value: 4
//queryParameter.groups[1].groups[0]....
  1. API请求如果是GET接口可能需要改造一下查询参数,以免出现参数为queryParameter.conditions: [jobect, object]导致请求失败,post接口无影响
export const listProduct = (query?: ProductQuery): AxiosPromise<ProductVO[]> => {
  // 处理 queryParameter 参数
  const params = new URLSearchParams();

  // 添加基础参数
  params.append('pageNum', query?.pageNum?.toString() || '1');
  params.append('pageSize', query?.pageSize?.toString() || '10');

  // 添加 queryParameter 参数
  if (query?.queryParameter) {
    const qp = query.queryParameter;
    params.append('queryParameter.logic', qp.logic || 'AND');

    // 处理 conditions
    qp.conditions?.forEach((condition, index) => {
      params.append(`queryParameter.conditions[${index}].field`, condition.field);
      params.append(`queryParameter.conditions[${index}].operator`, condition.operator);
      params.append(`queryParameter.conditions[${index}].value`, condition.value);
    });

    // 处理 groups
    qp.groups?.forEach((group, gIndex) => {
      params.append(`queryParameter.groups[${gIndex}].logic`, group.logic);
      group.conditions?.forEach((condition, cIndex) => {
        params.append(`queryParameter.groups[${gIndex}].conditions[${cIndex}].field`, condition.field);
        params.append(`queryParameter.groups[${gIndex}].conditions[${cIndex}].operator`, condition.operator);
        params.append(`queryParameter.groups[${gIndex}].conditions[${cIndex}].value`, condition.value);
      });
    });
  }

  return request({
    url: '/product/product/list',
    method: 'get',
    params: params
  });
};

生成SQL示例

SELECT COUNT(*) AS total 
FROM product 
WHERE (
    product_property_template = '432' 
    AND original_product_id = '6456' 
    AND (name_spec LIKE '%cs%' OR original_product_id = '44') 
    AND (name_spec = 'dd' AND status LIKE '4%')
)
posted @ 2025-07-20 01:58  明月心~  阅读(86)  评论(0)    收藏  举报