Browse Source

代码优化

Woody 5 months ago
parent
commit
9f00e73d3b
27 changed files with 486 additions and 461 deletions
  1. 6 2
      framework-aliyun/src/main/java/com/chelvc/framework/aliyun/DefaultAliyunHandler.java
  2. 3 3
      framework-base/src/main/java/com/chelvc/framework/base/config/MultipleServerConfigurer.java
  3. 74 54
      framework-base/src/main/java/com/chelvc/framework/base/context/ApplicationContextHolder.java
  4. 27 24
      framework-base/src/main/java/com/chelvc/framework/base/util/HttpUtils.java
  5. 4 4
      framework-common/src/main/java/com/chelvc/framework/common/util/StringUtils.java
  6. 1 1
      framework-database/src/main/java/com/chelvc/framework/database/config/DatabaseConfigurer.java
  7. 1 1
      framework-database/src/main/java/com/chelvc/framework/database/config/MybatisConfigurer.java
  8. 43 5
      framework-database/src/main/java/com/chelvc/framework/database/config/TypeHandlerConfigurer.java
  9. 0 179
      framework-database/src/main/java/com/chelvc/framework/database/context/DatabaseContextHolder.java
  10. 0 53
      framework-database/src/main/java/com/chelvc/framework/database/context/TableContext.java
  11. 1 2
      framework-database/src/main/java/com/chelvc/framework/database/interceptor/DeletedIsolateInterceptor.java
  12. 11 73
      framework-database/src/main/java/com/chelvc/framework/database/interceptor/DynamicInvokeInterceptor.java
  13. 1 2
      framework-database/src/main/java/com/chelvc/framework/database/interceptor/EnvIsolateInterceptor.java
  14. 58 9
      framework-database/src/main/java/com/chelvc/framework/database/interceptor/Expressions.java
  15. 54 0
      framework-database/src/main/java/com/chelvc/framework/database/interceptor/Table.java
  16. 19 4
      framework-database/src/main/java/com/chelvc/framework/database/interceptor/TableField.java
  17. 142 0
      framework-database/src/main/java/com/chelvc/framework/database/sql/SQLUtils.java
  18. 9 11
      framework-database/src/main/java/com/chelvc/framework/database/support/Updates.java
  19. 2 2
      framework-feign/src/main/java/com/chelvc/framework/feign/interceptor/FeignInvokeInterceptor.java
  20. 1 1
      framework-kafka/src/main/java/com/chelvc/framework/kafka/config/KafkaConfigurer.java
  21. 1 2
      framework-rocketmq/src/main/java/com/chelvc/framework/rocketmq/producer/RocketMQProducerFactory.java
  22. 3 4
      framework-security/src/main/java/com/chelvc/framework/security/context/SecurityContextHolder.java
  23. 3 3
      framework-upload/src/main/java/com/chelvc/framework/upload/config/UploadConfigurer.java
  24. 7 7
      framework-upload/src/main/java/com/chelvc/framework/upload/config/UploadProperties.java
  25. 4 4
      framework-upload/src/main/java/com/chelvc/framework/upload/support/AliyunUploadHandler.java
  26. 7 7
      framework-upload/src/main/java/com/chelvc/framework/upload/support/LocalUploadHandler.java
  27. 4 4
      framework-upload/src/main/java/com/chelvc/framework/upload/support/TencentUploadHandler.java

+ 6 - 2
framework-aliyun/src/main/java/com/chelvc/framework/aliyun/DefaultAliyunHandler.java

@@ -10,6 +10,7 @@ import com.aliyun.teaopenapi.models.Config;
 import com.aliyun.teautil.models.RuntimeOptions;
 import com.chelvc.framework.aliyun.config.AliyunProperties;
 import lombok.NonNull;
+import lombok.extern.slf4j.Slf4j;
 
 /**
  * 阿里云操作接口默认实现
@@ -17,6 +18,7 @@ import lombok.NonNull;
  * @author Woody
  * @date 2024/8/12
  */
+@Slf4j
 public class DefaultAliyunHandler implements AliyunHandler {
     private final AliyunProperties properties;
     private final com.aliyun.captcha20230305.Client captcha;
@@ -84,10 +86,12 @@ public class DefaultAliyunHandler implements AliyunHandler {
             throw new RuntimeException("Invalid aliyun response");
         }
         if (!Boolean.TRUE.equals(response.body.success)) {
-            throw new RuntimeException(response.body.code + ", " + response.body.message);
+            log.error("Aliyun intelligent captcha verify failed: {}, {}", response.body.code, response.body.message);
+            return false;
         }
         if (!Boolean.TRUE.equals(response.body.result.verifyResult)) {
-            throw new RuntimeException(response.body.result.verifyCode);
+            log.warn("Aliyun intelligent captcha verify failed: {}", response.body.result.verifyCode);
+            return false;
         }
         return true;
     }

+ 3 - 3
framework-base/src/main/java/com/chelvc/framework/base/config/MultiserverMvcConfigurer.java → framework-base/src/main/java/com/chelvc/framework/base/config/MultipleServerConfigurer.java

@@ -12,12 +12,12 @@ import org.springframework.core.io.Resource;
 import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
 
 /**
- * 多服务MVC配置
+ * 多服务部署配置
  *
  * @author Woody
- * @date 2024/1/30
+ * @date 2024/4/3
  */
-public class MultiserverMvcConfigurer implements WebMvcRegistrations {
+public class MultipleServerConfigurer implements WebMvcRegistrations {
     @Override
     public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
         List<Resource> resources = ApplicationContextHolder.getApplicationResources();

+ 74 - 54
framework-base/src/main/java/com/chelvc/framework/base/context/ApplicationContextHolder.java

@@ -23,6 +23,7 @@ import java.util.function.Supplier;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
+import com.chelvc.framework.base.config.MultipleServerConfigurer;
 import com.chelvc.framework.base.util.SpringUtils;
 import com.chelvc.framework.common.model.Paging;
 import com.chelvc.framework.common.model.Period;
@@ -214,6 +215,25 @@ public class ApplicationContextHolder implements ApplicationContextAware, Proper
         return getProperty("environment.production", boolean.class, false);
     }
 
+    /**
+     * 判断是否是多服务部署
+     *
+     * @return true/false
+     */
+    public static boolean isMultipleServer() {
+        return isMultipleServer(getApplicationContext());
+    }
+
+    /**
+     * 判断是否是多服务部署
+     *
+     * @param applicationContext 应用上下文实例
+     * @return true/false
+     */
+    public static boolean isMultipleServer(@NonNull ApplicationContext applicationContext) {
+        return applicationContext.containsBean(MultipleServerConfigurer.class.getName());
+    }
+
     /**
      * 获取应用环境对象
      *
@@ -496,6 +516,55 @@ public class ApplicationContextHolder implements ApplicationContextAware, Proper
         }).collect(Collectors.toList());
     }
 
+    /**
+     * 基于@Order注解对类对象排序
+     *
+     * @param classes 类对象列表
+     */
+    public static void sort(List<Class<?>> classes) {
+        if (ObjectUtils.notEmpty(classes)) {
+            classes.sort(Comparator.nullsFirst(Comparator.comparingInt(clazz -> ObjectUtils.ifNull(
+                    clazz.getAnnotation(Order.class), Order::value, () -> 0
+            ))));
+        }
+    }
+
+    /**
+     * 基于@Order注解对Bean排序
+     *
+     * @param beans Bean对象实例集合
+     * @param <T>   对象类型
+     * @return 排序后对象实例列表
+     */
+    public static <T> List<T> sort(Collection<? extends T> beans) {
+        if (ObjectUtils.isEmpty(beans)) {
+            return Collections.emptyList();
+        }
+        List<T> orders = Lists.newArrayListWithCapacity(beans.size());
+        sort(beans, orders::add);
+        return orders;
+    }
+
+    /**
+     * 基于@Order注解对Bean排序
+     *
+     * @param beans    Bean对象实例集合
+     * @param consumer Bean对象实例回调函数
+     * @param <T>      对象类型
+     */
+    public static <T> void sort(Collection<? extends T> beans, @NonNull Consumer<T> consumer) {
+        if (ObjectUtils.isEmpty(beans)) {
+            return;
+        }
+        beans.stream().sorted(Comparator.nullsFirst(Comparator.comparingInt(bean -> {
+            if (bean instanceof Ordered) {
+                return ((Ordered) bean).getOrder();
+            }
+            Order order = AopProxyUtils.ultimateTargetClass(bean).getAnnotation(Order.class);
+            return order == null ? 0 : order.value();
+        }))).forEach(consumer);
+    }
+
     /**
      * 获取对象实例
      *
@@ -641,8 +710,8 @@ public class ApplicationContextHolder implements ApplicationContextAware, Proper
      * @param <T>  对象类型
      * @return Bean对象实例列表
      */
-    public static <T> List<T> getOrderBeans(@NonNull Class<? extends T> type) {
-        return getOrderBeans(getApplicationContext(), type);
+    public static <T> List<T> getSortBeans(@NonNull Class<? extends T> type) {
+        return getSortBeans(getApplicationContext(), type);
     }
 
     /**
@@ -653,59 +722,10 @@ public class ApplicationContextHolder implements ApplicationContextAware, Proper
      * @param <T>                对象类型
      * @return Bean对象实例列表
      */
-    public static <T> List<T> getOrderBeans(@NonNull ApplicationContext applicationContext,
-                                            @NonNull Class<? extends T> type) {
+    public static <T> List<T> getSortBeans(@NonNull ApplicationContext applicationContext,
+                                           @NonNull Class<? extends T> type) {
         Map<String, ? extends T> beans = getBeans(applicationContext, type);
-        return ObjectUtils.isEmpty(beans) ? Collections.emptyList() : order(beans.values());
-    }
-
-    /**
-     * 基于@Order注解对类对象排序
-     *
-     * @param classes 类对象列表
-     */
-    public static void order(List<Class<?>> classes) {
-        if (ObjectUtils.notEmpty(classes)) {
-            classes.sort(Comparator.nullsFirst(Comparator.comparingInt(clazz -> ObjectUtils.ifNull(
-                    clazz.getAnnotation(Order.class), Order::value, () -> 0
-            ))));
-        }
-    }
-
-    /**
-     * 基于@Order注解对Bean排序
-     *
-     * @param beans Bean对象实例集合
-     * @param <T>   对象类型
-     * @return 排序后对象实例列表
-     */
-    public static <T> List<T> order(Collection<? extends T> beans) {
-        if (ObjectUtils.isEmpty(beans)) {
-            return Collections.emptyList();
-        }
-        List<T> orders = Lists.newArrayListWithCapacity(beans.size());
-        order(beans, orders::add);
-        return orders;
-    }
-
-    /**
-     * 基于@Order注解对Bean排序
-     *
-     * @param beans    Bean对象实例集合
-     * @param consumer Bean对象实例回调函数
-     * @param <T>      对象类型
-     */
-    public static <T> void order(Collection<? extends T> beans, @NonNull Consumer<T> consumer) {
-        if (ObjectUtils.isEmpty(beans)) {
-            return;
-        }
-        beans.stream().sorted(Comparator.nullsFirst(Comparator.comparingInt(bean -> {
-            if (bean instanceof Ordered) {
-                return ((Ordered) bean).getOrder();
-            }
-            Order order = AopProxyUtils.ultimateTargetClass(bean).getAnnotation(Order.class);
-            return order == null ? 0 : order.value();
-        }))).forEach(consumer);
+        return ObjectUtils.isEmpty(beans) ? Collections.emptyList() : sort(beans.values());
     }
 
     /**

+ 27 - 24
framework-base/src/main/java/com/chelvc/framework/base/util/HttpUtils.java

@@ -105,6 +105,33 @@ public final class HttpUtils {
         return uri(prefix + suffix);
     }
 
+    /**
+     * URL地址拼接
+     *
+     * @param domain 域名
+     * @param uris   资源地址数组
+     * @return URL地址
+     */
+    public static String url(String domain, String... uris) {
+        if (StringUtils.isEmpty(domain) || ObjectUtils.isEmpty(uris)) {
+            return domain;
+        }
+        String path = StringUtils.join(uris, StringUtils.URI_SEPARATOR);
+        if (StringUtils.isEmpty(path)) {
+            return domain;
+        }
+        path = URI_SEPARATOR_PATTERN.matcher(path).replaceAll(StringUtils.URI_SEPARATOR);
+        if (domain.charAt(domain.length() - 1) == StringUtils.URI_SEPARATOR.charAt(0)) {
+            if (path.charAt(0) == StringUtils.URI_SEPARATOR.charAt(0)) {
+                return domain + path.substring(1);
+            }
+            return domain + path;
+        } else if (path.charAt(0) == StringUtils.URI_SEPARATOR.charAt(0)) {
+            return domain + path;
+        }
+        return domain + StringUtils.URI_SEPARATOR + path;
+    }
+
     /**
      * 构建资源地址列表
      *
@@ -338,30 +365,6 @@ public final class HttpUtils {
         }
     }
 
-    /**
-     * URL地址拼接
-     *
-     * @param domain 域名
-     * @param uris   资源地址数组
-     * @return URL地址
-     */
-    public static String url(@NonNull String domain, @NonNull String... uris) {
-        if (uris == null || uris.length == 0) {
-            return domain;
-        }
-        Matcher matcher = URI_SEPARATOR_PATTERN.matcher(StringUtils.join(uris, StringUtils.URI_SEPARATOR));
-        String uri = matcher.replaceAll(StringUtils.URI_SEPARATOR);
-        if (domain.charAt(domain.length() - 1) == StringUtils.URI_SEPARATOR.charAt(0)) {
-            if (uri.charAt(0) == StringUtils.URI_SEPARATOR.charAt(0)) {
-                return domain + uri.substring(1);
-            }
-            return domain + uri;
-        } else if (uri.charAt(0) == StringUtils.URI_SEPARATOR.charAt(0)) {
-            return domain + uri;
-        }
-        return domain + StringUtils.URI_SEPARATOR + uri;
-    }
-
     /**
      * Http请求参数序列化
      *

+ 4 - 4
framework-common/src/main/java/com/chelvc/framework/common/util/StringUtils.java

@@ -1050,7 +1050,7 @@ public final class StringUtils {
      */
     private static <T> String join(@NonNull Stream<T> stream, @NonNull CharSequence delimiter,
                                    @NonNull Function<T, String> adapter) {
-        return stream.filter(Objects::nonNull).map(adapter).filter(Objects::nonNull)
+        return stream.filter(StringUtils::notEmpty).map(adapter).filter(Objects::nonNull)
                 .collect(Collectors.joining(delimiter));
     }
 
@@ -1066,7 +1066,7 @@ public final class StringUtils {
      */
     private static <T> String join(@NonNull Stream<T> stream, @NonNull CharSequence delimiter,
                                    @NonNull Function<T, String> adapter, @NonNull Comparator<String> comparator) {
-        return stream.filter(Objects::nonNull).map(adapter).filter(Objects::nonNull).sorted(comparator)
+        return stream.filter(StringUtils::notEmpty).map(adapter).filter(Objects::nonNull).sorted(comparator)
                 .collect(Collectors.joining(delimiter));
     }
 
@@ -1156,7 +1156,7 @@ public final class StringUtils {
         if (ObjectUtils.isEmpty(mapping)) {
             return EMPTY;
         }
-        return mapping.entrySet().stream().filter(entry -> Objects.nonNull(entry.getKey()))
+        return mapping.entrySet().stream().filter(entry -> notEmpty(entry.getKey()))
                 .map(entry -> adapter.apply(entry.getKey(), entry.getValue())).collect(Collectors.joining(delimiter));
     }
 
@@ -1194,7 +1194,7 @@ public final class StringUtils {
         if (ObjectUtils.isEmpty(mapping)) {
             return EMPTY;
         }
-        return mapping.keySet().stream().filter(Objects::nonNull).sorted(comparator)
+        return mapping.keySet().stream().filter(StringUtils::notEmpty).sorted(comparator)
                 .map(key -> adapter.apply(key, mapping.get(key))).collect(Collectors.joining(delimiter));
     }
 

+ 1 - 1
framework-database/src/main/java/com/chelvc/framework/database/config/DatabaseConfigurer.java

@@ -117,7 +117,7 @@ public class DatabaseConfigurer {
         MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
 
         // 设置自定义租户隔离拦截器
-        ApplicationContextHolder.getOrderBeans(applicationContext, InnerInterceptor.class)
+        ApplicationContextHolder.getSortBeans(applicationContext, InnerInterceptor.class)
                 .forEach(interceptor::addInnerInterceptor);
 
         // 设置数据环境隔离过滤拦截器

+ 1 - 1
framework-database/src/main/java/com/chelvc/framework/database/config/MybatisConfigurer.java

@@ -163,7 +163,7 @@ public abstract class MybatisConfigurer {
         }
 
         // 基于@Order注解对拦截器对象排序
-        ApplicationContextHolder.order(classes);
+        ApplicationContextHolder.sort(classes);
 
         // 初始化对象实例
         List<MybatisConfigurer> interceptors = Lists.newArrayListWithCapacity(classes.size());

+ 43 - 5
framework-database/src/main/java/com/chelvc/framework/database/config/TypeHandlerConfigurer.java

@@ -1,14 +1,30 @@
 package com.chelvc.framework.database.config;
 
+import java.io.InputStream;
+import java.io.Reader;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.math.BigInteger;
 import java.sql.CallableStatement;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.Month;
+import java.time.OffsetDateTime;
+import java.time.OffsetTime;
+import java.time.Year;
+import java.time.YearMonth;
+import java.time.ZonedDateTime;
+import java.time.chrono.JapaneseDate;
 import java.util.Collection;
+import java.util.Date;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -30,7 +46,6 @@ import com.chelvc.framework.common.model.Region;
 import com.chelvc.framework.common.util.ObjectUtils;
 import com.chelvc.framework.common.util.StringUtils;
 import com.chelvc.framework.database.annotation.Sensitive;
-import com.chelvc.framework.database.context.DatabaseContextHolder;
 import com.chelvc.framework.database.handler.BooleanArrayTypeHandler;
 import com.chelvc.framework.database.handler.BooleanSetTypeHandler;
 import com.chelvc.framework.database.handler.BooleansTypeHandler;
@@ -80,6 +95,7 @@ import com.chelvc.framework.database.handler.StringSetTypeHandler;
 import com.chelvc.framework.database.handler.StringsTypeHandler;
 import com.chelvc.framework.database.sql.CallableStringDecrypter;
 import com.chelvc.framework.database.sql.ResultStringDecrypter;
+import com.chelvc.framework.database.sql.SQLUtils;
 import com.chelvc.framework.database.sql.WriteStringEncryptor;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
@@ -163,10 +179,32 @@ public class TypeHandlerConfigurer extends MybatisConfigurer {
      * @return true/false
      */
     private boolean isMetaType(Class<?> type) {
-        return DatabaseContextHolder.isTypeHandlerRegistered(type) || Map.class.isAssignableFrom(type)
+        return this.isTypeHandlerRegistered(type) || Map.class.isAssignableFrom(type)
                 || Collection.class.isAssignableFrom(type);
     }
 
+    /**
+     * 判断对象类型处理器是否已注册
+     *
+     * @param type 对象类型
+     * @return true/false
+     */
+    private boolean isTypeHandlerRegistered(Class<?> type) {
+        return type == byte.class || type == Byte.class || type == byte[].class || type == Byte[].class
+                || type == char.class || type == Character.class || type == short.class || type == Short.class
+                || type == int.class || type == Integer.class || type == long.class || type == Long.class
+                || type == float.class || type == Float.class || type == double.class || type == Double.class
+                || type == boolean.class || type == Boolean.class || type == String.class || type == Object.class
+                || type == BigInteger.class || type == BigDecimal.class || type == Reader.class
+                || type == InputStream.class || type == Date.class || type == java.sql.Date.class
+                || type == java.sql.Time.class || type == java.sql.Timestamp.class || type == Instant.class
+                || type == LocalDate.class || type == LocalTime.class || type == LocalDateTime.class
+                || type == OffsetTime.class || type == OffsetDateTime.class || type == ZonedDateTime.class
+                || type == Year.class || type == Month.class || type == YearMonth.class || type == JapaneseDate.class
+                || (type != null && Enum.class.isAssignableFrom(type)) || type == File.class || type == Period.class
+                || type == Region.class || type == Modification.class;
+    }
+
     /**
      * 判断对象是否可配置类型处理器
      *
@@ -185,7 +223,7 @@ public class TypeHandlerConfigurer extends MybatisConfigurer {
 
             // JSON字段
             Type type = TypeParameterResolver.resolveFieldType(field, clazz);
-            return !DatabaseContextHolder.isTypeHandlerRegistered(ObjectUtils.type2class(type));
+            return !this.isTypeHandlerRegistered(ObjectUtils.type2class(type));
         }));
     }
 
@@ -346,7 +384,7 @@ public class TypeHandlerConfigurer extends MybatisConfigurer {
         if (((handler == null && (handler = (Class<? extends TypeHandler<?>>)
                 ObjectUtils.ifNull(annotation, TableField::typeHandler)) == null)
                 || handler == UnknownTypeHandler.class || handler == JacksonTypeHandler.class)
-                && !DatabaseContextHolder.isTypeHandlerRegistered(model)) {
+                && !this.isTypeHandlerRegistered(model)) {
             handler = this.lookupJsonHandlerClass(type);
         }
 
@@ -491,7 +529,7 @@ public class TypeHandlerConfigurer extends MybatisConfigurer {
         aliases.add(StringUtils.hump2underscore(property));
         aliases.add(StringUtils.underscore2hump(property));
         String column = ObjectUtils.ifNull(field.getAnnotation(TableField.class), TableField::value);
-        if (StringUtils.notEmpty(column = DatabaseContextHolder.unquote(column))) {
+        if (StringUtils.notEmpty(column = SQLUtils.unquote(column))) {
             aliases.add(column);
             aliases.add(StringUtils.hump2underscore(column));
             aliases.add(StringUtils.underscore2hump(column));

+ 0 - 179
framework-database/src/main/java/com/chelvc/framework/database/context/DatabaseContextHolder.java

@@ -1,60 +1,28 @@
 package com.chelvc.framework.database.context;
 
-import java.io.InputStream;
-import java.io.Reader;
 import java.lang.reflect.Modifier;
-import java.math.BigDecimal;
-import java.math.BigInteger;
 import java.sql.Connection;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
-import java.time.Instant;
-import java.time.LocalDate;
-import java.time.LocalDateTime;
-import java.time.LocalTime;
-import java.time.Month;
-import java.time.OffsetDateTime;
-import java.time.OffsetTime;
-import java.time.Year;
-import java.time.YearMonth;
-import java.time.ZonedDateTime;
-import java.time.chrono.JapaneseDate;
 import java.util.Collection;
-import java.util.Date;
 import java.util.List;
-import java.util.Map;
 import java.util.function.Function;
 import java.util.function.Supplier;
 
 import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.core.metadata.TableInfo;
-import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
-import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
-import com.baomidou.mybatisplus.core.toolkit.support.ColumnCache;
-import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
-import com.baomidou.mybatisplus.core.toolkit.support.SerializedLambda;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.chelvc.framework.base.context.ApplicationContextHolder;
 import com.chelvc.framework.base.util.SpringUtils;
 import com.chelvc.framework.common.function.Executor;
 import com.chelvc.framework.common.function.Handler;
 import com.chelvc.framework.common.function.Provider;
-import com.chelvc.framework.common.model.File;
-import com.chelvc.framework.common.model.Modification;
 import com.chelvc.framework.common.model.Pagination;
 import com.chelvc.framework.common.model.Paging;
-import com.chelvc.framework.common.model.Period;
-import com.chelvc.framework.common.model.Region;
-import com.chelvc.framework.common.util.AssertUtils;
 import com.chelvc.framework.common.util.ObjectUtils;
-import com.chelvc.framework.common.util.StringUtils;
 import com.chelvc.framework.database.crypto.DatabaseCipherHandler;
 import com.chelvc.framework.database.entity.Entity;
-import com.google.common.collect.Maps;
 import lombok.NonNull;
-import org.apache.ibatis.reflection.property.PropertyNamer;
-import org.apache.ibatis.type.TypeHandler;
 import org.mybatis.spring.SqlSessionTemplate;
 import org.springframework.context.ApplicationContext;
 import org.springframework.dao.DuplicateKeyException;
@@ -66,16 +34,6 @@ import org.springframework.dao.DuplicateKeyException;
  * @date 2024/1/30
  */
 public final class DatabaseContextHolder {
-    /**
-     * 表名称/上下文映射表
-     */
-    private static final Map<String, TableContext> TABLE_CONTEXT_MAPPING = Maps.newConcurrentMap();
-
-    /**
-     * 类对象/类型处理器实例映射表
-     */
-    private static final Map<Class<?>, TypeHandler<?>> TYPE_HANDLER_MAPPING = Maps.newConcurrentMap();
-
     /**
      * 事务处理器
      */
@@ -264,143 +222,6 @@ public final class DatabaseContextHolder {
         }
     }
 
-    /**
-     * 去掉表、字段名称反引号
-     *
-     * @param name 表、字段名称
-     * @return 表、字段名称
-     */
-    public static String unquote(String name) {
-        return isQuoted(name) ? name.substring(1, name.length() - 1) : name;
-    }
-
-    /**
-     * 判断表、字段名称是否包含反引号
-     *
-     * @param name 表、字段名称
-     * @return true/false
-     */
-    public static boolean isQuoted(String name) {
-        return StringUtils.notEmpty(name) && name.charAt(0) == '`' && name.charAt(name.length() - 1) == '`';
-    }
-
-    /**
-     * 获取表数据模型
-     *
-     * @param table 表名
-     * @return 数据模型对象
-     */
-    public static Class<?> getTableModel(@NonNull String table) {
-        return ObjectUtils.ifNull(getTableContext(table), TableContext::getModel);
-    }
-
-    /**
-     * 获取表上下文
-     *
-     * @param table 表名称
-     * @return 表上下文
-     */
-    public static TableContext getTableContext(@NonNull String table) {
-        String unquote = unquote(table);
-        TableContext context = TABLE_CONTEXT_MAPPING.get(unquote);
-        if (context == null) {
-            context = TABLE_CONTEXT_MAPPING.computeIfAbsent(unquote, k -> {
-                TableInfo info = TableInfoHelper.getTableInfo(table);
-                if (info == null && isQuoted(table)) {
-                    info = TableInfoHelper.getTableInfo(unquote);
-                }
-                return ObjectUtils.ifNull(info, TableContext::new);
-            });
-        }
-        return context;
-    }
-
-    /**
-     * 获取表字段上下文
-     *
-     * @param table 表名称
-     * @param field 字段名称
-     * @return 表字段上下文
-     */
-    public static TableFieldContext getTableFieldContext(@NonNull String table, @NonNull String field) {
-        return ObjectUtils.ifNull(getTableContext(table), context -> context.getFieldContext(field));
-    }
-
-    /**
-     * 获取TypeHandler实例
-     *
-     * @param clazz TypeHandler对象类型
-     * @param <T>   TypeHandler类型
-     * @return TypeHandler实例
-     */
-    @SuppressWarnings("unchecked")
-    public static <T extends TypeHandler<?>> T getTypeHandler(@NonNull Class<T> clazz) {
-        T handler = (T) TYPE_HANDLER_MAPPING.get(clazz);
-        if (handler == null) {
-            handler = (T) TYPE_HANDLER_MAPPING.computeIfAbsent(clazz, k -> ObjectUtils.instance(clazz));
-        }
-        return handler;
-    }
-
-    /**
-     * 将Getter方法转换成字段名称
-     *
-     * @param getter Getter方法
-     * @return 字段名称
-     */
-    public static String getter2column(@NonNull SFunction<?, ?> getter) {
-        SerializedLambda lambda = LambdaUtils.resolve(getter);
-        String property = PropertyNamer.methodToProperty(lambda.getImplMethodName());
-        return property2column(lambda.getInstantiatedType(), property);
-    }
-
-    /**
-     * 将Getter方法转换成属性名称
-     *
-     * @param getter Getter方法
-     * @return 属性名称
-     */
-    public static String getter2property(@NonNull SFunction<?, ?> getter) {
-        SerializedLambda lambda = LambdaUtils.resolve(getter);
-        return PropertyNamer.methodToProperty(lambda.getImplMethodName());
-    }
-
-    /**
-     * 将属性名转换成字段名称
-     *
-     * @param clazz    实体对象
-     * @param property 属性名称
-     * @return 字段名称
-     */
-    public static String property2column(@NonNull Class<?> clazz, @NonNull String property) {
-        Map<String, ColumnCache> columns = LambdaUtils.getColumnMap(clazz);
-        ColumnCache column = ObjectUtils.ifNull(columns, cs -> cs.get(LambdaUtils.formatKey(property)));
-        AssertUtils.nonnull(column, () -> "Column not found for property: " + clazz.getName() + "." + property);
-        return column.getColumn();
-    }
-
-    /**
-     * 判断对象类型处理器是否已注册
-     *
-     * @param type 对象类型
-     * @return true/false
-     */
-    public static boolean isTypeHandlerRegistered(Class<?> type) {
-        return type == byte.class || type == Byte.class || type == byte[].class || type == Byte[].class
-                || type == char.class || type == Character.class || type == short.class || type == Short.class
-                || type == int.class || type == Integer.class || type == long.class || type == Long.class
-                || type == float.class || type == Float.class || type == double.class || type == Double.class
-                || type == boolean.class || type == Boolean.class || type == String.class || type == Object.class
-                || type == BigInteger.class || type == BigDecimal.class || type == Reader.class
-                || type == InputStream.class || type == Date.class || type == java.sql.Date.class
-                || type == java.sql.Time.class || type == java.sql.Timestamp.class || type == Instant.class
-                || type == LocalDate.class || type == LocalTime.class || type == LocalDateTime.class
-                || type == OffsetTime.class || type == OffsetDateTime.class || type == ZonedDateTime.class
-                || type == Year.class || type == Month.class || type == YearMonth.class || type == JapaneseDate.class
-                || (type != null && Enum.class.isAssignableFrom(type)) || type == File.class || type == Period.class
-                || type == Region.class || type == Modification.class;
-    }
-
     /**
      * 根据包名查找数据模型对象
      *

+ 0 - 53
framework-database/src/main/java/com/chelvc/framework/database/context/TableContext.java

@@ -1,53 +0,0 @@
-package com.chelvc.framework.database.context;
-
-import java.io.Serializable;
-import java.util.Collections;
-import java.util.Map;
-import java.util.stream.Collectors;
-
-import com.baomidou.mybatisplus.core.metadata.TableInfo;
-import com.chelvc.framework.common.util.ObjectUtils;
-import lombok.Getter;
-import lombok.NonNull;
-
-/**
- * 表上下文对象
- *
- * @author Woody
- * @date 2025/1/5
- */
-@Getter
-public class TableContext implements Serializable {
-    private final TableInfo table;
-    private final Map<String, TableFieldContext> fields;
-
-    public TableContext(@NonNull TableInfo table) {
-        this.table = table;
-        if (ObjectUtils.isEmpty(table.getFieldList())) {
-            this.fields = Collections.emptyMap();
-        } else {
-            this.fields = Collections.unmodifiableMap(table.getFieldList().stream().collect(Collectors.toMap(
-                    field -> DatabaseContextHolder.unquote(field.getColumn()), TableFieldContext::new
-            )));
-        }
-    }
-
-    /**
-     * 获取数据模型
-     *
-     * @return 模型对象
-     */
-    public Class<?> getModel() {
-        return this.table.getEntityType();
-    }
-
-    /**
-     * 获取表字段上下文
-     *
-     * @param field 字段名称
-     * @return 表字段上下文
-     */
-    public TableFieldContext getFieldContext(@NonNull String field) {
-        return this.fields.get(DatabaseContextHolder.unquote(field));
-    }
-}

+ 1 - 2
framework-database/src/main/java/com/chelvc/framework/database/interceptor/DeletedIsolateInterceptor.java

@@ -1,7 +1,6 @@
 package com.chelvc.framework.database.interceptor;
 
 import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
-import com.chelvc.framework.database.context.DatabaseContextHolder;
 import com.chelvc.framework.database.entity.Deletable;
 import net.sf.jsqlparser.expression.Expression;
 
@@ -24,7 +23,7 @@ public class DeletedIsolateInterceptor implements TenantLineHandler {
 
     @Override
     public boolean ignoreTable(String table) {
-        Class<?> model = DatabaseContextHolder.getTableModel(table);
+        Class<?> model = Expressions.getTableModel(table);
         return model == null || !Deletable.class.isAssignableFrom(model);
     }
 }

+ 11 - 73
framework-database/src/main/java/com/chelvc/framework/database/interceptor/DynamicInvokeInterceptor.java

@@ -13,14 +13,13 @@ import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
 import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
 import com.baomidou.mybatisplus.core.toolkit.StringPool;
 import com.chelvc.framework.common.util.ObjectUtils;
-import com.chelvc.framework.common.util.StringUtils;
 import com.chelvc.framework.database.config.DatabaseProperties;
 import com.chelvc.framework.database.context.DatabaseContextHolder;
-import com.chelvc.framework.database.context.TableFieldContext;
 import com.chelvc.framework.database.entity.Creatable;
 import com.chelvc.framework.database.entity.Deletable;
 import com.chelvc.framework.database.entity.Environmental;
 import com.chelvc.framework.database.entity.Updatable;
+import com.chelvc.framework.database.sql.SQLUtils;
 import com.google.common.collect.Lists;
 import lombok.RequiredArgsConstructor;
 import net.sf.jsqlparser.JSQLParserException;
@@ -110,67 +109,6 @@ public class DynamicInvokeInterceptor implements Interceptor {
 
     private final DatabaseProperties properties;
 
-    /**
-     * 字符串参数特殊字符转义
-     *
-     * @param value 参数值
-     * @return 参数值
-     */
-    private String escape(String value) {
-        if (StringUtils.isEmpty(value)) {
-            return value;
-        }
-
-        // 判断参数是否需要转义
-        boolean needs = false;
-        int size = value.length();
-        for (int i = 0; i < size; i++) {
-            char c = value.charAt(i);
-            if (c == '\u0000' || c == '\n' || c == '\r' || c == '\u001a' || c == '"' || c == '\'' || c == '\\') {
-                needs = true;
-                break;
-            }
-        }
-        if (!needs) {
-            return value;
-        }
-
-        // 特殊字符转义
-        StringBuilder buffer = new StringBuilder((int) ((double) size * 1.1D));
-        for (int i = 0; i < size; i++) {
-            char c = value.charAt(i);
-            switch (c) {
-                case '\u0000':
-                    buffer.append('\\');
-                    buffer.append('0');
-                    break;
-                case '\n':
-                    buffer.append('\\');
-                    buffer.append('n');
-                    break;
-                case '\r':
-                    buffer.append('\\');
-                    buffer.append('r');
-                    break;
-                case '\u001a':
-                    buffer.append('\\');
-                    buffer.append('Z');
-                    break;
-                case '\'':
-                    buffer.append('\'');
-                    buffer.append('\'');
-                    break;
-                case '\\':
-                    buffer.append('\\');
-                    buffer.append('\\');
-                    break;
-                default:
-                    buffer.append(c);
-            }
-        }
-        return buffer.toString();
-    }
-
     /**
      * 判断字段别名与指定别名是否相同
      *
@@ -693,8 +631,8 @@ public class DynamicInvokeInterceptor implements Interceptor {
      * @param parameter 参数表达式
      */
     private void bindTypeHandler(BoundSql bound, Table table, Column column, JdbcParameter parameter) {
-        TableFieldContext context = DatabaseContextHolder.getTableFieldContext(table.getName(), column.getColumnName());
-        TypeHandler<?> handler = ObjectUtils.ifNull(context, TableFieldContext::getHandler);
+        TableField field = Expressions.getTableField(table.getName(), column.getColumnName());
+        TypeHandler<?> handler = ObjectUtils.ifNull(field, TableField::getHandler);
         if (handler != null) {
             ParameterMapping mapping = bound.getParameterMappings().get(parameter.getIndex() - 1);
             SystemMetaObject.forObject(mapping).setValue("typeHandler", handler);
@@ -850,7 +788,7 @@ public class DynamicInvokeInterceptor implements Interceptor {
      */
     private boolean initializeDefaultValue(BoundSql bound, Update update, Table table, Pair<?, Expression> operator,
                                            Pair<?, Expression> datetime) {
-        Class<?> model = DatabaseContextHolder.getTableModel(table.getName());
+        Class<?> model = Expressions.getTableModel(table.getName());
         if (model == null) {
             return false;
         }
@@ -903,8 +841,8 @@ public class DynamicInvokeInterceptor implements Interceptor {
         }
 
         // 获取字段上下文信息
-        TableFieldContext context = DatabaseContextHolder.getTableFieldContext(table.getName(), column.getColumnName());
-        if (context == null || !context.isSensitive()) {
+        TableField field = Expressions.getTableField(table.getName(), column.getColumnName());
+        if (field == null || !field.isSensitive()) {
             return false;
         }
 
@@ -918,7 +856,7 @@ public class DynamicInvokeInterceptor implements Interceptor {
         if (!this.properties.getCrypto().isWritable()) {
             value = DatabaseContextHolder.getDatabaseCipherHandler().encrypt((String) value, true);
         }
-        StringValue extra = new StringValue(this.escape((String) value));
+        StringValue extra = new StringValue(SQLUtils.escape((String) value));
         if (condition instanceof InExpression) {
             InExpression in = (InExpression) condition;
             ((ExpressionList) in.getRightItemsList()).getExpressions().add(extra);
@@ -993,7 +931,7 @@ public class DynamicInvokeInterceptor implements Interceptor {
         }
 
         // 初始化参数默认值
-        Class<?> model = DatabaseContextHolder.getTableModel(insert.getTable().getName());
+        Class<?> model = Expressions.getTableModel(insert.getTable().getName());
         if (model == null) {
             return rebuild;
         }
@@ -1112,7 +1050,7 @@ public class DynamicInvokeInterceptor implements Interceptor {
         Map<String, Map<String, ExpressionList>> additional = Expressions.getSelectAdditionalConditions();
         if (ObjectUtils.notEmpty(additional)) {
             rebuild |= this.prepareSelectCondition(select, (table, condition, changing) -> {
-                Map<String, ExpressionList> conditions = additional.get(DatabaseContextHolder.unquote(table.getName()));
+                Map<String, ExpressionList> conditions = additional.get(SQLUtils.unquote(table.getName()));
                 if (ObjectUtils.isEmpty(conditions)) {
                     return false;
                 }
@@ -1172,7 +1110,7 @@ public class DynamicInvokeInterceptor implements Interceptor {
      * @param invocation 调用信息
      * @return true/false
      */
-    private boolean isIgnorable(Invocation invocation) {
+    private boolean isIgnore(Invocation invocation) {
         if (!(invocation.getTarget() instanceof StatementHandler)) {
             return true;
         }
@@ -1183,7 +1121,7 @@ public class DynamicInvokeInterceptor implements Interceptor {
 
     @Override
     public Object intercept(Invocation invocation) throws Throwable {
-        return this.isIgnorable(invocation) ? invocation.proceed() : this.prepare(invocation);
+        return this.isIgnore(invocation) ? invocation.proceed() : this.prepare(invocation);
     }
 
     @Override

+ 1 - 2
framework-database/src/main/java/com/chelvc/framework/database/interceptor/EnvIsolateInterceptor.java

@@ -1,7 +1,6 @@
 package com.chelvc.framework.database.interceptor;
 
 import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
-import com.chelvc.framework.database.context.DatabaseContextHolder;
 import com.chelvc.framework.database.entity.Environmental;
 import net.sf.jsqlparser.expression.Expression;
 
@@ -27,7 +26,7 @@ public class EnvIsolateInterceptor implements TenantLineHandler {
         if (Expressions.env().getLeft() == null) {
             return true;
         }
-        Class<?> model = DatabaseContextHolder.getTableModel(table);
+        Class<?> model = Expressions.getTableModel(table);
         return model == null || !Environmental.class.isAssignableFrom(model);
     }
 }

+ 58 - 9
framework-database/src/main/java/com/chelvc/framework/database/interceptor/Expressions.java

@@ -9,16 +9,18 @@ import java.util.Date;
 import java.util.Map;
 import java.util.Set;
 
+import com.baomidou.mybatisplus.core.metadata.TableInfo;
+import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
 import com.chelvc.framework.base.context.ApplicationContextHolder;
 import com.chelvc.framework.base.context.SessionContextHolder;
 import com.chelvc.framework.common.util.DateUtils;
 import com.chelvc.framework.common.util.ObjectUtils;
 import com.chelvc.framework.common.util.StringUtils;
-import com.chelvc.framework.database.context.DatabaseContextHolder;
-import com.chelvc.framework.database.context.TableFieldContext;
+import com.chelvc.framework.database.sql.SQLUtils;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
+import lombok.NonNull;
 import lombok.extern.slf4j.Slf4j;
 import net.sf.jsqlparser.expression.Expression;
 import net.sf.jsqlparser.expression.LongValue;
@@ -40,6 +42,11 @@ final class Expressions {
      */
     private static final String ENUM_OPTION_LIMIT_PROPERTY = "enum.option.limit";
 
+    /**
+     * 表名称/对象映射表
+     */
+    private static final Map<String, Table> TABLE_NAME_MAPPING = Maps.newConcurrentMap();
+
     private Expressions() {
     }
 
@@ -119,7 +126,7 @@ final class Expressions {
         if (ObjectUtils.isEmpty(expressions)) {
             return Collections.emptyMap();
         }
-        Map<String, Set<TableFieldContext>> tables = Maps.newHashMapWithExpectedSize(expressions.length);
+        Map<String, Set<TableField>> tables = Maps.newHashMapWithExpectedSize(expressions.length);
         for (String expression : expressions) {
             if (StringUtils.isEmpty(expression = expression.trim())) {
                 continue;
@@ -135,9 +142,9 @@ final class Expressions {
             }
 
             // 获取表字段上下文信息
-            TableFieldContext context = DatabaseContextHolder.getTableFieldContext(table, column);
-            if (context != null && Enum.class.isAssignableFrom(context.getType())) {
-                tables.computeIfAbsent(DatabaseContextHolder.unquote(table), k -> Sets.newHashSet()).add(context);
+            TableField field = getTableField(table, column);
+            if (field != null && Enum.class.isAssignableFrom(field.getType())) {
+                tables.computeIfAbsent(SQLUtils.unquote(table), k -> Sets.newHashSet()).add(field);
             }
         }
         if (ObjectUtils.isEmpty(tables)) {
@@ -148,7 +155,7 @@ final class Expressions {
         Map<String, Map<String, ExpressionList>> conditions = Maps.newHashMapWithExpectedSize(tables.size());
         tables.forEach((table, contexts) -> {
             Map<String, ExpressionList> additional = Maps.newLinkedHashMapWithExpectedSize(contexts.size());
-            for (TableFieldContext context : contexts) {
+            for (TableField context : contexts) {
                 Field[] fields = context.getType().getDeclaredFields();
                 if (ObjectUtils.notEmpty(fields)) {
                     ArrayList<Expression> values = Lists.newArrayListWithCapacity(fields.length);
@@ -205,8 +212,50 @@ final class Expressions {
     public static Pair<Date, Expression> datetime() {
         Date datetime = new Date();
         LocalDateTime now = DateUtils.convert(datetime);
-        String format = DateUtils.DEFAULT_TIMESTAMP_FORMATTER.format(now);
-        return Pair.of(datetime, new StringValue(format));
+        String value = DateUtils.DEFAULT_TIMESTAMP_FORMATTER.format(now);
+        return Pair.of(datetime, new StringValue(value));
+    }
+
+    /**
+     * 获取表上下文
+     *
+     * @param name 表名称
+     * @return 表上下文
+     */
+    public static Table getTable(@NonNull String name) {
+        String unquote = SQLUtils.unquote(name);
+        Table table = TABLE_NAME_MAPPING.get(unquote);
+        if (table == null) {
+            table = TABLE_NAME_MAPPING.computeIfAbsent(unquote, k -> {
+                TableInfo info = TableInfoHelper.getTableInfo(name);
+                if (info == null && SQLUtils.isQuoted(name)) {
+                    info = TableInfoHelper.getTableInfo(unquote);
+                }
+                return ObjectUtils.ifNull(info, Table::new);
+            });
+        }
+        return table;
+    }
+
+    /**
+     * 获取表数据模型
+     *
+     * @param table 表名
+     * @return 数据模型对象
+     */
+    public static Class<?> getTableModel(@NonNull String table) {
+        return ObjectUtils.ifNull(getTable(table), Table::getModel);
+    }
+
+    /**
+     * 获取表字段上下文
+     *
+     * @param table 表名称
+     * @param field 字段名称
+     * @return 表字段上下文
+     */
+    public static TableField getTableField(@NonNull String table, @NonNull String field) {
+        return ObjectUtils.ifNull(getTable(table), context -> context.getField(field));
     }
 
     /**

+ 54 - 0
framework-database/src/main/java/com/chelvc/framework/database/interceptor/Table.java

@@ -0,0 +1,54 @@
+package com.chelvc.framework.database.interceptor;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import com.baomidou.mybatisplus.core.metadata.TableInfo;
+import com.chelvc.framework.common.util.ObjectUtils;
+import com.chelvc.framework.database.sql.SQLUtils;
+import lombok.Getter;
+import lombok.NonNull;
+
+/**
+ * 表上下文对象
+ *
+ * @author Woody
+ * @date 2025/1/5
+ */
+@Getter
+public class Table implements Serializable {
+    private final TableInfo info;
+    private final Map<String, TableField> fields;
+
+    public Table(@NonNull TableInfo info) {
+        this.info = info;
+        if (ObjectUtils.isEmpty(info.getFieldList())) {
+            this.fields = Collections.emptyMap();
+        } else {
+            this.fields = Collections.unmodifiableMap(info.getFieldList().stream().collect(Collectors.toMap(
+                    field -> SQLUtils.unquote(field.getColumn()), TableField::new
+            )));
+        }
+    }
+
+    /**
+     * 获取数据模型
+     *
+     * @return 模型对象
+     */
+    public Class<?> getModel() {
+        return this.info.getEntityType();
+    }
+
+    /**
+     * 获取表字段
+     *
+     * @param field 字段名称
+     * @return 表字段信息
+     */
+    public TableField getField(@NonNull String field) {
+        return this.fields.get(SQLUtils.unquote(field));
+    }
+}

+ 19 - 4
framework-database/src/main/java/com/chelvc/framework/database/context/TableFieldContext.java → framework-database/src/main/java/com/chelvc/framework/database/interceptor/TableField.java

@@ -1,10 +1,13 @@
-package com.chelvc.framework.database.context;
+package com.chelvc.framework.database.interceptor;
 
 import java.io.Serializable;
 import java.lang.reflect.Field;
+import java.util.Map;
 
 import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
+import com.chelvc.framework.common.util.ObjectUtils;
 import com.chelvc.framework.database.annotation.Sensitive;
+import com.google.common.collect.Maps;
 import lombok.Getter;
 import lombok.NonNull;
 import org.apache.ibatis.type.TypeHandler;
@@ -17,11 +20,16 @@ import org.apache.ibatis.type.UnknownTypeHandler;
  * @date 2024/6/10
  */
 @Getter
-public class TableFieldContext implements Serializable {
+public class TableField implements Serializable {
+    /**
+     * 类对象/类型处理器实例映射表
+     */
+    private static final Map<Class<?>, TypeHandler<?>> TYPE_HANDLER_MAPPING = Maps.newConcurrentMap();
+
     private final boolean sensitive;
     private final TableFieldInfo info;
 
-    public TableFieldContext(@NonNull TableFieldInfo info) {
+    public TableField(@NonNull TableFieldInfo info) {
         this.info = info;
         this.sensitive = info.getField().isAnnotationPresent(Sensitive.class);
     }
@@ -69,6 +77,13 @@ public class TableFieldContext implements Serializable {
      */
     public TypeHandler<?> getHandler() {
         Class<? extends TypeHandler<?>> type = this.info.getTypeHandler();
-        return type == null || type == UnknownTypeHandler.class ? null : DatabaseContextHolder.getTypeHandler(type);
+        if (type == null || type == UnknownTypeHandler.class) {
+            return null;
+        }
+        TypeHandler<?> handler = TYPE_HANDLER_MAPPING.get(type);
+        if (handler == null) {
+            handler = TYPE_HANDLER_MAPPING.computeIfAbsent(type, k -> ObjectUtils.instance(type));
+        }
+        return handler;
     }
 }

+ 142 - 0
framework-database/src/main/java/com/chelvc/framework/database/sql/SQLUtils.java

@@ -0,0 +1,142 @@
+package com.chelvc.framework.database.sql;
+
+import java.util.Map;
+
+import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
+import com.baomidou.mybatisplus.core.toolkit.support.ColumnCache;
+import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
+import com.baomidou.mybatisplus.core.toolkit.support.SerializedLambda;
+import com.chelvc.framework.common.util.AssertUtils;
+import com.chelvc.framework.common.util.ObjectUtils;
+import com.chelvc.framework.common.util.StringUtils;
+import lombok.NonNull;
+import org.apache.ibatis.reflection.property.PropertyNamer;
+
+/**
+ * SQL处理工具类
+ *
+ * @author Woody
+ * @date 2025/3/14
+ */
+public final class SQLUtils {
+    private SQLUtils() {
+    }
+
+    /**
+     * 字符串参数特殊字符转义
+     *
+     * @param value 参数值
+     * @return 参数值
+     */
+    public static String escape(String value) {
+        if (StringUtils.isEmpty(value)) {
+            return value;
+        }
+
+        // 判断参数是否需要转义
+        boolean needs = false;
+        int size = value.length();
+        for (int i = 0; i < size; i++) {
+            char c = value.charAt(i);
+            if (c == '\u0000' || c == '\n' || c == '\r' || c == '\u001a' || c == '"' || c == '\'' || c == '\\') {
+                needs = true;
+                break;
+            }
+        }
+        if (!needs) {
+            return value;
+        }
+
+        // 特殊字符转义
+        StringBuilder buffer = new StringBuilder((int) ((double) size * 1.1D));
+        for (int i = 0; i < size; i++) {
+            char c = value.charAt(i);
+            switch (c) {
+                case '\u0000':
+                    buffer.append('\\');
+                    buffer.append('0');
+                    break;
+                case '\n':
+                    buffer.append('\\');
+                    buffer.append('n');
+                    break;
+                case '\r':
+                    buffer.append('\\');
+                    buffer.append('r');
+                    break;
+                case '\u001a':
+                    buffer.append('\\');
+                    buffer.append('Z');
+                    break;
+                case '\'':
+                    buffer.append('\'');
+                    buffer.append('\'');
+                    break;
+                case '\\':
+                    buffer.append('\\');
+                    buffer.append('\\');
+                    break;
+                default:
+                    buffer.append(c);
+            }
+        }
+        return buffer.toString();
+    }
+
+    /**
+     * 去掉表、字段名称反引号
+     *
+     * @param name 表、字段名称
+     * @return 表、字段名称
+     */
+    public static String unquote(String name) {
+        return isQuoted(name) ? name.substring(1, name.length() - 1) : name;
+    }
+
+    /**
+     * 判断表、字段名称是否包含反引号
+     *
+     * @param name 表、字段名称
+     * @return true/false
+     */
+    public static boolean isQuoted(String name) {
+        return StringUtils.notEmpty(name) && name.charAt(0) == '`' && name.charAt(name.length() - 1) == '`';
+    }
+
+    /**
+     * 将Getter方法转换成字段名称
+     *
+     * @param getter Getter方法
+     * @return 字段名称
+     */
+    public static String getter2column(@NonNull SFunction<?, ?> getter) {
+        SerializedLambda lambda = LambdaUtils.resolve(getter);
+        String property = PropertyNamer.methodToProperty(lambda.getImplMethodName());
+        return property2column(lambda.getInstantiatedType(), property);
+    }
+
+    /**
+     * 将Getter方法转换成属性名称
+     *
+     * @param getter Getter方法
+     * @return 属性名称
+     */
+    public static String getter2property(@NonNull SFunction<?, ?> getter) {
+        SerializedLambda lambda = LambdaUtils.resolve(getter);
+        return PropertyNamer.methodToProperty(lambda.getImplMethodName());
+    }
+
+    /**
+     * 将属性名转换成字段名称
+     *
+     * @param clazz    实体对象
+     * @param property 属性名称
+     * @return 字段名称
+     */
+    public static String property2column(@NonNull Class<?> clazz, @NonNull String property) {
+        Map<String, ColumnCache> columns = LambdaUtils.getColumnMap(clazz);
+        ColumnCache column = ObjectUtils.ifNull(columns, cs -> cs.get(LambdaUtils.formatKey(property)));
+        AssertUtils.nonnull(column, () -> "Column not found for property: " + clazz.getName() + "." + property);
+        return column.getColumn();
+    }
+}

+ 9 - 11
framework-database/src/main/java/com/chelvc/framework/database/support/Updates.java

@@ -13,7 +13,7 @@ import com.baomidou.mybatisplus.core.toolkit.support.ColumnCache;
 import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
 import com.chelvc.framework.common.util.ObjectUtils;
 import com.chelvc.framework.common.util.StringUtils;
-import com.chelvc.framework.database.context.DatabaseContextHolder;
+import com.chelvc.framework.database.sql.SQLUtils;
 import com.google.common.collect.Maps;
 import lombok.NonNull;
 
@@ -426,7 +426,7 @@ public final class Updates {
         @SafeVarargs
         public final Update<T> use(Condition condition, @NonNull SFunction<T, ?>... getters) {
             for (SFunction<T, ?> getter : getters) {
-                this.use(condition, DatabaseContextHolder.getter2column(getter));
+                this.use(condition, SQLUtils.getter2column(getter));
             }
             return this;
         }
@@ -453,7 +453,7 @@ public final class Updates {
         @SafeVarargs
         public final Update<T> add(@NonNull SFunction<T, ?>... getters) {
             for (SFunction<T, ?> getter : getters) {
-                this.add(DatabaseContextHolder.getter2column(getter));
+                this.add(SQLUtils.getter2column(getter));
             }
             return this;
         }
@@ -480,7 +480,7 @@ public final class Updates {
         @SafeVarargs
         public final Update<T> clear(@NonNull SFunction<T, ?>... getters) {
             for (SFunction<T, ?> getter : getters) {
-                this.clear(DatabaseContextHolder.getter2column(getter));
+                this.clear(SQLUtils.getter2column(getter));
             }
             return this;
         }
@@ -507,7 +507,7 @@ public final class Updates {
         @SafeVarargs
         public final Update<T> subtract(@NonNull SFunction<T, ?>... getters) {
             for (SFunction<T, ?> getter : getters) {
-                this.subtract(DatabaseContextHolder.getter2column(getter));
+                this.subtract(SQLUtils.getter2column(getter));
             }
             return this;
         }
@@ -534,7 +534,7 @@ public final class Updates {
         @SafeVarargs
         public final Update<T> multiply(@NonNull SFunction<T, ?>... getters) {
             for (SFunction<T, ?> getter : getters) {
-                this.multiply(DatabaseContextHolder.getter2column(getter));
+                this.multiply(SQLUtils.getter2column(getter));
             }
             return this;
         }
@@ -561,7 +561,7 @@ public final class Updates {
         @SafeVarargs
         public final Update<T> divide(@NonNull SFunction<T, ?>... getters) {
             for (SFunction<T, ?> getter : getters) {
-                this.divide(DatabaseContextHolder.getter2column(getter));
+                this.divide(SQLUtils.getter2column(getter));
             }
             return this;
         }
@@ -588,7 +588,7 @@ public final class Updates {
         @SafeVarargs
         public final Update<T> jsonMerge(@NonNull SFunction<T, ?>... getters) {
             for (SFunction<T, ?> getter : getters) {
-                this.jsonMerge(DatabaseContextHolder.getter2column(getter));
+                this.jsonMerge(SQLUtils.getter2column(getter));
             }
             return this;
         }
@@ -612,9 +612,7 @@ public final class Updates {
          * @return 字段更新对象实例
          */
         public final Update<T> jsonLength(@NonNull SFunction<T, ?> getter, @NonNull SFunction<T, ?> target) {
-            return this.jsonLength(
-                    DatabaseContextHolder.getter2column(getter), DatabaseContextHolder.getter2column(target)
-            );
+            return this.jsonLength(SQLUtils.getter2column(getter), SQLUtils.getter2column(target));
         }
 
         /**

+ 2 - 2
framework-feign/src/main/java/com/chelvc/framework/feign/interceptor/FeignInvokeInterceptor.java

@@ -8,7 +8,7 @@ import java.util.Objects;
 import java.util.Set;
 import java.util.function.Predicate;
 
-import com.chelvc.framework.base.config.MultiserverMvcConfigurer;
+import com.chelvc.framework.base.context.ApplicationContextHolder;
 import com.chelvc.framework.base.context.Session;
 import com.chelvc.framework.base.context.SessionContextHolder;
 import com.chelvc.framework.base.context.Using;
@@ -51,7 +51,7 @@ public class FeignInvokeInterceptor implements RequestInterceptor {
         // 初始化请求转发配置
         FeignProperties properties = applicationContext.getBean(FeignProperties.class);
         List<FeignProperties.Forward> forwards = Lists.newArrayList(properties.getForwards());
-        if (applicationContext.containsBean(MultiserverMvcConfigurer.class.getName())
+        if (ApplicationContextHolder.isMultipleServer(applicationContext)
                 && forwards.stream().noneMatch(forward -> Objects.equals(forward.getSource(), "*"))) {
             FeignProperties.Forward forward = new FeignProperties.Forward();
             forward.setSource("*");

+ 1 - 1
framework-kafka/src/main/java/com/chelvc/framework/kafka/config/KafkaConfigurer.java

@@ -150,7 +150,7 @@ public class KafkaConfigurer implements KafkaListenerConfigurer {
                 // 初始化消息发送处理器
                 KafkaMessageSender<Object, Object> sender = super::doSend;
                 List<KafkaSenderWrapper> wrappers =
-                        ApplicationContextHolder.getOrderBeans(applicationContext, KafkaSenderWrapper.class);
+                        ApplicationContextHolder.getSortBeans(applicationContext, KafkaSenderWrapper.class);
                 for (KafkaSenderWrapper wrapper : wrappers) {
                     sender = wrapper.wrap(sender);
                 }

+ 1 - 2
framework-rocketmq/src/main/java/com/chelvc/framework/rocketmq/producer/RocketMQProducerFactory.java

@@ -67,8 +67,7 @@ public class RocketMQProducerFactory implements FactoryBean<Producer>, Disposabl
         }
 
         // 消息生产者包装处理
-        List<RocketMQProducerWrapper> wrappers =
-                ApplicationContextHolder.getOrderBeans(RocketMQProducerWrapper.class);
+        List<RocketMQProducerWrapper> wrappers = ApplicationContextHolder.getSortBeans(RocketMQProducerWrapper.class);
         for (RocketMQProducerWrapper wrapper : wrappers) {
             producer = wrapper.wrap(producer);
         }

+ 3 - 4
framework-security/src/main/java/com/chelvc/framework/security/context/SecurityContextHolder.java

@@ -6,7 +6,6 @@ import java.util.List;
 import java.util.Set;
 import java.util.stream.Stream;
 
-import com.chelvc.framework.base.config.MultiserverMvcConfigurer;
 import com.chelvc.framework.base.context.ApplicationContextHolder;
 import com.chelvc.framework.base.context.Session;
 import com.chelvc.framework.base.context.SessionContextHolder;
@@ -223,15 +222,15 @@ public final class SecurityContextHolder {
      */
     public static Set<String> getAuthorizeIgnores(@NonNull ApplicationContext applicationContext) {
         Set<String> ignores = Sets.newHashSet();
-        boolean multiserver = applicationContext.containsBean(MultiserverMvcConfigurer.class.getName());
-        List<Resource> resources = multiserver ?
+        boolean multiple = ApplicationContextHolder.isMultipleServer(applicationContext);
+        List<Resource> resources = multiple ?
                 ApplicationContextHolder.getApplicationResources() : Collections.emptyList();
         ApplicationContextHolder.lookupControllers(applicationContext).forEach(controller -> {
             Class<?> clazz = AopProxyUtils.ultimateTargetClass(controller);
 
             // 如果启用多服务MVC配置则将服务名作为接口地址前缀
             String prefix = null;
-            if (multiserver) {
+            if (multiple) {
                 Resource resource = ApplicationContextHolder.lookupClassResource(clazz, resources);
                 prefix = resource == null ? null : ApplicationContextHolder.getApplicationName(resource);
             }

+ 3 - 3
framework-upload/src/main/java/com/chelvc/framework/upload/config/UploadConfigurer.java

@@ -2,7 +2,7 @@ package com.chelvc.framework.upload.config;
 
 import com.chelvc.framework.upload.UploadHandler;
 import com.chelvc.framework.upload.support.AliyunUploadHandler;
-import com.chelvc.framework.upload.support.DefaultUploadHandler;
+import com.chelvc.framework.upload.support.LocalUploadHandler;
 import com.chelvc.framework.upload.support.TencentUploadHandler;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.context.annotation.Bean;
@@ -20,8 +20,8 @@ public class UploadConfigurer {
     @ConditionalOnMissingBean(UploadHandler.class)
     public UploadHandler uploadHandler(UploadProperties properties) {
         UploadProperties.Channel channel = properties.getChannel();
-        if (channel == UploadProperties.Channel.DEFAULT) {
-            return new DefaultUploadHandler(properties);
+        if (channel == UploadProperties.Channel.LOCAL) {
+            return new LocalUploadHandler(properties);
         } else if (channel == UploadProperties.Channel.ALIYUN) {
             return new AliyunUploadHandler(properties);
         } else if (channel == UploadProperties.Channel.TENCENT) {

+ 7 - 7
framework-upload/src/main/java/com/chelvc/framework/upload/config/UploadProperties.java

@@ -20,9 +20,9 @@ public class UploadProperties {
      */
     public enum Channel {
         /**
-         * 默认
+         * 本地
          */
-        DEFAULT,
+        LOCAL,
 
         /**
          * 阿里云
@@ -50,6 +50,11 @@ public class UploadProperties {
      */
     private String region;
 
+    /**
+     * 存储桶/目录
+     */
+    private String bucket;
+
     /**
      * 访问域名
      */
@@ -60,11 +65,6 @@ public class UploadProperties {
      */
     private String endpoint;
 
-    /**
-     * 存储目录
-     */
-    private String directory;
-
     /**
      * 上传渠道
      */

+ 4 - 4
framework-upload/src/main/java/com/chelvc/framework/upload/support/AliyunUploadHandler.java

@@ -34,16 +34,16 @@ import lombok.NonNull;
 public class AliyunUploadHandler implements UploadHandler {
     private final OSS oss;
     private final IAcsClient acs;
+    private final String bucket;
     private final String domain;
-    private final String directory;
 
     public AliyunUploadHandler(@NonNull UploadProperties properties) {
         String appid = AssertUtils.nonempty(properties.getAppid(), () -> "Aliyun upload appid is missing");
         String secret = AssertUtils.nonempty(properties.getSecret(), () -> "Aliyun upload secret is missing");
         String region = AssertUtils.nonempty(properties.getRegion(), () -> "Aliyun upload region is missing");
         String endpoint = AssertUtils.nonempty(properties.getEndpoint(), () -> "Aliyun upload endpoint is missing");
+        this.bucket = AssertUtils.nonempty(properties.getBucket(), () -> "Aliyun upload bucket is missing");
         this.domain = AssertUtils.nonempty(properties.getDomain(), () -> "Aliyun upload access domain is missing");
-        this.directory = AssertUtils.nonempty(properties.getDirectory(), () -> "Aliyun upload directory is missing");
 
         // 初始化OSS客户端
         ClientBuilderConfiguration configuration = new ClientBuilderConfiguration();
@@ -83,7 +83,7 @@ public class AliyunUploadHandler implements UploadHandler {
     public String upload(@NonNull File file, @NonNull String filename) throws IOException {
         ObjectMetadata metadata = new ObjectMetadata();
         metadata.setObjectAcl(CannedAccessControlList.PublicRead);
-        this.oss.putObject(this.directory, filename, file, metadata);
+        this.oss.putObject(this.bucket, filename, file, metadata);
         return HttpUtils.url(this.domain, filename);
     }
 
@@ -97,7 +97,7 @@ public class AliyunUploadHandler implements UploadHandler {
         ObjectMetadata metadata = new ObjectMetadata();
         metadata.setContentLength(length);
         metadata.setObjectAcl(CannedAccessControlList.PublicRead);
-        this.oss.putObject(this.directory, filename, stream, metadata);
+        this.oss.putObject(this.bucket, filename, stream, metadata);
         return HttpUtils.url(this.domain, filename);
     }
 

+ 7 - 7
framework-upload/src/main/java/com/chelvc/framework/upload/support/DefaultUploadHandler.java → framework-upload/src/main/java/com/chelvc/framework/upload/support/LocalUploadHandler.java

@@ -12,18 +12,18 @@ import com.chelvc.framework.upload.config.UploadProperties;
 import lombok.NonNull;
 
 /**
- * 文件上传默认实现
+ * 文件本地上传实现
  *
  * @author Woody
  * @date 2024/1/30
  */
-public class DefaultUploadHandler implements UploadHandler {
+public class LocalUploadHandler implements UploadHandler {
+    private final String bucket;
     private final String domain;
-    private final String directory;
 
-    public DefaultUploadHandler(@NonNull UploadProperties properties) {
+    public LocalUploadHandler(@NonNull UploadProperties properties) {
+        this.bucket = AssertUtils.nonempty(properties.getBucket(), () -> "Local upload bucket is missing");
         this.domain = AssertUtils.nonempty(properties.getDomain(), () -> "Local upload access domain is missing");
-        this.directory = AssertUtils.nonempty(properties.getDirectory(), () -> "Local upload directory is missing");
     }
 
     @Override
@@ -33,7 +33,7 @@ public class DefaultUploadHandler implements UploadHandler {
 
     @Override
     public String upload(@NonNull File file, @NonNull String filename) throws IOException {
-        FileUtils.write(new File(this.directory, filename), file);
+        FileUtils.write(new File(this.bucket, filename), file);
         return HttpUtils.url(this.domain, filename);
     }
 
@@ -44,7 +44,7 @@ public class DefaultUploadHandler implements UploadHandler {
 
     @Override
     public String upload(@NonNull InputStream stream, @NonNull String filename, long length) throws IOException {
-        FileUtils.write(new File(this.directory, filename), stream);
+        FileUtils.write(new File(this.bucket, filename), stream);
         return HttpUtils.url(this.domain, filename);
     }
 }

+ 4 - 4
framework-upload/src/main/java/com/chelvc/framework/upload/support/TencentUploadHandler.java

@@ -31,8 +31,8 @@ import lombok.NonNull;
  * @date 2024/1/30
  */
 public class TencentUploadHandler implements UploadHandler {
+    private final String bucket;
     private final String domain;
-    private final String directory;
     private final COSClient cos;
     private final CdnClient cdn;
 
@@ -40,8 +40,8 @@ public class TencentUploadHandler implements UploadHandler {
         String appid = AssertUtils.nonempty(properties.getAppid(), () -> "Tencent upload appid is missing");
         String secret = AssertUtils.nonempty(properties.getSecret(), () -> "Tencent upload secret is missing");
         String region = AssertUtils.nonempty(properties.getRegion(), () -> "Tencent upload region is missing");
+        this.bucket = AssertUtils.nonempty(properties.getBucket(), () -> "Tencent upload bucket is missing");
         this.domain = AssertUtils.nonempty(properties.getDomain(), () -> "Tencent upload access domain is missing");
-        this.directory = AssertUtils.nonempty(properties.getDirectory(), () -> "Tencent upload directory is missing");
 
         // 初始化COS客户端
         ClientConfig config = new ClientConfig();
@@ -80,7 +80,7 @@ public class TencentUploadHandler implements UploadHandler {
 
     @Override
     public String upload(@NonNull File file, @NonNull String filename) throws IOException {
-        this.cos.putObject(this.directory, filename, file);
+        this.cos.putObject(this.bucket, filename, file);
         return HttpUtils.url(this.domain, filename);
     }
 
@@ -93,7 +93,7 @@ public class TencentUploadHandler implements UploadHandler {
     public String upload(@NonNull InputStream stream, @NonNull String filename, long length) throws IOException {
         ObjectMetadata metadata = new ObjectMetadata();
         metadata.setContentLength(length);
-        this.cos.putObject(this.directory, filename, stream, metadata);
+        this.cos.putObject(this.bucket, filename, stream, metadata);
         return HttpUtils.url(this.domain, filename);
     }