Springboot+mybatis动态设置数据源


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.AbstractRoutingDataSourcedetermineTargetDataSource()方法中获取数据源 
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)精品汇
@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;
}
}

2)中台

@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;
}
}

6 为了方便使用 我们使用aop加注解的方式来切换数据源
/**

* 在方法上使用,用于指定使用哪个数据源
*/

@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();

}

}












免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM