所謂的動態的多源數據庫的配置,是一個DataSource合併 共用一個sqlsessionfactory
1、大概的結構
用到的jar包
2、com.example.entity ;com.example.dao.mapper;com.example.service;com.example.service.impl;這些包裏面的東西跟平時寫的沒啥區別,關鍵的包com.example.config 這裏面有兩個類 一個是配置DataSource 一個是sqlsessionfactory
/**
* DataSource
* @author Administrator
*
*/
@Configuration
public class DataSourceConfig {
@Autowired
Environment env;
public DataSource dataSource1() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(env.getProperty("spring.write.datasource.url"));
dataSource.setUsername(env.getProperty("spring.write.datasource.username"));
dataSource.setPassword(env.getProperty("spring.write.datasource.password"));
dataSource.setDriverClassName(env.getProperty("spring.write.datasource.driverClassName"));
return dataSource;
}
public DataSource dataSource2() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(env.getProperty("spring.read.one.url"));
dataSource.setUsername(env.getProperty("spring.read.one.username"));
dataSource.setPassword(env.getProperty("spring.read.one.password"));
dataSource.setDriverClassName(env.getProperty("spring.read.one.driverClassName"));
return dataSource;
}
public DataSource dataSource3() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(env.getProperty("spring.read.two.url"));
dataSource.setUsername(env.getProperty("spring.read.two.username"));
dataSource.setPassword(env.getProperty("spring.read.two.password"));
dataSource.setDriverClassName(env.getProperty("spring.read.two.driverClassName"));
return dataSource;
}
public DataSource dataSource4() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(env.getProperty("spring.read.three.url"));
dataSource.setUsername(env.getProperty("spring.read.three.username"));
dataSource.setPassword(env.getProperty("spring.read.three.password"));
dataSource.setDriverClassName(env.getProperty("spring.read.three.driverClassName"));
return dataSource;
}
@Bean(name = "dynamicDS")
public DataSource dataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
// 默認數據源,當沒有指定的時候使用,可以當做主數據源
dynamicDataSource.setDefaultTargetDataSource(dataSource1());
Map<Object, Object> dsMap = new HashMap();
dsMap.put("dataSource1", dataSource1());
dsMap.put("dataSource2", dataSource2());
dsMap.put("dataSource3", dataSource3());
dsMap.put("dataSource4", dataSource4());
// 註冊多數據源
dynamicDataSource.setTargetDataSources(dsMap);
return dynamicDataSource;
}
}
/**
* sqlSessionFactory
* @author Administrator
*
*/
@Configuration
@MapperScan(basePackages = {"com.example.dao.mapper"})
public class MybatisDbConfig {
@Qualifier("dynamicDS")
@Autowired
DataSource dataSource;
@Bean(name = "sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
factoryBean.setMapperLocations(resolver.getResources("classpath:mapper/*.xml"));
factoryBean.setTypeAliasesPackage("com.example.entity");
return factoryBean.getObject();
}
@Bean
public SqlSessionTemplate sqlSessionTemplate1() throws Exception {
SqlSessionTemplate template = new SqlSessionTemplate(sqlSessionFactory()); // 使用上面配置的Factory
return template;
}
}
3、com.example.datasource 側重於數據切換
public class DataSourceContextHolder {
//默認數據源
public static final String DEFAULT_DS = "dataSource1";
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
public static final Logger log = LoggerFactory.getLogger(DataSourceContextHolder.class);
// 設置數據源標識
public static void setDataSource(String dbType) {
log.info("切換到{}數據源", dbType);
contextHolder.set(dbType);
}
// 獲取數據源標識
public static String getDataSource() {
return (contextHolder.get());
}
// 清除數據源標識
public static void clearDataSource() {
contextHolder.remove();
}
}
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final Logger log = LoggerFactory.getLogger(DynamicDataSource.class);
/**
*DynamicDataSourceContextHolder代碼中使用setDataSource
* 設置當前的數據源,在路由類中使用getDataSource進行獲取,
* 交給AbstractRoutingDataSource進行注入使用。
* @return
*/
@Override
protected Object determineCurrentLookupKey() {
log.info("數據源爲===>{}", DataSourceContextHolder.getDataSource());
return DataSourceContextHolder.getDataSource();
}
}
@Aspect
@Component
@Order(value=-1)//保證該AOP在@Transactional之前執行
public class DynamicDataSourceAspect {
/**
* 定義攔截規則
*/
@Pointcut("within(com.example.service.impl.*)")
public void pointCut(){}
/**
* @Before:在方法執行之前進行執行:
* @annotation(targetDataSource):
* 會攔截註解targetDataSource的方法,否則不攔截;
*
*/
@Before("@annotation(TargetDataSource)") //這樣註解在類上會掃描不到
public void beforeDynamicDataSource(JoinPoint point){
/*類*/
Class<?> targetClass = point.getTarget().getClass();
/*方法名*/
String methodName = point.getSignature().getName();
/*方法的參數的類型*/
Class[] parameterTypes = ((MethodSignature)point.getSignature()).getParameterTypes();
String dataSourceType = DataSourceContextHolder.DEFAULT_DS;
try {
/*默認使用類型註解*/
if (targetClass.isAnnotationPresent(TargetDataSource.class)) {
TargetDataSource annotation = targetClass.getAnnotation(TargetDataSource.class);
dataSourceType = annotation.value();
}
/*由方法名和參數獲取該類下的目標方法對象 */
Method method = targetClass.getMethod(methodName, parameterTypes);
/*方法註解可以覆蓋類型註解*/
if (method!=null && method.isAnnotationPresent(TargetDataSource.class)) {
TargetDataSource annotation = method.getAnnotation(TargetDataSource.class);
dataSourceType = annotation.value();
}
} catch (Exception e) {
e.printStackTrace();
}
/*切換數據源*/
DataSourceContextHolder.setDataSource(dataSourceType);
}
/**
* 使用完後清理
*
*/
@After("@annotation(TargetDataSource)")
public void afterDynamicDataSource(JoinPoint point){
DataSourceContextHolder.clearDataSource();
}
}
//自定義註解 通過@TargetDataSource("dataSource2")知道數據源的切換 放serviceImpl 也可以放controller 根據需求
@Retention(RetentionPolicy.RUNTIME)
@Target({
ElementType.METHOD,ElementType.TYPE
})
public @interface TargetDataSource {
String value() default "dataSource1";
}
運行 調用即可