diff --git a/README.md b/README.md
index a7b6e2fa..63182fb1 100644
--- a/README.md
+++ b/README.md
@@ -236,10 +236,10 @@ ContiNew Starter 的分支目前分为下个大版本的开发分支和上个大
### 特别鸣谢
-- 感谢 JetBrains 提供的 非商业开源软件开发授权
+- 感谢 JetBrains 提供的 非商业开源软件开发授权
- 感谢 MyBatis Plus 、Sa-Token 、JetCache 、Crane4j 、Knife4j 、Hutool 等开源组件作者为国内开源世界作出的贡献
- 感谢 ELADMIN 、RuoYi-Vue-Plus 、Dante-Engine ,致敬各位作者为开源脚手架领域作出的贡献
- - e.g. 扩展于 ELADMIN 项目开源的 QueryHelper 组件
+ - e.g. 起源于 ELADMIN 项目开源的 QueryHelper 组件
- e.g. 扩展于 RuoYi-Vue-Plus 项目封装的 SaToken 相关认证鉴权配置
- e.g. 扩展于 Dante-Engine 项目封装的 Redisson 相关配置
- 感谢项目使用或未使用到的每一款开源组件,致敬各位开源先驱 :fire:
diff --git a/continew-starter-data/continew-starter-data-mybatis-plus/src/main/java/top/charles7c/continew/starter/data/mybatis/plus/query/Query.java b/continew-starter-data/continew-starter-data-mybatis-plus/src/main/java/top/charles7c/continew/starter/data/mybatis/plus/query/Query.java
index 7b6ec2f2..96315569 100644
--- a/continew-starter-data/continew-starter-data-mybatis-plus/src/main/java/top/charles7c/continew/starter/data/mybatis/plus/query/Query.java
+++ b/continew-starter-data/continew-starter-data-mybatis-plus/src/main/java/top/charles7c/continew/starter/data/mybatis/plus/query/Query.java
@@ -23,7 +23,6 @@ import java.lang.annotation.*;
*
* @author Charles7c
* @author Jasmine
- * @author Zheng Jie(ELADMIN )
* @since 1.0.0
*/
@Target(ElementType.FIELD)
@@ -32,7 +31,7 @@ import java.lang.annotation.*;
public @interface Query {
/**
- * 列名
+ * 列名(注意:列名是数据库字段名,而不是实体类字段名。如果命名是数据库关键字的,请使用转义符包裹)
*
*
* columns 为空时,默认取值字段名(自动转换为下划线命名);
diff --git a/continew-starter-data/continew-starter-data-mybatis-plus/src/main/java/top/charles7c/continew/starter/data/mybatis/plus/query/QueryHelper.java b/continew-starter-data/continew-starter-data-mybatis-plus/src/main/java/top/charles7c/continew/starter/data/mybatis/plus/query/QueryHelper.java
deleted file mode 100644
index f87f667a..00000000
--- a/continew-starter-data/continew-starter-data-mybatis-plus/src/main/java/top/charles7c/continew/starter/data/mybatis/plus/query/QueryHelper.java
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
- *
- * 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
- *
- * http://www.gnu.org/licenses/lgpl.html
- *
- * 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(ELADMIN )
- * @since 1.0.0
- */
-@Slf4j
-@NoArgsConstructor(access = AccessLevel.PRIVATE)
-public class QueryHelper {
-
- /**
- * 根据查询条件构建查询条件封装对象
- *
- * @param query 查询条件
- * @param 查询条件数据类型
- * @param 查询数据类型
- * @return 查询条件封装对象
- */
- public static QueryWrapper build(Q query) {
- QueryWrapper queryWrapper = new QueryWrapper<>();
- // 没有查询条件,直接返回
- if (null == query) {
- return queryWrapper;
- }
- // 解析并拼接查询条件
- List fieldList = ReflectUtils.getNonStaticFields(query.getClass());
- fieldList.forEach(field -> buildWrapper(query, field, queryWrapper));
- return queryWrapper;
- }
-
- /**
- * 构建查询条件封装对象
- *
- * @param query 查询条件
- * @param field 字段
- * @param queryWrapper 查询条件封装对象
- * @param 查询条件数据类型
- * @param 查询数据类型
- */
- public static void buildWrapper(Q query, Field field, QueryWrapper 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 between = new ArrayList<>((List)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)fieldValue);
- }
- case NOT_IN -> {
- ValidationUtils.throwIfEmpty(fieldValue, "[{}] 不能为空", fieldName);
- queryWrapper.or().notIn(column, (List)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 查询数据类型
- */
- private static void parse(QueryType queryType,
- String columnName,
- Object fieldValue,
- QueryWrapper 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 between = new ArrayList<>((List)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)fieldValue);
- }
- case NOT_IN -> {
- ValidationUtils.throwIfEmpty(fieldValue, "[{}] 不能为空", columnName);
- queryWrapper.notIn(columnName, (List)fieldValue);
- }
- case IS_NULL -> queryWrapper.isNull(columnName);
- case IS_NOT_NULL -> queryWrapper.isNotNull(columnName);
- default -> throw new IllegalArgumentException(String.format("暂不支持 [%s] 查询类型", queryType));
- }
- }
-}
diff --git a/continew-starter-data/continew-starter-data-mybatis-plus/src/main/java/top/charles7c/continew/starter/data/mybatis/plus/query/QueryWrapperHelper.java b/continew-starter-data/continew-starter-data-mybatis-plus/src/main/java/top/charles7c/continew/starter/data/mybatis/plus/query/QueryWrapperHelper.java
new file mode 100644
index 00000000..9ec6792a
--- /dev/null
+++ b/continew-starter-data/continew-starter-data-mybatis-plus/src/main/java/top/charles7c/continew/starter/data/mybatis/plus/query/QueryWrapperHelper.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * 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
+ *
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * 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 查询条件数据类型
+ * @param 查询数据类型
+ * @return QueryWrapper
+ */
+ public static QueryWrapper build(Q query) {
+ QueryWrapper queryWrapper = new QueryWrapper<>();
+ // 没有查询条件,直接返回
+ if (null == query) {
+ return queryWrapper;
+ }
+ // 获取查询条件中所有的字段
+ List fieldList = ReflectUtils.getNonStaticFields(query.getClass());
+ return build(query, fieldList, queryWrapper);
+ }
+
+ /**
+ * 构建 QueryWrapper
+ *
+ * @param query 查询条件
+ * @param fields 查询条件字段列表
+ * @param queryWrapper QueryWrapper
+ * @param 查询条件数据类型
+ * @param 查询数据类型
+ * @return QueryWrapper
+ */
+ public static QueryWrapper build(Q query, List fields, QueryWrapper queryWrapper) {
+ // 没有查询条件,直接返回
+ if (null == query) {
+ return queryWrapper;
+ }
+ // 解析并拼接查询条件
+ for (Field field : fields) {
+ List>> consumers = buildWrapperConsumer(query, field);
+ queryWrapper.and(CollUtil.isNotEmpty(consumers), q -> consumers.forEach(q::or));
+ }
+ return queryWrapper;
+ }
+
+ /**
+ * 构建 QueryWrapper Consumer
+ *
+ * @param query 查询条件
+ * @param field 查询条件字段
+ * @param 查询条件数据类型
+ * @param 查询数据类型
+ * @return QueryWrapper Consumer
+ */
+ public static List>> buildWrapperConsumer(Q query, Field field) {
+ List>> 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 查询数据类型
+ */
+ private static void parse(QueryType queryType,
+ String columnName,
+ Object fieldValue,
+ List>> 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 between = new ArrayList<>((List)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)fieldValue));
+ }
+ case NOT_IN -> {
+ ValidationUtils.throwIfEmpty(fieldValue, "[{}] 不能为空", columnName);
+ consumers.add(q -> q.notIn(columnName, (List)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));
+ }
+ }
+}
diff --git a/continew-starter-extension/continew-starter-extension-crud/src/main/java/top/charles7c/continew/starter/extension/crud/base/BaseServiceImpl.java b/continew-starter-extension/continew-starter-extension-crud/src/main/java/top/charles7c/continew/starter/extension/crud/base/BaseServiceImpl.java
index f9b43be6..626a21de 100644
--- a/continew-starter-extension/continew-starter-extension-crud/src/main/java/top/charles7c/continew/starter/extension/crud/base/BaseServiceImpl.java
+++ b/continew-starter-extension/continew-starter-extension-crud/src/main/java/top/charles7c/continew/starter/extension/crud/base/BaseServiceImpl.java
@@ -40,7 +40,7 @@ import top.charles7c.continew.starter.core.util.ReflectUtils;
import top.charles7c.continew.starter.core.util.validate.CheckUtils;
import top.charles7c.continew.starter.core.util.validate.ValidationUtils;
import top.charles7c.continew.starter.data.mybatis.plus.base.BaseMapper;
-import top.charles7c.continew.starter.data.mybatis.plus.query.QueryHelper;
+import top.charles7c.continew.starter.data.mybatis.plus.query.QueryWrapperHelper;
import top.charles7c.continew.starter.extension.crud.annotation.TreeField;
import top.charles7c.continew.starter.extension.crud.model.query.PageQuery;
import top.charles7c.continew.starter.extension.crud.model.query.SortQuery;
@@ -81,7 +81,7 @@ public abstract class BaseServiceImpl, T extends BaseDO,
@Override
public PageResp page(Q query, PageQuery pageQuery) {
- QueryWrapper queryWrapper = handleQueryWrapper(query);
+ QueryWrapper queryWrapper = this.handleQueryWrapper(query);
IPage page = baseMapper.selectPage(pageQuery.toPage(), queryWrapper);
PageResp pageResp = PageResp.build(page, listClass);
pageResp.getList().forEach(this::fill);
@@ -134,7 +134,7 @@ public abstract class BaseServiceImpl, T extends BaseDO,
* @return 列表信息
*/
protected List list(Q query, SortQuery sortQuery, Class targetClass) {
- QueryWrapper queryWrapper = handleQueryWrapper(query);
+ QueryWrapper queryWrapper = this.handleQueryWrapper(query);
// 设置排序
this.sort(queryWrapper, sortQuery);
List entityList = baseMapper.selectList(queryWrapper);
@@ -248,19 +248,14 @@ public abstract class BaseServiceImpl, T extends BaseDO,
}
/**
- * 封装查询条件
+ * 处理查询条件
*
- * @return 查询条件封装对象
+ * @return QueryWrapper
*/
protected QueryWrapper handleQueryWrapper(Q query) {
QueryWrapper queryWrapper = new QueryWrapper<>();
- // 没有查询条件,直接返回
- if (null == query) {
- return queryWrapper;
- }
// 解析并拼接查询条件
- queryFields.forEach(field -> QueryHelper.buildWrapper(query, field, queryWrapper));
- return queryWrapper;
+ return QueryWrapperHelper.build(query, queryFields, queryWrapper);
}
/**