mirror of
				https://github.com/continew-org/continew-starter.git
				synced 2025-10-31 22:57:19 +08:00 
			
		
		
		
	refactor: 重构 QueryHelper => QueryWrapperHelper,正式支持多列查询
This commit is contained in:
		| @@ -23,7 +23,6 @@ import java.lang.annotation.*; | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @author Jasmine | ||||
|  * @author Zheng Jie(<a href="https://gitee.com/elunez/eladmin">ELADMIN</a>) | ||||
|  * @since 1.0.0 | ||||
|  */ | ||||
| @Target(ElementType.FIELD) | ||||
| @@ -32,7 +31,7 @@ import java.lang.annotation.*; | ||||
| public @interface Query { | ||||
|  | ||||
|     /** | ||||
|      * 列名 | ||||
|      * 列名(注意:列名是数据库字段名,而不是实体类字段名。如果命名是数据库关键字的,请使用转义符包裹) | ||||
|      * | ||||
|      * <p> | ||||
|      * columns 为空时,默认取值字段名(自动转换为下划线命名);<br> | ||||
|   | ||||
| @@ -1,186 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * <p> | ||||
|  * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * <p> | ||||
|  * http://www.gnu.org/licenses/lgpl.html | ||||
|  * <p> | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| package top.charles7c.continew.starter.data.mybatis.plus.query; | ||||
|  | ||||
| import cn.hutool.core.util.ArrayUtil; | ||||
| import cn.hutool.core.util.ObjectUtil; | ||||
| import cn.hutool.core.util.StrUtil; | ||||
| import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; | ||||
| import lombok.AccessLevel; | ||||
| import lombok.NoArgsConstructor; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
| import top.charles7c.continew.starter.core.exception.BadRequestException; | ||||
| import top.charles7c.continew.starter.core.util.ReflectUtils; | ||||
| import top.charles7c.continew.starter.core.util.validate.ValidationUtils; | ||||
|  | ||||
| import java.lang.reflect.Field; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| /** | ||||
|  * 查询助手 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @author Jasmine | ||||
|  * @author Zheng Jie(<a href="https://gitee.com/elunez/eladmin">ELADMIN</a>) | ||||
|  * @since 1.0.0 | ||||
|  */ | ||||
| @Slf4j | ||||
| @NoArgsConstructor(access = AccessLevel.PRIVATE) | ||||
| public class QueryHelper { | ||||
|  | ||||
|     /** | ||||
|      * 根据查询条件构建查询条件封装对象 | ||||
|      * | ||||
|      * @param query 查询条件 | ||||
|      * @param <Q>   查询条件数据类型 | ||||
|      * @param <R>   查询数据类型 | ||||
|      * @return 查询条件封装对象 | ||||
|      */ | ||||
|     public static <Q, R> QueryWrapper<R> build(Q query) { | ||||
|         QueryWrapper<R> queryWrapper = new QueryWrapper<>(); | ||||
|         // 没有查询条件,直接返回 | ||||
|         if (null == query) { | ||||
|             return queryWrapper; | ||||
|         } | ||||
|         // 解析并拼接查询条件 | ||||
|         List<Field> fieldList = ReflectUtils.getNonStaticFields(query.getClass()); | ||||
|         fieldList.forEach(field -> buildWrapper(query, field, queryWrapper)); | ||||
|         return queryWrapper; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 构建查询条件封装对象 | ||||
|      * | ||||
|      * @param query        查询条件 | ||||
|      * @param field        字段 | ||||
|      * @param queryWrapper 查询条件封装对象 | ||||
|      * @param <Q>          查询条件数据类型 | ||||
|      * @param <R>          查询数据类型 | ||||
|      */ | ||||
|     public static <Q, R> void buildWrapper(Q query, Field field, QueryWrapper<R> queryWrapper) { | ||||
|         boolean accessible = field.canAccess(query); | ||||
|         try { | ||||
|             field.setAccessible(true); | ||||
|             // 如果字段值为空,直接返回 | ||||
|             Object fieldValue = field.get(query); | ||||
|             if (ObjectUtil.isEmpty(fieldValue)) { | ||||
|                 return; | ||||
|             } | ||||
|             // 建议:数据库规范中列建议采用下划线连接法命名,程序规范中变量建议采用驼峰法命名 | ||||
|             String fieldName = field.getName(); | ||||
|             String columnName = StrUtil.toUnderlineCase(fieldName); | ||||
|             // 没有 @Query 注解,默认等值查询 | ||||
|             Query queryAnnotation = field.getAnnotation(Query.class); | ||||
|             if (null == queryAnnotation) { | ||||
|                 queryWrapper.eq(columnName, fieldValue); | ||||
|                 return; | ||||
|             } | ||||
|             // 解析单列查询 | ||||
|             String[] columns = queryAnnotation.columns(); | ||||
|             final int columnLength = ArrayUtil.length(columns); | ||||
|             if (columnLength == 0 || columnLength == 1) { | ||||
|                 columnName = columnLength == 1 ? columns[0] : columnName; | ||||
|                 parse(queryAnnotation.type(), columnName, fieldValue, queryWrapper); | ||||
|                 return; | ||||
|             } | ||||
|             // 解析多列查询 | ||||
|             QueryType queryType = queryAnnotation.type(); | ||||
|             queryWrapper.nested(wrapper -> { | ||||
|                 for (String column : columns) { | ||||
|                     switch (queryType) { | ||||
|                         case EQ -> queryWrapper.or().eq(column, fieldValue); | ||||
|                         case NE -> queryWrapper.or().ne(column, fieldValue); | ||||
|                         case GT -> queryWrapper.or().gt(column, fieldValue); | ||||
|                         case GE -> queryWrapper.or().ge(column, fieldValue); | ||||
|                         case LT -> queryWrapper.or().lt(column, fieldValue); | ||||
|                         case LE -> queryWrapper.or().le(column, fieldValue); | ||||
|                         case BETWEEN -> { | ||||
|                             List<Object> between = new ArrayList<>((List<Object>)fieldValue); | ||||
|                             ValidationUtils.throwIf(between.size() != 2, "[{}] 必须是一个范围", fieldName); | ||||
|                             queryWrapper.or().between(column, between.get(0), between.get(1)); | ||||
|                         } | ||||
|                         case LIKE -> queryWrapper.or().like(column, fieldValue); | ||||
|                         case LIKE_LEFT -> queryWrapper.or().likeLeft(column, fieldValue); | ||||
|                         case LIKE_RIGHT -> queryWrapper.or().likeRight(column, fieldValue); | ||||
|                         case IN -> { | ||||
|                             ValidationUtils.throwIfEmpty(fieldValue, "[{}] 不能为空", fieldName); | ||||
|                             queryWrapper.or().in(column, (List<Object>)fieldValue); | ||||
|                         } | ||||
|                         case NOT_IN -> { | ||||
|                             ValidationUtils.throwIfEmpty(fieldValue, "[{}] 不能为空", fieldName); | ||||
|                             queryWrapper.or().notIn(column, (List<Object>)fieldValue); | ||||
|                         } | ||||
|                         case IS_NULL -> queryWrapper.or().isNull(column); | ||||
|                         case IS_NOT_NULL -> queryWrapper.or().isNotNull(column); | ||||
|                         default -> throw new IllegalArgumentException(String.format("暂不支持 [%s] 查询类型", queryType)); | ||||
|                     } | ||||
|                 } | ||||
|             }); | ||||
|         } catch (BadRequestException e) { | ||||
|             log.error("Build query occurred an validation error: {}. Query: {}, Field: {}.", e | ||||
|                 .getMessage(), query, field, e); | ||||
|             throw e; | ||||
|         } catch (Exception e) { | ||||
|             log.error("Build query occurred an error: {}. Query: {}, Field: {}.", e.getMessage(), query, field, e); | ||||
|         } finally { | ||||
|             field.setAccessible(accessible); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 解析查询条件 | ||||
|      * | ||||
|      * @param queryType    查询类型 | ||||
|      * @param columnName   驼峰字段名 | ||||
|      * @param fieldValue   字段值 | ||||
|      * @param queryWrapper 查询条件封装对象 | ||||
|      * @param <R>          查询数据类型 | ||||
|      */ | ||||
|     private static <R> void parse(QueryType queryType, | ||||
|                                   String columnName, | ||||
|                                   Object fieldValue, | ||||
|                                   QueryWrapper<R> queryWrapper) { | ||||
|         switch (queryType) { | ||||
|             case EQ -> queryWrapper.eq(columnName, fieldValue); | ||||
|             case NE -> queryWrapper.ne(columnName, fieldValue); | ||||
|             case GT -> queryWrapper.gt(columnName, fieldValue); | ||||
|             case GE -> queryWrapper.ge(columnName, fieldValue); | ||||
|             case LT -> queryWrapper.lt(columnName, fieldValue); | ||||
|             case LE -> queryWrapper.le(columnName, fieldValue); | ||||
|             case BETWEEN -> { | ||||
|                 List<Object> between = new ArrayList<>((List<Object>)fieldValue); | ||||
|                 ValidationUtils.throwIf(between.size() != 2, "[{}] 必须是一个范围", columnName); | ||||
|                 queryWrapper.between(columnName, between.get(0), between.get(1)); | ||||
|             } | ||||
|             case LIKE -> queryWrapper.like(columnName, fieldValue); | ||||
|             case LIKE_LEFT -> queryWrapper.likeLeft(columnName, fieldValue); | ||||
|             case LIKE_RIGHT -> queryWrapper.likeRight(columnName, fieldValue); | ||||
|             case IN -> { | ||||
|                 ValidationUtils.throwIfEmpty(fieldValue, "[{}] 不能为空", columnName); | ||||
|                 queryWrapper.in(columnName, (List<Object>)fieldValue); | ||||
|             } | ||||
|             case NOT_IN -> { | ||||
|                 ValidationUtils.throwIfEmpty(fieldValue, "[{}] 不能为空", columnName); | ||||
|                 queryWrapper.notIn(columnName, (List<Object>)fieldValue); | ||||
|             } | ||||
|             case IS_NULL -> queryWrapper.isNull(columnName); | ||||
|             case IS_NOT_NULL -> queryWrapper.isNotNull(columnName); | ||||
|             default -> throw new IllegalArgumentException(String.format("暂不支持 [%s] 查询类型", queryType)); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,183 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * <p> | ||||
|  * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * <p> | ||||
|  * http://www.gnu.org/licenses/lgpl.html | ||||
|  * <p> | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| package top.charles7c.continew.starter.data.mybatis.plus.query; | ||||
|  | ||||
| import cn.hutool.core.collection.CollUtil; | ||||
| import cn.hutool.core.util.ArrayUtil; | ||||
| import cn.hutool.core.util.ObjectUtil; | ||||
| import cn.hutool.core.util.StrUtil; | ||||
| import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; | ||||
| import lombok.AccessLevel; | ||||
| import lombok.NoArgsConstructor; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
| import top.charles7c.continew.starter.core.exception.BadRequestException; | ||||
| import top.charles7c.continew.starter.core.util.ReflectUtils; | ||||
| import top.charles7c.continew.starter.core.util.validate.ValidationUtils; | ||||
|  | ||||
| import java.lang.reflect.Field; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| import java.util.Objects; | ||||
| import java.util.function.Consumer; | ||||
|  | ||||
| /** | ||||
|  * QueryWrapper 助手 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @author Jasmine | ||||
|  * @since 1.0.0 | ||||
|  */ | ||||
| @Slf4j | ||||
| @NoArgsConstructor(access = AccessLevel.PRIVATE) | ||||
| public class QueryWrapperHelper { | ||||
|  | ||||
|     /** | ||||
|      * 构建 QueryWrapper | ||||
|      * | ||||
|      * @param query 查询条件 | ||||
|      * @param <Q>   查询条件数据类型 | ||||
|      * @param <R>   查询数据类型 | ||||
|      * @return QueryWrapper | ||||
|      */ | ||||
|     public static <Q, R> QueryWrapper<R> build(Q query) { | ||||
|         QueryWrapper<R> queryWrapper = new QueryWrapper<>(); | ||||
|         // 没有查询条件,直接返回 | ||||
|         if (null == query) { | ||||
|             return queryWrapper; | ||||
|         } | ||||
|         // 获取查询条件中所有的字段 | ||||
|         List<Field> fieldList = ReflectUtils.getNonStaticFields(query.getClass()); | ||||
|         return build(query, fieldList, queryWrapper); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 构建 QueryWrapper | ||||
|      * | ||||
|      * @param query        查询条件 | ||||
|      * @param fields       查询条件字段列表 | ||||
|      * @param queryWrapper QueryWrapper | ||||
|      * @param <Q>          查询条件数据类型 | ||||
|      * @param <R>          查询数据类型 | ||||
|      * @return QueryWrapper | ||||
|      */ | ||||
|     public static <Q, R> QueryWrapper<R> build(Q query, List<Field> fields, QueryWrapper<R> queryWrapper) { | ||||
|         // 没有查询条件,直接返回 | ||||
|         if (null == query) { | ||||
|             return queryWrapper; | ||||
|         } | ||||
|         // 解析并拼接查询条件 | ||||
|         for (Field field : fields) { | ||||
|             List<Consumer<QueryWrapper<R>>> consumers = buildWrapperConsumer(query, field); | ||||
|             queryWrapper.and(CollUtil.isNotEmpty(consumers), q -> consumers.forEach(q::or)); | ||||
|         } | ||||
|         return queryWrapper; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 构建 QueryWrapper Consumer | ||||
|      * | ||||
|      * @param query 查询条件 | ||||
|      * @param field 查询条件字段 | ||||
|      * @param <Q>   查询条件数据类型 | ||||
|      * @param <R>   查询数据类型 | ||||
|      * @return QueryWrapper Consumer | ||||
|      */ | ||||
|     public static <Q, R> List<Consumer<QueryWrapper<R>>> buildWrapperConsumer(Q query, Field field) { | ||||
|         List<Consumer<QueryWrapper<R>>> consumers = new ArrayList<>(); | ||||
|         boolean accessible = field.canAccess(query); | ||||
|         try { | ||||
|             field.setAccessible(true); | ||||
|             // 如果字段值为空,直接返回 | ||||
|             Object fieldValue = field.get(query); | ||||
|             if (ObjectUtil.isEmpty(fieldValue)) { | ||||
|                 return consumers; | ||||
|             } | ||||
|             // 建议:数据库表列建议采用下划线连接法命名,程序变量建议采用驼峰法命名 | ||||
|             String fieldName = field.getName(); | ||||
|             // 没有 @Query 注解,默认等值查询 | ||||
|             Query queryAnnotation = field.getAnnotation(Query.class); | ||||
|             if (Objects.isNull(queryAnnotation)) { | ||||
|                 consumers.add(q -> q.eq(StrUtil.toUnderlineCase(fieldName), fieldValue)); | ||||
|                 return consumers; | ||||
|             } | ||||
|             // 解析单列查询 | ||||
|             QueryType queryType = queryAnnotation.type(); | ||||
|             String[] columns = queryAnnotation.columns(); | ||||
|             final int columnLength = ArrayUtil.length(columns); | ||||
|             if (columnLength <= 1) { | ||||
|                 String columnName = columnLength == 1 ? columns[0] : StrUtil.toUnderlineCase(fieldName); | ||||
|                 parse(queryType, columnName, fieldValue, consumers); | ||||
|                 return consumers; | ||||
|             } | ||||
|             // 解析多列查询 | ||||
|             for (String column : columns) { | ||||
|                 parse(queryType, column, fieldValue, consumers); | ||||
|             } | ||||
|         } catch (BadRequestException e) { | ||||
|             log.error("Build query wrapper occurred an validation error: {}. Query: {}, Field: {}.", e | ||||
|                 .getMessage(), query, field, e); | ||||
|             throw e; | ||||
|         } catch (Exception e) { | ||||
|             log.error("Build query wrapper occurred an error: {}. Query: {}, Field: {}.", e | ||||
|                 .getMessage(), query, field, e); | ||||
|         } finally { | ||||
|             field.setAccessible(accessible); | ||||
|         } | ||||
|         return consumers; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 解析查询条件 | ||||
|      * | ||||
|      * @param queryType  查询类型 | ||||
|      * @param columnName 列名 | ||||
|      * @param fieldValue 字段值 | ||||
|      * @param <R>        查询数据类型 | ||||
|      */ | ||||
|     private static <R> void parse(QueryType queryType, | ||||
|                                   String columnName, | ||||
|                                   Object fieldValue, | ||||
|                                   List<Consumer<QueryWrapper<R>>> consumers) { | ||||
|         switch (queryType) { | ||||
|             case EQ -> consumers.add(q -> q.eq(columnName, fieldValue)); | ||||
|             case NE -> consumers.add(q -> q.ne(columnName, fieldValue)); | ||||
|             case GT -> consumers.add(q -> q.gt(columnName, fieldValue)); | ||||
|             case GE -> consumers.add(q -> q.ge(columnName, fieldValue)); | ||||
|             case LT -> consumers.add(q -> q.lt(columnName, fieldValue)); | ||||
|             case LE -> consumers.add(q -> q.le(columnName, fieldValue)); | ||||
|             case BETWEEN -> { | ||||
|                 List<Object> between = new ArrayList<>((List<Object>)fieldValue); | ||||
|                 ValidationUtils.throwIf(between.size() != 2, "[{}] 必须是一个范围", columnName); | ||||
|                 consumers.add(q -> q.between(columnName, between.get(0), between.get(1))); | ||||
|             } | ||||
|             case LIKE -> consumers.add(q -> q.like(columnName, fieldValue)); | ||||
|             case LIKE_LEFT -> consumers.add(q -> q.likeLeft(columnName, fieldValue)); | ||||
|             case LIKE_RIGHT -> consumers.add(q -> q.likeRight(columnName, fieldValue)); | ||||
|             case IN -> { | ||||
|                 ValidationUtils.throwIfEmpty(fieldValue, "[{}] 不能为空", columnName); | ||||
|                 consumers.add(q -> q.in(columnName, (List<Object>)fieldValue)); | ||||
|             } | ||||
|             case NOT_IN -> { | ||||
|                 ValidationUtils.throwIfEmpty(fieldValue, "[{}] 不能为空", columnName); | ||||
|                 consumers.add(q -> q.notIn(columnName, (List<Object>)fieldValue)); | ||||
|             } | ||||
|             case IS_NULL -> consumers.add(q -> q.isNull(columnName)); | ||||
|             case IS_NOT_NULL -> consumers.add(q -> q.isNotNull(columnName)); | ||||
|             default -> throw new IllegalArgumentException(String.format("暂不支持 [%s] 查询类型", queryType)); | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user