Browse Source

修复动态数据源SQL执行异常问题

Woody 1 month ago
parent
commit
57ba5b3c07

+ 4 - 17
framework-database/src/main/java/com/chelvc/framework/database/interceptor/CustomizeMybatisPlusInterceptor.java

@@ -7,7 +7,6 @@ import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerIntercept
 import com.chelvc.framework.base.context.ApplicationContextHolder;
 import lombok.NonNull;
 import org.apache.ibatis.executor.Executor;
-import org.apache.ibatis.mapping.MappedStatement;
 import org.apache.ibatis.plugin.Invocation;
 import org.springframework.context.ApplicationContext;
 
@@ -42,28 +41,16 @@ public abstract class CustomizeMybatisPlusInterceptor extends MybatisPlusInterce
     }
 
     /**
-     * 获取数据库Schema
+     * 获取SQL执行上下文
      *
-     * @return Schema名称
+     * @return 上下文对象实例
      */
-    protected abstract String getSchema();
-
-    /**
-     * 获取方法调用上下文
-     *
-     * @param invocation 调用信息
-     * @return 上下文实例
-     */
-    protected Tables.Context getContext(Invocation invocation) {
-        Object[] args = invocation.getArgs();
-        String id = ((MappedStatement) args[0]).getId();
-        return new Tables.Context(id, this.getSchema());
-    }
+    protected abstract Tables.Context getContext();
 
     @Override
     public Object intercept(Invocation invocation) throws Throwable {
         if (invocation.getTarget() instanceof Executor) {
-            Tables.setContext(this.getContext(invocation));
+            Tables.setContext(this.getContext());
             try {
                 return super.intercept(invocation);
             } finally {

+ 7 - 3
framework-database/src/main/java/com/chelvc/framework/database/interceptor/DynamicDataSourceMybatisPlusInterceptor.java

@@ -7,7 +7,7 @@ import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourcePrope
 import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration;
 import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
 import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
-import com.chelvc.framework.common.util.ObjectUtils;
+import com.chelvc.framework.common.util.StringUtils;
 import com.chelvc.framework.database.util.SQLUtils;
 import com.google.common.collect.Maps;
 import lombok.NonNull;
@@ -50,11 +50,13 @@ import org.springframework.stereotype.Component;
 @ConditionalOnClass(DynamicDataSourceAutoConfiguration.class)
 public class DynamicDataSourceMybatisPlusInterceptor extends CustomizeMybatisPlusInterceptor {
     private final String primary;
+    private final boolean routing;
     private final Map<String, String> schemas;
 
     public DynamicDataSourceMybatisPlusInterceptor(@NonNull ApplicationContext applicationContext) {
         super(applicationContext);
 
+        this.routing = applicationContext.containsBean(DynamicDatasourceInterceptor.class.getName());
         DynamicDataSourceProperties properties = applicationContext.getBean(DynamicDataSourceProperties.class);
         this.primary = properties.getPrimary();
         Map<String, DataSourceProperty> sources = properties.getDatasource();
@@ -63,7 +65,9 @@ public class DynamicDataSourceMybatisPlusInterceptor extends CustomizeMybatisPlu
     }
 
     @Override
-    protected String getSchema() {
-        return this.schemas.get(ObjectUtils.ifNull(DynamicDataSourceContextHolder.peek(), this.primary));
+    protected Tables.Context getContext() {
+        String datasource = StringUtils.ifEmpty(DynamicDataSourceContextHolder.peek(), this.primary);
+        String schema = this.schemas.get(datasource);
+        return new Tables.Context(schema, this.routing ? datasource : null);
     }
 }

+ 15 - 29
framework-database/src/main/java/com/chelvc/framework/database/interceptor/DynamicDatasourceInterceptor.java

@@ -1,14 +1,12 @@
 package com.chelvc.framework.database.interceptor;
 
-import java.util.Collections;
-import java.util.Map;
-import java.util.Set;
+import java.util.List;
 
+import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration;
 import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
 import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
+import com.beust.jcommander.internal.Lists;
 import com.chelvc.framework.common.util.ObjectUtils;
-import com.chelvc.framework.common.util.StringUtils;
-import com.google.common.collect.Maps;
 import org.aspectj.lang.ProceedingJoinPoint;
 import org.aspectj.lang.annotation.Around;
 import org.aspectj.lang.annotation.Aspect;
@@ -25,23 +23,12 @@ import org.springframework.core.annotation.Order;
  */
 @Aspect
 @Order(Ordered.HIGHEST_PRECEDENCE)
-@ConditionalOnClass(DynamicDataSourceContextHolder.class)
+@ConditionalOnClass(DynamicDataSourceAutoConfiguration.class)
 public class DynamicDatasourceInterceptor {
-    private final Map<String, String> datasourceContext;
+    private final List<String> routes;
 
     public DynamicDatasourceInterceptor(DynamicDataSourceProperties properties) {
-        // 初始化数据源标识集合
-        Set<String> keys = properties.getDatasource().keySet();
-        if (ObjectUtils.isEmpty(keys)) {
-            this.datasourceContext = Collections.emptyMap();
-        } else {
-            this.datasourceContext = Maps.newHashMapWithExpectedSize(keys.size());
-            keys.forEach(value -> {
-                for (String key : StringUtils.splitActives(value)) {
-                    this.datasourceContext.put(key, value);
-                }
-            });
-        }
+        this.routes = Lists.newArrayList(properties.getDatasource().keySet());
     }
 
     /**
@@ -55,22 +42,21 @@ public class DynamicDatasourceInterceptor {
 
         // 根据目标类对象查找数据源标识
         String className = AopProxyUtils.ultimateTargetClass(target).getName();
-        Set<Map.Entry<String, String>> routes = this.datasourceContext.entrySet();
-        String key = routes.stream().filter(entry -> className.startsWith(entry.getKey())).findAny()
-                .map(Map.Entry::getValue).orElse(null);
-        if (StringUtils.notEmpty(key)) {
-            return key;
+        for (String route : this.routes) {
+            if (className.startsWith(route)) {
+                return route;
+            }
         }
 
         // 根据目标接口对象查找数据源标识
         Class<?>[] interfaces = AopProxyUtils.proxiedUserInterfaces(target);
-        if (interfaces.length > 0) {
+        if (ObjectUtils.notEmpty(interfaces)) {
             for (Class<?> clazz : interfaces) {
                 String interfaceName = clazz.getName();
-                String route = routes.stream().filter(entry -> interfaceName.startsWith(entry.getKey())).findAny()
-                        .map(Map.Entry::getValue).orElse(null);
-                if (StringUtils.notEmpty(route)) {
-                    return route;
+                for (String route : this.routes) {
+                    if (interfaceName.startsWith(route)) {
+                        return route;
+                    }
                 }
             }
         }

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

@@ -1055,8 +1055,8 @@ public class DynamicInvokeInterceptor implements Interceptor {
                     return false;
                 }
 
+                String schema = Tables.getContext().getSchema();
                 String unquote = SQLUtils.unquote(table.getName());
-                String schema = Objects.requireNonNull(Tables.getContext()).getSchema();
                 Expressions.Provider provider = providers.get(schema + StringPool.DOT + unquote);
                 Map<String, ItemsList> expressions = ObjectUtils.ifNull(provider, p -> p.get(tb));
                 if (ObjectUtils.isEmpty(expressions)) {

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

@@ -60,7 +60,7 @@ public class NormalDataSourceMybatisPlusInterceptor extends CustomizeMybatisPlus
     }
 
     @Override
-    protected String getSchema() {
-        return this.schema;
+    protected Tables.Context getContext() {
+        return new Tables.Context(this.schema, null);
     }
 }

+ 15 - 24
framework-database/src/main/java/com/chelvc/framework/database/interceptor/Tables.java

@@ -48,32 +48,23 @@ final class Tables {
     }
 
     /**
-     * 上下文
+     * SQL执行上下文
      */
     @Getter
     static class Context implements Serializable {
-        /**
-         * 方法ID
-         */
-        private final String id;
-
         /**
          * 数据库Schema
          */
         private final String schema;
 
-        public Context(@NonNull String id, @NonNull String schema) {
-            this.id = id;
-            this.schema = schema;
-        }
-
         /**
-         * 获取命名空间
-         *
-         * @return 命名空间
+         * 数据源标识
          */
-        public String getNamespace() {
-            return this.id.substring(0, this.id.lastIndexOf('.'));
+        private final String datasource;
+
+        public Context(@NonNull String schema, String datasource) {
+            this.schema = schema;
+            this.datasource = datasource;
         }
     }
 
@@ -193,25 +184,25 @@ final class Tables {
     }
 
     /**
-     * 获取上下文
+     * 获取SQL执行上下文
      *
      * @return 上下文实例
      */
     public static Context getContext() {
-        return CONTEXT.get().peek();
+        return Objects.requireNonNull(CONTEXT.get().peek());
     }
 
     /**
-     * 设置上下文
+     * 设置SQL执行上下文
      *
      * @param context 上下文实例
      */
-    public static void setContext(Context context) {
+    public static void setContext(@NonNull Context context) {
         CONTEXT.get().push(context);
     }
 
     /**
-     * 移除上下文
+     * 移除SQL执行上下文
      */
     public static void removeContext() {
         Deque<Context> deque = CONTEXT.get();
@@ -242,17 +233,17 @@ final class Tables {
      * @return 表信息
      */
     public static Table getTable(@NonNull String name) {
+        Context context = getContext();
         String unquote = SQLUtils.unquote(name);
-        Context context = Objects.requireNonNull(getContext());
         String key = context.getSchema() + StringPool.DOT + unquote;
         Table table = TABLE_NAME_MAPPING.get(key);
         if (table == null) {
             table = TABLE_NAME_MAPPING.computeIfAbsent(key, k -> {
                 boolean quoted = SQLUtils.isQuoted(name);
-                String namespace = context.getNamespace();
+                String datasource = context.getDatasource();
                 for (TableInfo info : TableInfoHelper.getTableInfos()) {
                     String n = getSimpleName(info.getTableName());
-                    if (Objects.equals(info.getCurrentNamespace(), namespace)
+                    if ((StringUtils.isEmpty(datasource) || info.getCurrentNamespace().startsWith(datasource))
                             && (Objects.equals(n, name) || (quoted && Objects.equals(n, unquote)))) {
                         return new Table(info);
                     }