SpringBoot整合多数据源
配置文件如下
spring:
datasource:
master:
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://127.0.0.1:9998/test
username: test
password: test
cluster:
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://127.0.0.1:9999/test
username: test
password: test
自定义注解
/**
* 切换数据注解 可以用于类或者方法级别 方法级别优先级 > 类级别
* @author lzh
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD,ElementType.PARAMETER})
public @interface CustomerDataSource {
/**
* 数据源类型
* @return DataSourceEnum
*/
DataSourceEnum value() default DataSourceEnum.CLUSTER;
}
/**
* 数据源类型
* @author lzh
*/
public enum DataSourceEnum {
/**
*
*/
MASTER,
/**
*
*/
CLUSTER
;
}
动态数据源配置
/**
* JPA数据源配置
* @author lzh
* @date 2021/3/31
*/
@Configuration
public class DataSourceConfig {
/**
* 创建EntityManagerFactoryBuilder
*/
@Bean
public EntityManagerFactoryBuilder entityManagerFactoryBuilder() {
return new EntityManagerFactoryBuilder(new HibernateJpaVendorAdapter(), new HashMap<>(), null);
}
@Primary
@Bean
@ConfigurationProperties(prefix = "spring.datasource.master")
public DataSourceProperties masterProperties() {
return new DataSourceProperties();
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource.cluster")
public DataSourceProperties clusterProperties() {
return new DataSourceProperties();
}
/**
* 主数据源
* @return DataSource
*/
@Bean("master")
public DataSource matserDataSource() {
return masterProperties().initializeDataSourceBuilder().build();
}
/**
* 从数据源
* @return DataSource
*/
@Bean("cluster")
public DataSource clusterDataSource() {
return clusterProperties().initializeDataSourceBuilder().build();
}
/**
* 配置动态数据源
*
* @return 动态数据源
*/
@Bean(name = "dynamicDataSource")
public DynamicDataSource dynamicDataSource() {
// 配置多数据源
Map<Object, Object> targetDataSource = new HashMap<>(2);
targetDataSource.put(DataSourceEnum.MASTER, masterDataSource());
targetDataSource.put(DataSourceEnum.CLUSTER, clusterDataSource());
DynamicDataSource dataSource = new DynamicDataSource();
dataSource.setTargetDataSources(targetDataSource);
// 配置默认数据源
dataSource.setDefaultTargetDataSource(masterDataSource());
return dataSource;
}
/**
* JPA配置
*/
@Bean(name="entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder build) {
HashMap<String, Object> properties = new HashMap<>(1);
properties.put("hibernate.physical_naming_strategy", SpringPhysicalNamingStrategy.class);
return build.dataSource(dynamicDataSource()).
packages("com.*.dataobject").
properties(properties).
persistenceUnit("defaultPersistenceUnit").
build();
}
@Bean(name = "entityManager")
public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
return entityManagerFactory(builder).getObject().createEntityManager();
}
@Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager(EntityManagerFactoryBuilder builder){
return new JpaTransactionManager(entityManagerFactory(builder).getObject());
}
AOP动态数据源切换切面类
/**
* 动态数据源切换切面类
* @author lzh
* @date 2021/3/31
*/
@Aspect
@Component
@Slf4j
public class DynamicDataSourceAspect {
/**
* 先创建一个方法,方法名随意,但是需要制定@annotation为刚刚自定义的注解
*/
@Pointcut("@annotation(com.*.config.annotation.CustomerDataSource)")
public void test() {}
/**
* 如有使用CustomerDataSource注解,则直接将数据源切换为APPECUSTAG数据源
* 使用@Before,需要先引入上面@Pointcut注解的方法名,在加上@annotation,
* @annotation中的值,需要和action方法中的参数名称相同(必须相同,但是名称任意)
*/
@Before("test() && @annotation(customerDataSource)")
public void setDataSource(CustomerDataSource customerDataSource) {
log.debug("切换当前线程数据源到cluster");
DataSourceContextHolder.setDataSource(customerDataSource.value());
}
/**
* 如有使用CustomerDataSource注解,方法调用完需要清除数据源,防止数据源切换异常
*/
@After("@annotation(com.*.config.annotation.CustomerDataSource)")
public void after() {
log.debug("清除当前线程数据源");
DataSourceContextHolder.clearDataSource();
}
}
线程数据源类型
/**
* 数据源类型
* @author lzh
* @date 2021/3/31
*/
@Slf4j
public class DataSourceContextHolder {
/**
* 使用ThreadLocal保证线程安全
*/
private static final ThreadLocal<DataSourceEnum> CURRENT_DATASOURCE = new ThreadLocal<>();
/**
* 往当前线程里设置数据源类型
* @param dataSourceEnum 数据源类型描述
*/
public static void setDataSource(DataSourceEnum dataSourceEnum) {
if (dataSourceEnum == null) {
throw new NullPointerException();
}
log.debug("[将当前数据源设置为]:{}", dataSourceEnum);
CURRENT_DATASOURCE.set(dataSourceEnum);
}
/**
* 获取数据源类型
* @return 当前数据源
*/
public static DataSourceEnum getDataSource() {
DataSourceEnum dataBaseType = CURRENT_DATASOURCE.get() == null ? DataSourceEnum.MASTER: CURRENT_DATASOURCE.get();
log.debug("[获取当前数据源的类型为]:{}", dataBaseType);
return dataBaseType;
}
/**
* 清空数据类型
*/
public static void clearDataSource() {
CURRENT_DATASOURCE.remove();
}
}
动态数据源配置
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.util.StringUtils;
/**
* 动态数据源配置,重写{@link AbstractRoutingDataSource#determineCurrentLookupKey()} 方法,
* 获取当前请求线程的数据源类型
* @author lzh
* @date 2021/3/31
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSource();
}
}
Mybatis数据源配置,其余不做变动
/**
* 数据源配置
* @author lzh
* @date 2021/3/31
*/
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "mybatis.configuration")
public org.apache.ibatis.session.Configuration globalConfiguration() {
return new org.apache.ibatis.session.Configuration();
}
@Primary
@Bean
@ConfigurationProperties(prefix = "spring.datasource.master")
public DataSourceProperties masterDataSourceProperties() {
return new DataSourceProperties();
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource.cluster")
public DataSourceProperties clusterDataSourceProperties() {
return new DataSourceProperties();
}
/**
* 主数据源
* @return DataSource
*/
@Bean("master")
public DataSource matserDataSource() {
return masterProperties().initializeDataSourceBuilder().build();
}
/**
* 从数据源
* @return DataSource
*/
@Bean("cluster")
public DataSource clusterDataSource() {
return clusterProperties().initializeDataSourceBuilder().build();
}
/**
* 配置动态数据源
* @return 动态数据源
*/
@Bean(name = "dynamicDataSource")
public DynamicDataSource dynamicDataSource() {
// 配置多数据源
Map<Object, Object> targetDataSource = new HashMap<>(2);
targetDataSource.put(DataSourceEnum.MASTER, masterDataSource());
targetDataSource.put(DataSourceEnum.CLUSTER, clusterDataSource());
DynamicDataSource dataSource = new DynamicDataSource();
dataSource.setTargetDataSources(targetDataSource);
// 配置默认数据源
dataSource.setDefaultTargetDataSource(masterDataSource());
return dataSource;
}
/**
* MyBatis配置
* @return SqlSessionFactory
* @throws Exception 异常
*/
@Bean(name = "SqlSessionFactory")
public SqlSessionFactory sqlSessionFactory(org.apache.ibatis.session.Configuration config) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dynamicDataSource());
bean.setConfiguration(config);
bean.setMapperLocations(
new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/*.xml"));
return bean.getObject();
}
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dynamicDataSource());
}
}