|
|
@@ -0,0 +1,644 @@
|
|
|
+package shop.alien.util.myBaticsPlus;
|
|
|
+
|
|
|
+import com.baomidou.mybatisplus.annotation.TableField;
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
|
+import com.baomidou.mybatisplus.core.metadata.IPage;
|
|
|
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
+import com.baomidou.mybatisplus.extension.service.IService;
|
|
|
+import lombok.Data;
|
|
|
+import lombok.experimental.Accessors;
|
|
|
+
|
|
|
+import java.lang.reflect.Field;
|
|
|
+import java.lang.reflect.Modifier;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.Collection;
|
|
|
+import java.util.List;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 通用查询构建器
|
|
|
+ * <p>
|
|
|
+ * 功能:根据对象非空字段自动构建查询条件,支持分页、模糊、批量等多种查询方式
|
|
|
+ * <p>
|
|
|
+ * 使用示例:
|
|
|
+ * <pre>{@code
|
|
|
+ * // 1. 简单查询
|
|
|
+ * QueryBuilder.of(query).build().list(service);
|
|
|
+ *
|
|
|
+ * // 2. 分页查询
|
|
|
+ * QueryBuilder.of(query).page(1, 10).build().page(service);
|
|
|
+ *
|
|
|
+ * // 3. 批量查询(字段名以_List结尾,如:id_List)
|
|
|
+ * QueryBuilder.of(query).build().list(service);
|
|
|
+ *
|
|
|
+ * // 4. 范围查询(字段名以_Start/_End结尾,如:createdTime_Start, createdTime_End)
|
|
|
+ * QueryBuilder.of(query).build().list(service);
|
|
|
+ *
|
|
|
+ * // 5. 模糊查询(字段名以_Like结尾,如:name_Like)
|
|
|
+ * QueryBuilder.of(query).build().list(service);
|
|
|
+ * }</pre>
|
|
|
+ *
|
|
|
+ * @author system
|
|
|
+ * @since 2025-01-XX
|
|
|
+ */
|
|
|
+public class QueryBuilder<T> {
|
|
|
+
|
|
|
+ // ==================== 常量定义 ====================
|
|
|
+
|
|
|
+ /** 特殊查询字段后缀(统一使用下划线前缀,避免与普通字段冲突) */
|
|
|
+ private static final String SUFFIX_LIST = "_List";
|
|
|
+ private static final String SUFFIX_START = "_Start";
|
|
|
+ private static final String SUFFIX_END = "_End";
|
|
|
+ private static final String SUFFIX_LIKE = "_Like";
|
|
|
+
|
|
|
+ /** 模糊查询通配符 */
|
|
|
+ private static final String LIKE_WILDCARD = "%";
|
|
|
+
|
|
|
+ /** 模糊查询模式枚举 */
|
|
|
+ public enum LikeMode {
|
|
|
+ /** 前后模糊:%value% */
|
|
|
+ BOTH,
|
|
|
+ /** 前模糊:%value */
|
|
|
+ LEFT,
|
|
|
+ /** 后模糊:value% */
|
|
|
+ RIGHT
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 成员变量 ====================
|
|
|
+
|
|
|
+ private final T queryEntity;
|
|
|
+ private final QueryWrapper<T> wrapper;
|
|
|
+ private boolean ignoreEmptyStr = true;
|
|
|
+ private boolean stringFieldLike = false; // String类型字段是否默认使用模糊查询
|
|
|
+ private final List<String> likeFields = new ArrayList<>(); // 指定哪些字段使用模糊查询
|
|
|
+ private LikeMode likeMode = LikeMode.BOTH; // 模糊查询模式(默认前后模糊)
|
|
|
+ private Page<T> page;
|
|
|
+
|
|
|
+ // ==================== 构造函数 ====================
|
|
|
+
|
|
|
+ private QueryBuilder(T queryEntity) {
|
|
|
+ this.queryEntity = queryEntity;
|
|
|
+ this.wrapper = new QueryWrapper<>();
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 公开API ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 创建查询构建器
|
|
|
+ */
|
|
|
+ public static <T> QueryBuilder<T> of(T queryEntity) {
|
|
|
+ return new QueryBuilder<>(queryEntity);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置是否忽略空字符串(默认true)
|
|
|
+ */
|
|
|
+ public QueryBuilder<T> ignoreEmptyStr(boolean ignore) {
|
|
|
+ this.ignoreEmptyStr = ignore;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置String类型字段是否默认使用模糊查询(默认false,即等值查询)
|
|
|
+ */
|
|
|
+ public QueryBuilder<T> stringFieldLike(boolean like) {
|
|
|
+ this.stringFieldLike = like;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 指定哪些字段使用模糊查询(可指定多个字段)
|
|
|
+ * 例如:.likeFields("name", "code") 表示 name 和 code 字段使用模糊查询
|
|
|
+ */
|
|
|
+ public QueryBuilder<T> likeFields(String... fieldNames) {
|
|
|
+ if (fieldNames != null) {
|
|
|
+ for (String fieldName : fieldNames) {
|
|
|
+ if (fieldName != null && !fieldName.trim().isEmpty()) {
|
|
|
+ likeFields.add(fieldName.trim());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置模糊查询模式(默认前后模糊:%value%)
|
|
|
+ *
|
|
|
+ * @param mode 模糊查询模式
|
|
|
+ * - BOTH: 前后模糊 %value%(默认)
|
|
|
+ * - LEFT: 前模糊 %value
|
|
|
+ * - RIGHT: 后模糊 value%
|
|
|
+ *
|
|
|
+ * 使用示例:
|
|
|
+ * <pre>{@code
|
|
|
+ * QueryBuilder.of(query)
|
|
|
+ * .likeFields("name")
|
|
|
+ * .likeMode(LikeMode.LEFT) // 前模糊查询
|
|
|
+ * .build()
|
|
|
+ * }</pre>
|
|
|
+ */
|
|
|
+ public QueryBuilder<T> likeMode(LikeMode mode) {
|
|
|
+ if (mode != null) {
|
|
|
+ this.likeMode = mode;
|
|
|
+ }
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置分页参数
|
|
|
+ */
|
|
|
+ public QueryBuilder<T> page(int pageNum, int pageSize) {
|
|
|
+ this.page = new Page<>(pageNum, pageSize);
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置分页对象
|
|
|
+ */
|
|
|
+ public QueryBuilder<T> page(Page<T> page) {
|
|
|
+ this.page = page;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建查询条件
|
|
|
+ */
|
|
|
+ public QueryResult<T> build() {
|
|
|
+ buildBaseQueryConditions();
|
|
|
+ processSpecialQueries();
|
|
|
+ return new QueryResult<>(this.wrapper, this.page);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 核心构建逻辑 ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建基础查询条件(等值查询)
|
|
|
+ */
|
|
|
+ private void buildBaseQueryConditions() {
|
|
|
+ if (queryEntity == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ List<Field> allFields = getAllFields(queryEntity.getClass());
|
|
|
+
|
|
|
+ for (Field field : allFields) {
|
|
|
+ if (shouldSkipField(field)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ Object value = getFieldValue(field);
|
|
|
+ if (value == null || shouldSkipValue(value)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ String columnName = getColumnName(field);
|
|
|
+ if (columnName == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ Object serializableValue = ensureSerializable(value);
|
|
|
+ if (serializableValue != null) {
|
|
|
+ String fieldName = field.getName();
|
|
|
+
|
|
|
+ // 判断是否使用模糊查询
|
|
|
+ boolean shouldUseLike = false;
|
|
|
+
|
|
|
+ // 1. 检查是否在指定的模糊查询字段列表中
|
|
|
+ if (likeFields.contains(fieldName)) {
|
|
|
+ shouldUseLike = true;
|
|
|
+ }
|
|
|
+ // 2. 检查是否全局启用String字段模糊查询
|
|
|
+ else if (stringFieldLike && value instanceof String) {
|
|
|
+ shouldUseLike = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (shouldUseLike && value instanceof String) {
|
|
|
+ String likeValue = buildLikeValue(serializableValue.toString());
|
|
|
+ wrapper.like(columnName, likeValue);
|
|
|
+ } else {
|
|
|
+ wrapper.eq(columnName, serializableValue);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理特殊查询场景(批量、范围、模糊查询)
|
|
|
+ */
|
|
|
+ private void processSpecialQueries() {
|
|
|
+ if (queryEntity == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ List<Field> allFields = getAllFields(queryEntity.getClass());
|
|
|
+
|
|
|
+ for (Field field : allFields) {
|
|
|
+ if (isStaticField(field)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ Object value = getFieldValue(field);
|
|
|
+ if (value == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ String fieldName = field.getName();
|
|
|
+
|
|
|
+ // 批量查询:集合类型字段
|
|
|
+ if (value instanceof Collection) {
|
|
|
+ processBatchQuery(fieldName, (Collection<?>) value);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 范围查询:Start/End 后缀
|
|
|
+ if (isRangeQueryField(fieldName)) {
|
|
|
+ processRangeQuery(fieldName, value);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 模糊查询:Like 后缀
|
|
|
+ if (isLikeQueryField(fieldName)) {
|
|
|
+ processLikeQuery(fieldName, value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 字段过滤判断 ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断是否应该跳过该字段
|
|
|
+ */
|
|
|
+ private boolean shouldSkipField(Field field) {
|
|
|
+ String fieldName = field.getName();
|
|
|
+
|
|
|
+ // 跳过特殊查询字段
|
|
|
+ if (isSpecialQueryField(fieldName)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 跳过 transient 字段
|
|
|
+ return Modifier.isTransient(field.getModifiers());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断是否为特殊查询字段(_List/_Start/_End/_Like后缀)
|
|
|
+ */
|
|
|
+ private boolean isSpecialQueryField(String fieldName) {
|
|
|
+ return fieldName.endsWith(SUFFIX_LIST) ||
|
|
|
+ fieldName.endsWith(SUFFIX_START) ||
|
|
|
+ fieldName.endsWith(SUFFIX_END) ||
|
|
|
+ fieldName.endsWith(SUFFIX_LIKE);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断是否为范围查询字段
|
|
|
+ */
|
|
|
+ private boolean isRangeQueryField(String fieldName) {
|
|
|
+ return fieldName.endsWith(SUFFIX_START) || fieldName.endsWith(SUFFIX_END);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断是否为模糊查询字段
|
|
|
+ */
|
|
|
+ private boolean isLikeQueryField(String fieldName) {
|
|
|
+ return fieldName.endsWith(SUFFIX_LIKE);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断是否为静态字段
|
|
|
+ */
|
|
|
+ private boolean isStaticField(Field field) {
|
|
|
+ return Modifier.isStatic(field.getModifiers());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断是否应该跳过该值
|
|
|
+ */
|
|
|
+ private boolean shouldSkipValue(Object value) {
|
|
|
+ // 空字符串
|
|
|
+ if (ignoreEmptyStr && value instanceof String && ((String) value).trim().isEmpty()) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 空集合
|
|
|
+ return value instanceof Collection && ((Collection<?>) value).isEmpty();
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 查询类型处理 ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理批量查询(IN查询)
|
|
|
+ */
|
|
|
+ private void processBatchQuery(String fieldName, Collection<?> collection) {
|
|
|
+ String baseFieldName = removeSuffix(fieldName, SUFFIX_LIST);
|
|
|
+ String columnName = getColumnNameByFieldName(baseFieldName, fieldName);
|
|
|
+
|
|
|
+ if (columnName != null) {
|
|
|
+ List<Object> serializableList = filterSerializableItems(collection);
|
|
|
+ if (!serializableList.isEmpty()) {
|
|
|
+ wrapper.in(columnName, serializableList);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理范围查询
|
|
|
+ */
|
|
|
+ private void processRangeQuery(String fieldName, Object value) {
|
|
|
+ String baseFieldName = removeSuffix(fieldName, SUFFIX_START, SUFFIX_END);
|
|
|
+ String columnName = getColumnNameByFieldName(baseFieldName);
|
|
|
+
|
|
|
+ if (columnName != null) {
|
|
|
+ Object serializableValue = ensureSerializable(value);
|
|
|
+ if (serializableValue != null) {
|
|
|
+ if (fieldName.endsWith(SUFFIX_START)) {
|
|
|
+ wrapper.ge(columnName, serializableValue);
|
|
|
+ } else {
|
|
|
+ wrapper.le(columnName, serializableValue);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理模糊查询
|
|
|
+ */
|
|
|
+ private void processLikeQuery(String fieldName, Object value) {
|
|
|
+ String baseFieldName = removeSuffix(fieldName, SUFFIX_LIKE);
|
|
|
+ String columnName = getColumnNameByFieldName(baseFieldName);
|
|
|
+
|
|
|
+ if (columnName != null && value != null) {
|
|
|
+ String likeValue = buildLikeValue(value);
|
|
|
+ if (likeValue != null) {
|
|
|
+ wrapper.like(columnName, likeValue);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 工具方法 ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取字段值
|
|
|
+ */
|
|
|
+ private Object getFieldValue(Field field) {
|
|
|
+ try {
|
|
|
+ field.setAccessible(true);
|
|
|
+ return field.get(queryEntity);
|
|
|
+ } catch (IllegalAccessException e) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取数据库字段名(通过字段对象)
|
|
|
+ */
|
|
|
+ private String getColumnName(Field field) {
|
|
|
+ TableField tableField = field.getAnnotation(TableField.class);
|
|
|
+
|
|
|
+ // 检查字段是否标记为不存在
|
|
|
+ if (tableField != null && !tableField.exist()) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 优先使用注解指定的字段名
|
|
|
+ if (tableField != null && !tableField.value().isEmpty()) {
|
|
|
+ return tableField.value();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 默认:驼峰转下划线
|
|
|
+ return camelToUnderscore(field.getName());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取数据库字段名(通过字段名,支持fallback)
|
|
|
+ */
|
|
|
+ private String getColumnNameByFieldName(String... fieldNames) {
|
|
|
+ for (String fieldName : fieldNames) {
|
|
|
+ try {
|
|
|
+ Field field = findField(queryEntity.getClass(), fieldName);
|
|
|
+ String columnName = getColumnName(field);
|
|
|
+ if (columnName != null) {
|
|
|
+ return columnName;
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 继续尝试下一个字段名
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 移除字段名后缀(精确实现:只移除末尾的后缀)
|
|
|
+ *
|
|
|
+ * 示例:
|
|
|
+ * - removeSuffix("id_List", "_List") → "id"
|
|
|
+ * - removeSuffix("createdTime_Start", "_Start") → "createdTime"
|
|
|
+ * - removeSuffix("name_Like", "_Like") → "name"
|
|
|
+ * - removeSuffix("id_List_List", "_List") → "id_List"(只移除末尾的)
|
|
|
+ *
|
|
|
+ * @param fieldName 字段名
|
|
|
+ * @param suffixes 要移除的后缀(按顺序尝试,找到第一个匹配的就移除)
|
|
|
+ * @return 移除后缀后的字段名
|
|
|
+ */
|
|
|
+ private String removeSuffix(String fieldName, String... suffixes) {
|
|
|
+ if (fieldName == null || fieldName.isEmpty()) {
|
|
|
+ return fieldName;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 按顺序尝试每个后缀,找到第一个匹配的就移除
|
|
|
+ for (String suffix : suffixes) {
|
|
|
+ if (suffix != null && !suffix.isEmpty() && fieldName.endsWith(suffix)) {
|
|
|
+ // 只移除末尾的后缀,使用 substring 精确移除
|
|
|
+ return fieldName.substring(0, fieldName.length() - suffix.length());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果没有匹配的后缀,返回原字段名
|
|
|
+ return fieldName;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建模糊查询值(根据likeMode模式)
|
|
|
+ *
|
|
|
+ * 模式说明:
|
|
|
+ * - BOTH: %value% (前后模糊,默认)
|
|
|
+ * - LEFT: %value (前模糊)
|
|
|
+ * - RIGHT: value% (后模糊)
|
|
|
+ */
|
|
|
+ private String buildLikeValue(Object value) {
|
|
|
+ if (value == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ String strValue = value instanceof String ? (String) value : value.toString();
|
|
|
+
|
|
|
+ switch (likeMode) {
|
|
|
+ case LEFT:
|
|
|
+ return LIKE_WILDCARD + strValue; // %value
|
|
|
+ case RIGHT:
|
|
|
+ return strValue + LIKE_WILDCARD; // value%
|
|
|
+ case BOTH:
|
|
|
+ default:
|
|
|
+ return LIKE_WILDCARD + strValue + LIKE_WILDCARD; // %value%
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 过滤集合中的可序列化元素
|
|
|
+ */
|
|
|
+ private List<Object> filterSerializableItems(Collection<?> collection) {
|
|
|
+ List<Object> result = new ArrayList<>();
|
|
|
+ for (Object item : collection) {
|
|
|
+ if (item != null && isSerializableType(item)) {
|
|
|
+ result.add(item);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 确保对象是可序列化的类型
|
|
|
+ */
|
|
|
+ private Object ensureSerializable(Object value) {
|
|
|
+ if (value == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isSerializableType(value)) {
|
|
|
+ return value;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 尝试转换为字符串
|
|
|
+ try {
|
|
|
+ return value.toString();
|
|
|
+ } catch (Exception e) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断对象是否为可序列化的基本类型
|
|
|
+ */
|
|
|
+ private boolean isSerializableType(Object obj) {
|
|
|
+ if (obj == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ Class<?> clazz = obj.getClass();
|
|
|
+
|
|
|
+ return clazz.isPrimitive() ||
|
|
|
+ Number.class.isAssignableFrom(clazz) ||
|
|
|
+ String.class.equals(clazz) ||
|
|
|
+ Boolean.class.equals(clazz) ||
|
|
|
+ Character.class.equals(clazz) ||
|
|
|
+ java.util.Date.class.isAssignableFrom(clazz) ||
|
|
|
+ java.sql.Date.class.isAssignableFrom(clazz) ||
|
|
|
+ java.sql.Timestamp.class.isAssignableFrom(clazz) ||
|
|
|
+ java.time.LocalDate.class.isAssignableFrom(clazz) ||
|
|
|
+ java.time.LocalDateTime.class.isAssignableFrom(clazz);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 驼峰命名转下划线命名
|
|
|
+ */
|
|
|
+ private String camelToUnderscore(String camelCase) {
|
|
|
+ if (camelCase == null || camelCase.isEmpty()) {
|
|
|
+ return camelCase;
|
|
|
+ }
|
|
|
+
|
|
|
+ StringBuilder result = new StringBuilder();
|
|
|
+ for (int i = 0; i < camelCase.length(); i++) {
|
|
|
+ char c = camelCase.charAt(i);
|
|
|
+ if (Character.isUpperCase(c) && i > 0) {
|
|
|
+ result.append('_');
|
|
|
+ }
|
|
|
+ result.append(Character.toLowerCase(c));
|
|
|
+ }
|
|
|
+ return result.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查找字段(支持父类)
|
|
|
+ */
|
|
|
+ private Field findField(Class<?> clazz, String fieldName) {
|
|
|
+ try {
|
|
|
+ return clazz.getDeclaredField(fieldName);
|
|
|
+ } catch (NoSuchFieldException e) {
|
|
|
+ Class<?> superClass = clazz.getSuperclass();
|
|
|
+ if (superClass != null && superClass != Object.class) {
|
|
|
+ return findField(superClass, fieldName);
|
|
|
+ }
|
|
|
+ throw new RuntimeException("字段不存在: " + fieldName + " in " + clazz.getName(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取实体类所有字段(包括父类,排除静态字段)
|
|
|
+ */
|
|
|
+ private List<Field> getAllFields(Class<?> clazz) {
|
|
|
+ List<Field> fieldList = new ArrayList<>();
|
|
|
+ while (clazz != null && clazz != Object.class) {
|
|
|
+ for (Field field : clazz.getDeclaredFields()) {
|
|
|
+ if (!isStaticField(field)) {
|
|
|
+ fieldList.add(field);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ clazz = clazz.getSuperclass();
|
|
|
+ }
|
|
|
+ return fieldList;
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 内部类 ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查询结果包装类
|
|
|
+ */
|
|
|
+ @Data
|
|
|
+ @Accessors(chain = true)
|
|
|
+ public static class QueryResult<T> {
|
|
|
+ private final QueryWrapper<T> wrapper;
|
|
|
+ private final Page<T> page;
|
|
|
+
|
|
|
+ public QueryResult(QueryWrapper<T> wrapper, Page<T> page) {
|
|
|
+ this.wrapper = wrapper;
|
|
|
+ this.page = page;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 执行列表查询
|
|
|
+ */
|
|
|
+ public List<T> list(IService<T> service) {
|
|
|
+ return service.list(wrapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 执行分页查询
|
|
|
+ */
|
|
|
+ public IPage<T> page(IService<T> service) {
|
|
|
+ if (page == null) {
|
|
|
+ throw new IllegalStateException("未设置分页参数,请先调用page()方法");
|
|
|
+ }
|
|
|
+ return service.page(page, wrapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 执行单条查询
|
|
|
+ */
|
|
|
+ public T one(IService<T> service) {
|
|
|
+ return service.getOne(wrapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 执行计数查询
|
|
|
+ */
|
|
|
+ public long count(IService<T> service) {
|
|
|
+ return service.count(wrapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取查询条件(用于调试)
|
|
|
+ */
|
|
|
+ public QueryWrapper<T> getWrapper() {
|
|
|
+ return wrapper;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|