繁体   English   中英

Spring 和动态数据源

[英]Spring and Dynamic DataSource

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

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>

任何例子,建议? 谢谢。

下面是我用 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;
    }
}

在 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;
    }
}

暂无
暂无

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

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