版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lizhengwei1989/article/details/88081703
1.AbstractRoutingDataSource实现方式
这种实现方式网上可以找到很多介绍。大同小异,我这里在总结一下。
1.1首先是数据源的配置
<!-- 有几个数据源就配几个 -->
<!-- 数据源配置1 -->
<bean id="testDataSource1" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="driverClassName" value="${db.driver}" />
<property name="url" value="${unity.db.jdbc.url}" />
<property name="username" value="${db.login.name}"></property>
<property name="password" value="${db.login.password}" />
...等等配置信息...
</bean>
<!-- 数据源配置2 -->
<bean id="testDataSource2" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="driverClassName" value="${db.driver}" />
<property name="url" value="${pub.db.jdbc.url}" />
<property name="username" value="${db.login.name}"></property>
<property name="password" value="${db.login.password}" />
...等等配置信息...
</bean>
<!-- 这里指定实现的动态数据源,配置默认数据源,配置key和对应数据源的对应关系 -->
<bean id="dataSource" class="com.xxx.dao.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="master" value-ref="testDataSource1"/>
<entry key="dspinsight" value-ref="testDataSource2"/>
</map>
</property>
<property name="defaultTargetDataSource" ref="master"/>
</bean>
1.2相关类的代码
动态数据源
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSourceType();
}
}
存储当前线程的数据源的类
public class DataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
/**
* @Description: 设置数据源类型
* @param dataSourceType 数据库类型
* @return void
* @throws
*/
public static void setDataSourceType(String dataSourceType) {
contextHolder.set(dataSourceType);
}
/**
* @Description: 获取数据源类型
* @param
* @return String
* @throws
*/
public static String getDataSourceType() {
return contextHolder.get();
}
/**
* @Description: 清除数据源类型
* @param
* @return void
* @throws
*/
public static void clearDataSourceType() {
contextHolder.remove();
}
}
在方法上指定数据源的注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DataSource {
String value();
}
切面的实现方式1,这种实现方式有个问题,就是方法中多次切换数据源会有问题。
@Component
@Aspect
@Order(1)
public class DataSourceAspect{
private static Logger logger = LogFactory.log;
// 这里是你的切面
@Pointcut("execution(public * com.xxx.ad.service..*.*(..))")
private void methodExec() {
}
@Before("methodExec()")
private void before(JoinPoint point) throws NoSuchMethodException, SecurityException {
Object target = point.getTarget();
String method = point.getSignature().getName();
Class<? extends Object> classz = target.getClass();
Class<?>[] parameterTypes = ((MethodSignature) point.getSignature()).getMethod().getParameterTypes();
Method m = classz.getMethod(method, parameterTypes);
if (m != null && m.isAnnotationPresent(DataSource.class)) {
DataSource data = m.getAnnotation(DataSource.class);
String ds = data.value();
logger.info("exchange to data source :" + ds);
DynamicDataSourceHolder.setDataSource(ds);
}
}
@After("methodExec()")
private void after(JoinPoint point) {
DynamicDataSourceHolder.clearDataSource();
}
}
切面实现方式2,一个方法中可以切换多次数据源
@Aspect
@Order(1)
@Component
public class DynamicDataSourceAspect {
// 这里的
@Around("@annotation(targetDataSource)")
public Object Around(ProceedingJoinPoint pjp, DataSource targetDataSource) throws Throwable {
String lastDbType = DataSourceContextHolder.getDBType();
long tid = Thread.currentThread().getId();
String methodName = "";
Signature signature = pjp.getSignature();
if(signature != null){
methodName = signature.getName();
}
LogFactory.sailInfo("切换数据源,lastDbType:{},tid:{},methodName:{}", lastDbType,tid,methodName);
String dataSourceKey = targetDataSource.value();
DataSourceContextHolder.setDBType(dataSourceKey);
LogFactory.sailInfo("设置数据源为 {},tid:{},methodName:{}", dataSourceKey,tid,methodName);
Object ret = null;
try {
ret = pjp.proceed();
}finally {
LogFactory.sailInfo("执行方法完毕,当前数据源:{},tid:{},methodName:{}", DataSourceContextHolder.getDBType(),tid,methodName);
LogFactory.sailInfo("恢复上次数据源:{},tid:{},methodName:{}", lastDbType,tid,methodName);
DataSourceContextHolder.setDBType(lastDbType);
}
return ret;
}
}
1.3使用方式
public class TestService ....{
@DataSource("master")
public void test1(){
如果使用切面方式2实现,方法里面可以调用其他的数据库操作的服务
testservice2.test2(); // 比如这个方法是操作其他的数据库
}
}
2.使用中遇到的问题
2.1首先就是一个服务中存在多次切库的情况。按照切面1的实现方式,是存在问题的。如果使用around切面的话,可以解决这个问题。个人觉得事务可以加在Mapper上或者Dao上,不要加在Service。如果需要,在考虑加在Service。
2.2事务问题。在一个事务中,无法进行切库。原因的话,这里不解释了,网上很多介绍。解决办法的话,比较多的是使用了jta,我也没用过。大家搜索分布式事务吧。。。
2.3切库失败的问题。其切库失败的原因肯定不止一种,多数情况下都是由于事务造成的。参考2.2。我个人使用过程中发现过一个现象,就是程序运行一段时间之后,切库就莫名其妙的会失败。经过长时间的排查发现,是有同事的代码中手动开启了事务,但是事务会存在不提交的情况。。。还是和事务有关。
3.另一种实现方式参考
手动装配Mapper,参考我的另一篇博文
https://blog.csdn.net/lizhengwei1989/article/details/88081917