简体   繁体   English

Spring 和动态数据源

[英]Spring and Dynamic DataSource

I have two database structures, example:我有两个数据库结构,例如:

1- MAIN_DATABASE: USER PASSWORD 1- MAIN_DATABASE:用户密码

2: CUSTOMER DATABASE: CUSTOMER_A CUSTOMER_B CUSTOMER_C 2:客户数据库:CUSTOMER_A CUSTOMER_B CUSTOMER_C

I want to access the main database and after validating the data, redirect to the customer database.我想访问主数据库,验证数据后,重定向到客户数据库。

I currently use spring and configure it in applicationContext.xml我目前使用 spring 并在 applicationContext.xml 中配置它

Example:例子:

<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>

Any example, suggestion?任何例子,建议? Thanks.谢谢。

The below is my code for dynamic datasource with mybatis.下面是我用 mybatis 编写动态数据源的代码。 one is main ds.一个是主ds。 another is read ds.另一个是读ds。 Hope it useful to you.希望对你有用。

  1. use AbstractRoutingDataSource to define a DynamicDataSource使用 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. use ThreadLocal to switch datasource in context使用 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. inject different datasource via application.yml or application.properties通过 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. define an AOP to control which Dao method use which datasource.定义一个 AOP 来控制哪个 Dao 方法使用哪个数据源。 Dao is interface to access DB via mybatis. 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;
    }
}

Configure two beans with two different set of configs in app.props在 app.props 中使用两组不同的配置配置两个 bean

For one configuration you can use this (beanName = dataSource1):对于一种配置,您可以使用此 (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;
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM