【问题标题】:Spring and Dynamic DataSourceSpring 和动态数据源
【发布时间】:2021-02-21 22:05:20
【问题描述】:

我有两个数据库结构,例如:

1- MAIN_DATABASE:用户密码

2:客户数据库:CUSTOMER_A CUSTOMER_B CUSTOMER_C

我想访问主数据库,验证数据后,重定向到客户数据库。

我目前使用spring并在applicationContext.xml中配置

例子:

<bean id = "encryptionPassword" class = "utils.EncryptionPasswordSpring" />
<bean id = "dataSource" class = "com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method = "close">
<property name = "driverClass" value = "com.mysql.jdbc.Driver" />
<property name = "user" value = "user" />
<property name = "password" value = "123456" />
<property name = "jdbcUrl" value = "jdbc:mysql://localhost/testdb?useSSL = false" />
</bean>

任何例子,建议?谢谢。

【问题讨论】:

    标签: java spring spring-boot spring-security spring-data


    【解决方案1】:

    在 app.props 中使用两组不同的配置配置两个 bean

    对于一种配置,您可以使用此 (beanName = dataSource1):

    @Configuration
    @EnableRetry
    public class DataSourceConfiguration {
    
        @Value("${datasource1.username}")
        private String username;
    
        @Value("${datasource1.password}")
        private String password;
    
        @Value("${datasource1.url}")
        private String connection;
    
        @Bean(name = "dataSource1")
        @Primary
        public DataSource mainDataSource() {
    
            DriverManagerDataSource dataSource = new DriverManagerDataSource();
    
            dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
            dataSource.setUrl(connection);
            dataSource.setUsername(username);
            dataSource.setPassword(password);
            return dataSource;
        }
    }
    

    【讨论】:

      【解决方案2】:

      下面是我的mybatis动态数据源代码。一个是主要的ds。另一个是读ds。希望对你有用。

      1. 使用 AbstractRoutingDataSource 定义 DynamicDataSource
      import java.util.Map;
      
      import javax.sql.DataSource;
      
      import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
      
      public class DynamicDataSource extends AbstractRoutingDataSource {
      
          @Override
          protected DataSource determineTargetDataSource() {
              return super.determineTargetDataSource();
          }
          
          /**
           */
          @Override
          protected Object determineCurrentLookupKey() {
              return DynamicDataSourceContextHolder.getDataSourceKey();
          }
          
          /**
           * @param defaultDataSource
           */
          public void setDefaultDataSource(Object defaultDataSource) {
              super.setDefaultTargetDataSource(defaultDataSource);
          }
          
          /**
           * @param dataSources
           */
          public void setDataSources(Map<Object, Object> dataSources) {
              super.setTargetDataSources(dataSources);
              DynamicDataSourceContextHolder.addDataSourceKeys(dataSources.keySet());
          }
      
      }
      
      
      
      1. 使用 ThreadLocal 在上下文中切换数据源
      import java.util.ArrayList;
      import java.util.Collection;
      import java.util.Collections;
      import java.util.List;
      
      public class DynamicDataSourceContextHolder {
          
          private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>() {
              /**
               make main as default ds
               */
              @Override
              protected String initialValue() {
                  return "main";
              }
          };
      
          /**
           * 
           */
          private static List<Object> dataSourceKeys = Collections.synchronizedList(new ArrayList<>());
      
          /**
           * switch ds
           * 
           * @param key
           */
          public static void setDataSourceKey(String key) {
              contextHolder.set(key);
          }
      
          /**
           * get ds
           * 
           * @return
           */
          public static String getDataSourceKey() {
              return contextHolder.get();
          }
      
          /**
           * reset ds
           */
          public static void clearDataSourceKey() {
              contextHolder.remove();
          }
      
          /**
           * judge if ds existed
           * 
           * @param key 
           * @return
           */
          public static boolean containDataSourceKey(String key) {
              return dataSourceKeys.contains(key);
          }
      
          /**
           * add ds
           * 
           * @param keys
           * @return
           */
          public static boolean addDataSourceKeys(Collection<? extends Object> keys) {
              return dataSourceKeys.addAll(keys);
          }
      
      }
      
      1. 通过 application.yml 或 application.properties 注入不同的数据源
      import java.util.HashMap;
      import java.util.Map;
      import java.util.Properties;
      
      import javax.sql.DataSource;
      
      import org.apache.ibatis.plugin.Interceptor;
      import org.apache.ibatis.session.SqlSessionFactory;
      import org.mybatis.spring.SqlSessionFactoryBean;
      import org.mybatis.spring.SqlSessionTemplate;
      import org.springframework.beans.factory.annotation.Qualifier;
      import org.springframework.beans.factory.annotation.Value;
      import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
      import org.springframework.boot.context.properties.ConfigurationProperties;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.context.annotation.Primary;
      import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
      import org.springframework.jdbc.datasource.DataSourceTransactionManager;
      import org.springframework.transaction.PlatformTransactionManager;
      
      
      @Configuration
      public class DataSourceConfig {
          
      
          @Primary
          @Bean
          @ConfigurationProperties("spring.datasource.main")
          public DataSource main() {
              return DataSourceBuilder.create().build();
          }
       
       
          @Bean
          @ConfigurationProperties("spring.datasource.read")
          public DataSource read() {
              return DataSourceBuilder.create().build();
          }
          
          @Bean
          public DataSource dynamicDataSource(
                  @Qualifier("main") DataSource main,
                  @Qualifier("read") DataSource read
                  ) {
      
              Map<Object, Object> targetDataSources = new HashMap<>(2);
              targetDataSources.put("main", main);
              targetDataSources.put("read", read);
              DynamicDataSource dynamicDataSource = new DynamicDataSource();
              dynamicDataSource.setDefaultTargetDataSource(main); //default
              dynamicDataSource.setDataSources(targetDataSources);
              return dynamicDataSource;
          }
          
          @Bean
          public SqlSessionFactory sqlSessionFactory(
              @Qualifier("dynamicDataSource") DataSource dynamicDataSource)
                  throws Exception {
              SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
              bean.setDataSource(dynamicDataSource);
              bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mappings/**/*.xml"));
              
              return bean.getObject();
          }
      
          @Bean(name = "sqlSessionTemplate")
          public SqlSessionTemplate sqlSessionTemplate(
              @Qualifier("sqlSessionFactory") SqlSessionFactory sqlSessionFactory)
                  throws Exception {
              return new SqlSessionTemplate(sqlSessionFactory);
          }
      
          @Bean
          public PlatformTransactionManager transactionManager(
              @Qualifier("dynamicDataSource") DataSource dynamicDataSource
          ) {
              return new DataSourceTransactionManager(dynamicDataSource);
          }
          
      }
      
      
      1. 定义一个AOP来控制哪个Dao方法使用哪个数据源。 Dao 是通过 mybatis 访问 DB 的接口。
      
      import org.aspectj.lang.JoinPoint;
      import org.aspectj.lang.annotation.After;
      import org.aspectj.lang.annotation.Aspect;
      import org.aspectj.lang.annotation.Before;
      import org.aspectj.lang.annotation.Pointcut;
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      import org.springframework.beans.factory.annotation.Value;
      import org.springframework.core.annotation.Order;
      import org.springframework.stereotype.Component;
      
      @Aspect
      @Order(-1)  
      @Component
      public class DynamicDataSourceAspect {
          private final static Logger logger = LoggerFactory.getLogger(DynamicDataSourceAspect.class);
      
          
      
          private final String[] QUERY_PREFIX = { 
              "select","get","find","query","quickGet"
          };
      
      
      
          @Pointcut("execution( * com.biz.dao..*.*(..))")
          public void daoAspect() {
          }
          
          @Before("daoAspect()")
          public void beforeDao(JoinPoint point) {
      
              boolean isQueryMethod = isQueryMethod(point.getSignature().getName());
              if (isQueryMethod) {
                  switchDataSource("read");
              }
          }
      
          @After("daoAspect()")
          public void afterDao(JoinPoint point) {
              restoreDataSource();
          }
              
          //===============================private method
          private void switchDataSource(String key) {
              if (!DynamicDataSourceContextHolder.containDataSourceKey(key)) {
                  logger.debug("======>DataSource [{}] doesn't exist, use default DataSource [{}] " + key);
              } else {
                  // switch ds
                  DynamicDataSourceContextHolder.setDataSourceKey(key);
                  logger.debug("======>Switch DataSource to " + DynamicDataSourceContextHolder.getDataSourceKey());
               }      
          }
          
          private void restoreDataSource() {
              // reset to default ds
              DynamicDataSourceContextHolder.clearDataSourceKey();
          }
      
      
          private boolean isQueryMethod(String methodName) {
              for (String prefix : QUERY_PREFIX) {
                  if (methodName.startsWith(prefix)) {
                      return true;
                  }
              }
              return false;
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2012-11-10
        • 1970-01-01
        • 2012-06-22
        • 2018-07-22
        • 1970-01-01
        • 1970-01-01
        • 2019-12-02
        • 2022-06-24
        • 2017-06-24
        相关资源
        最近更新 更多