最终结果:动态的根据不同的方法访问不同的数据库
代码也不记得是在哪复制的了
application.properties
spring.primary-datasource.driver-class-name =org.postgresql.Driver
spring.primary-datasource.url = jdbc:postgresql://xxxxxxxxx/primary
spring.primary-datasource.username = postgres
spring.primary-datasource.password = 123
spring.second-datasource.driver-class-name = org.postgresql.Driver
spring.second-datasource.url = jdbc:postgresql://xxxxxxxxx/second
spring.second-datasource.username = postgres
spring.second-datasource.password = 123
1.
package net.itraf.drivingbehavior.common.DynamicDataSource;
import java.util.ArrayList;
import java.util.List;
/**
* CreateTime: 2018-08-30 10:54
* ClassName: DynamicDataSourceContextHolder
* Package: net.itraf.drivingbehavior.common.util
* Describe:
* 动态数据源上下文
*
* @author JONEE
*/
public class DynamicDataSourceContextHolder{
/**
* 默认数据源
*/
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>() {
/** * 将 master 数据源的 key 作为默认数据源的 key */
@Override
protected String initialValue() {
return "primaryDataSource";
}
};
/** * 数据源的 key 集合,用于切换时判断数据源是否存在 */
public static List<Object> dataSourceKeys = new ArrayList<>();
/** * To switch DataSource * * @param key the key */
public static void setDataSourceKey(String key) {
contextHolder.set(key);
}
/** * Get current DataSource * * @return data source key */
public static String getDataSourceKey() {
return contextHolder.get();
}
/** * To set DataSource as default */
public static void clearDataSourceKey() {
contextHolder.remove();
}
/** * Check if give DataSource is in current DataSource list * * @param key the key * @return boolean boolean */
public static boolean containDataSourceKey(String key) {
return dataSourceKeys.contains(key);
}
}
2.
package net.itraf.drivingbehavior.common.DynamicDataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
* CreateTime: 2018-08-30 10:58
* ClassName: DataSourceConfig
* Package: net.itraf.drivingbehavior.common.util
* Describe:
* 数据源配置
*
* @author JONEE
*/
@Configuration
public class DataSourceConfig{
@Bean(name = "primaryDataSource")
@ConfigurationProperties(prefix = "spring.primary-datasource")
@Primary
public DataSource primaryDataSource(){
return DataSourceBuilder.create().build();
}
@Bean(name = "secondaryDataSource")
@ConfigurationProperties(prefix = "spring.second-datasource")
public DataSource secondaryDataSource(){
return DataSourceBuilder.create().build();
}
@Autowired
@Qualifier("primaryDataSource")
private DataSource primaryDataSource;
@Autowired
@Qualifier("secondaryDataSource")
private DataSource secondaryDataSource;
@Bean("dynamicDataSource")
public DynamicDataSource dynamicDataSource(){
DynamicDataSource dataSource = new DynamicDataSource();
Map<Object, Object> targetDataSources = new HashMap<>(2);
targetDataSources.put("primaryDataSource", primaryDataSource);
targetDataSources.put("secondaryDataSource", secondaryDataSource);
DynamicDataSourceContextHolder.dataSourceKeys.addAll(targetDataSources.keySet());
//设置数据源映射
dataSource.setTargetDataSources(targetDataSources);
//设置默认数据源,当无法映射到数据源时会使用默认数据源
dataSource.setDefaultTargetDataSource(primaryDataSource);
dataSource.afterPropertiesSet();
return dataSource;
}
/**
* 配置 SqlSessionFactoryBean
* @ConfigurationProperties 在这里是为了将 MyBatis 的 mapper 位置和持久层接口的别名设置到
* Bean 的属性中,如果没有使用 *.xml 则可以不用该配置,否则将会产生 invalid bond statement 异常
*
* @return the sql session factory bean
*/
@Bean
@ConfigurationProperties(prefix = "mybatis")
public SqlSessionFactoryBean sqlSessionFactoryBean() {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
// 配置数据源,此处配置为关键配置,如果没有将 dynamicDataSource 作为数据源则不能实现切换
sqlSessionFactoryBean.setDataSource(dynamicDataSource());
//PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
//sqlSessionFactoryBean.setMapperLocations(resolver.getResources("classpath*:mapper/**/*.xml"));
return sqlSessionFactoryBean;
}
/**
* 注入 DataSourceTransactionManager 用于事务管理
*/
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dynamicDataSource());
}
}
3.
package net.itraf.drivingbehavior.common.DynamicDataSource;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* CreateTime: 2018-08-29 17:22
* ClassName: ApplicationConfiguration
* Package: net.itraf.drivingbehavior.common.util
* Describe:
* 动态数据源配置文件
*
* @author JONEE
*/
@Configuration
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.getDataSourceKey();
}
}
4.
package net.itraf.drivingbehavior.common.DynamicDataSource;
import java.lang.annotation.*;
/**
* CreateTime: 2018-08-30 11:15
* ClassName: TargetDataSource
* Package: net.itraf.drivingbehavior.common.DynamicDataSource
* Describe:
* 动态数据源自定义注解
*
* @author JONEE
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {
String value() default "primaryDataSource";;
}
5.
package net.xxx.xxx.common.DynamicDataSource;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.After;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* CreateTime: 2018-08-30 14:08
* ClassName: DynamicDataSourceAspect
* Package: net.xxx.xxx.common.DynamicDataSource
* Describe:
* 动态数据源切面类
*
* @author JONEE
*/
@Aspect
// 该切面应当先于 @Transactional 执行
@Order(-1)
@Component
public class DynamicDataSourceAspect {
@Before("@annotation(targetDataSource))")
public void switchDataSource(JoinPoint point, TargetDataSource targetDataSource) {
if (!DynamicDataSourceContextHolder.containDataSourceKey(targetDataSource.value())) {
System.out.println("DataSource [{}] doesn't exist, use default DataSource [{}] " + targetDataSource.value());
} else {
// 切换数据源
DynamicDataSourceContextHolder.setDataSourceKey(targetDataSource.value());
System.out.println("Switch DataSource to [{}] in Method [{}] " +
DynamicDataSourceContextHolder.getDataSourceKey() + point.getSignature());
}
}
@After("@annotation(targetDataSource))")
public void restoreDataSource(JoinPoint point, TargetDataSource targetDataSource) {
// 将数据源置为默认数据源
DynamicDataSourceContextHolder.clearDataSourceKey();
System.out.println("Restore DataSource to [{}] in Method [{}] " +
DynamicDataSourceContextHolder.getDataSourceKey() + point.getSignature());
}
}
JPA:
package net.data.common.dynamicdatasource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
* CreateTime: 2019-04-09 17:00
* ClassName: DataSourceConfig
* Package: net.data.common.DynamicDataSource
* Describe:
*
* @author JONEE
*/
@Configuration
@EnableJpaRepositories(value = "net.xxx.xxx",entityManagerFactoryRef = "EntityManagerFactory",transactionManagerRef="TransactionManager")
public class DataSourceConfig{
@Autowired
JpaProperties jpaProperties;
@Bean(name = "primaryDataSource")
@ConfigurationProperties(prefix = "spring.primary-datasource")
@Primary
public DataSource primaryDataSource(){
return DataSourceBuilder.create().build();
}
@Bean(name = "secondaryDataSource")
@ConfigurationProperties(prefix = "spring.second-datasource")
public DataSource secondaryDataSource(){
return DataSourceBuilder.create().build();
}
@Autowired
@Qualifier("primaryDataSource")
private DataSource primaryDataSource;
@Autowired
@Qualifier("secondaryDataSource")
private DataSource secondaryDataSource;
@Bean("dynamicDataSource")
public DynamicDataSource dynamicDataSource(){
DynamicDataSource dataSource = new DynamicDataSource();
Map<Object, Object> targetDataSources = new HashMap<>(2);
targetDataSources.put("primaryDataSource", primaryDataSource);
targetDataSources.put("secondaryDataSource", secondaryDataSource);
DynamicDataSourceContextHolder.dataSourceKeys.addAll(targetDataSources.keySet());
//设置数据源映射
dataSource.setTargetDataSources(targetDataSources);
//设置默认数据源,当无法映射到数据源时会使用默认数据源
dataSource.setDefaultTargetDataSource(primaryDataSource);
dataSource.afterPropertiesSet();
return dataSource;
}
@Bean(name = "EntityManagerFactory")
@Primary
public EntityManagerFactory EntityManagerFactory() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan("net.xxx.xxx");
factory.setDataSource(dynamicDataSource());
factory.setJpaPropertyMap(jpaProperties.getProperties());
factory.afterPropertiesSet();//在完成了其它所有相关的配置加载以及属性设置后,才初始化
return factory.getObject();
}
/**
* 配置事物管理器
* @return
*/
@Bean(name = "TransactionManager")
@Primary
public PlatformTransactionManager TransactionManager() {
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
jpaTransactionManager.setEntityManagerFactory(this.EntityManagerFactory());
return jpaTransactionManager;
}
}
使用:
在Service方法上加 @TargetDataSource(value = "secondaryDataSource")