1.修改properties(spring默认的)数据源的前缀 新增中台数据
2编写数据源上下文
mport java.util.List;
public class DataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new InheritableThreadLocal<String>();
public static final String DEFAULT_DS ="defaultDs";
/*
* 管理所有的数据源id;
* 主要是为了判断数据源是否存在;
*/
public static List<String> dataSourceIds = new ArrayList<String>();
// 设置数据源类型
public static void setDataSourceType(String datasource) {
contextHolder.set(datasource);
}
// 获取数据源类型
public static String getDataSourceType() {
return (String) contextHolder.get();
}
// 清除数据源类型
public static void clearDataSourceType() {
contextHolder.remove();
}
//判断指定DataSource当前是否存在
public static boolean containsDataSource(String dataSourceId){
return dataSourceIds.contains(dataSourceId);
}
}
3 继承AbstractRoutingDataSource实现类DynamicDataSource(关键)
AbstractRoutingDataSource抽象类知识,实现AOP动态切换的关键
1.AbstractRoutingDataSource中determineTargetDataSource()方法中获取数据源
Object lookupKey = determineCurrentLookupKey();
DataSource dataSource = this.resolvedDataSources.get(lookupKey);
根据determineCurrentLookupKey()得到Datasource,并且此方法是抽象方法,应用可以实现
2.resolvedDataSources的值根据targetDataSources所得
afterPropertiesSet()方法中(在@Bean所在方法执行完成后,会调用此方法):
Map.Entry<Object, Object> entry : this.targetDataSources.entrySet()
3.然后在xml中使用<bean>或者代码中@Bean 设置DynamicDataSource的defaultTargetDataSource(默认数据源)和targetDataSources(多数据源)
4.利用自定义注解,AOP拦截动态的设置ThreadLocal的值
5.在DAO层与数据库建立连接时会根据ThreadLocal的key得到数据源
代码:getConnection()
determineTargetDataSource().getConnection();(determineTargetDataSource返回的是DataSource)
Object lookupKey = determineCurrentLookupKey();
DataSource dataSource = this.resolvedDataSources.get(lookupKey);
根据determineCurrentLookupKey()得到Datasource,并且此方法是抽象方法,应用可以实现
2.resolvedDataSources的值根据targetDataSources所得
afterPropertiesSet()方法中(在@Bean所在方法执行完成后,会调用此方法):
Map.Entry<Object, Object> entry : this.targetDataSources.entrySet()
3.然后在xml中使用<bean>或者代码中@Bean 设置DynamicDataSource的defaultTargetDataSource(默认数据源)和targetDataSources(多数据源)
4.利用自定义注解,AOP拦截动态的设置ThreadLocal的值
5.在DAO层与数据库建立连接时会根据ThreadLocal的key得到数据源
代码:getConnection()
determineTargetDataSource().getConnection();(determineTargetDataSource返回的是DataSource)
package com.lm.config.datasource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSourceType();
}
}
4配置多数据源
@Configuration
public class DataSourceConfig {
@Bean(name = "defaultDs")
@ConfigurationProperties(prefix = "jph.datasource") // application.properteis中对应属性的前缀
public DataSource dataSource1() {
return DataSourceBuilder.create().build();
}
@Bean(name = "ziKuDs")
@ConfigurationProperties(prefix = "ziku.datasource")
public DataSource dataSource2() {
return DataSourceBuilder.create().build();
}
/**
* 动态数据源: 通过AOP在不同数据源之间动态切换
* @return
*/
@Bean(name = "dynamicDS")
@Primary //优先使用
public DataSource dataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
// 默认数据源
dynamicDataSource.setDefaultTargetDataSource(dataSource1());
// 配置多数据源
Map<Object, Object> dsMap = new HashMap(4);
dsMap.put("defaultDs", dataSource1());
dsMap.put("ziKuDs", dataSource2());
dynamicDataSource.setTargetDataSources(dsMap);
return dynamicDataSource;
}
}5配置mybatis的核心类 (SqlSessionTemplate)
1)精品汇2)中台@Configuration
@MapperScan(basePackages = {"com.lm.mapper"}, sqlSessionFactoryRef = "sqlSessionFactory1")
public class jphDbConfig {
@Autowired
@Qualifier("defaultDs")
private DataSource defaultDs;
@Bean
@Primary
public SqlSessionFactory sqlSessionFactory1() throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(defaultDs);
return factoryBean.getObject();
}
@Bean
@Primary
public SqlSessionTemplate sqlSessionTemplate1() throws Exception {
SqlSessionTemplate template = new SqlSessionTemplate(sqlSessionFactory1()); // 使用上面配置的Factory
return template;
}
}
6 为了方便使用 我们使用aop加注解的方式来切换数据源
@Configuration
@MapperScan(basePackages = {"com.ziku.mapper"}, sqlSessionFactoryRef = "sqlSessionFactory2")
public class ZikuDbConfig {
@Autowired
@Qualifier("ziKuDs")
private DataSource zikuDs;
@Bean
public SqlSessionFactory sqlSessionFactory2() throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(zikuDs); // 使用titan数据源, 连接titan库
return factoryBean.getObject();
}
@Bean
public SqlSessionTemplate sqlSessionTemplate2() throws Exception {
SqlSessionTemplate template = new SqlSessionTemplate(sqlSessionFactory2()); // 使用上面配置的Factory
return template;
}
}
/**
* 在方法上使用,用于指定使用哪个数据源
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {
String value() default "defaultDs";
}
/**
* 切换数据源Advice
*/
@Aspect
@Order(-10)//保证该AOP在@Transactional之前执行
@Component
public class DynamicDataSourceAspect {
/*
* @Before("@annotation(TargetDataSource)")
* 的意思是:
* @Before:在方法执行之前进行执行:
* @annotation(targetDataSource):
* 会拦截注解targetDataSource的方法,否则不拦截;
*/
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Before("@annotation(TargetDataSource)")
public void beforeSwitchDS(JoinPoint point){
//获得当前访问的class
Class<?> className = point.getTarget().getClass();
//获得访问的方法名
String methodName = point.getSignature().getName();
//得到方法的参数的类型
Class[] argClass = ((MethodSignature)point.getSignature()).getParameterTypes();
String dataSource = DataSourceContextHolder.DEFAULT_DS;
try {
// 得到访问的方法对象
Method method = className.getMethod(methodName, argClass);
// 判断是否存在@DS注解
if (method.isAnnotationPresent(TargetDataSource.class)) {
TargetDataSource annotation = method.getAnnotation(TargetDataSource.class);
// 取出注解中的数据源名
dataSource = annotation.value();
}
} catch (Exception e) {
e.printStackTrace();
}
// 切换数据源
DataSourceContextHolder.setDataSourceType(dataSource);
}
@After("@annotation(TargetDataSource)")
public void afterSwitchDS(JoinPoint point){
DataSourceContextHolder.clearDataSourceType();
}
}