所谓的动态的多源数据库的配置,是一个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";
}
运行 调用即可