在做拆库的项目时,我们针对DAO层做动态数据源(Dynamic DataSource)切换,指定package来使用哪个数据源,这种动态切换数据源的方式,我们使用的是Spring Aop来实现的:
项目用的Hibernate、SpringJDBC、Mybatis组合来实现ORM,改造的过程中曾经遇到过不能切换数据源的情况,启动服务报错,具体报错忘记记录了,但是报错的根源是针对Spring Aop的动态代理的使用出现问题,先来看两种代码的介绍:
(1)JDK动态代理只能对实现了接口的类生成代理,而不能针对类和接口;
(2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,因为是继承,所以类的方法最好不要声明成final;
通过介绍我们也已经很清楚使用方法了,更多的Spring Aop的动态代理知识自行解决,网上资源很多。我们在改造的过程中,出现的问题就是我们对JDK动态代理使用错误,我们针对接口使用了,这个错误印象比较深刻,等等类似的情况不介绍了。
下面同时也记录下动态数据源的切换,后续项目开发可以直接只用了,代码不介绍了,自行解决,网上也是资源很多的:
/**
* @Description: 多数据源配置
*/
public class DataBaseContextHolder {
/**
* 注意:数据源标识保存在线程变量中,避免多线程操作数据源时互相干扰
*/
private static final ThreadLocal<String> THREAD_DATA_SOURCE = new ThreadLocal<String>();
public static String getDataSource() {
return THREAD_DATA_SOURCE.get();
}
public static void setDataSource(String dateSource) {
THREAD_DATA_SOURCE.set(dateSource);
}
public static void clearDataSource() {
THREAD_DATA_SOURCE.remove();
}
}
/**
* @Description: 动态数据源配置
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final Logger logger = Logger.getLogger(DynamicDataSource.class);
@Override
protected Object determineCurrentLookupKey() {
String dataSource = DataBaseContextHolder.getDataSource();
logger.info("\n\n=======The Current DateBase:" + dataSource + "=======\n\n");
return dataSource;
}
}
/**
* @Description: 动态数据源拦截器
*/
public class DataSourceInterceptor {
private static final Logger logger = Logger.getLogger(DataSourceInterceptor.class);
public Object nsDataSource(ProceedingJoinPoint pjp) throws Throwable {
DataBaseContextHolder.setDataSource("nsDataSource");
String className = pjp.getTarget().getClass().getSimpleName();
String methodName = pjp.getSignature().getName();
logger.debug(className + "." + methodName + "[" + DataBaseContextHolder.getDataSource() + "](......)");
return pjp.proceed();
}
public Object collateDataSource(ProceedingJoinPoint pjp) throws Throwable {
DataBaseContextHolder.setDataSource("collateDataSource");
String className = pjp.getTarget().getClass().getSimpleName();
String methodName = pjp.getSignature().getName();
logger.debug(className + "." + methodName + "[" + DataBaseContextHolder.getDataSource() + "](......)");
return pjp.proceed();
}
public Object naDataSource(ProceedingJoinPoint pjp) throws Throwable {
DataBaseContextHolder.setDataSource("naDataSource");
String className = pjp.getTarget().getClass().getSimpleName();
String methodName = pjp.getSignature().getName();
logger.debug(className + "." + methodName + "[" + DataBaseContextHolder.getDataSource() + "](......)");
return pjp.proceed();
}
public Object ntDataSource(ProceedingJoinPoint pjp) throws Throwable {
DataBaseContextHolder.setDataSource("ntDataSource");
String className = pjp.getTarget().getClass().getSimpleName();
String methodName = pjp.getSignature().getName();
logger.debug(className + "." + methodName + "[" + DataBaseContextHolder.getDataSource() + "](......)");
return pjp.proceed();
}
public void clearDBType() {
logger.debug("clearDataSource...");
DataBaseContextHolder.clearDataSource();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:util="http://www.springframework.org/schema/util"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd"
default-autowire="byName">
<!-- 交易库 -->
<bean id="nsDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.ns.driverClassName}" />
<property name="url" value="${jdbc.ns.url}" />
<property name="username" value="${jdbc.ns.username}" />
<property name="password" value="${jdbc.ns.password}" />
<property name="testOnBorrow" value="true" />
<property name="maxActive" value="5" />
<property name="initialSize" value="2" />
<property name="maxIdle" value="2" />
<property name="maxWait" value="30000" />
<property name="validationQuery" value="select 1 from dual" />
<!-- <property name="connectionProperties" value="v$session.machine=${oracle.clientMachine};v$session.program=${project.name}"/>
--> </bean>
<!-- 对账库 -->
<bean id="collateDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.collate.driverClassName}" />
<property name="url" value="${jdbc.collate.url}" />
<property name="username" value="${jdbc.collate.username}" />
<property name="password" value="${jdbc.collate.password}" />
<property name="testOnBorrow" value="true" />
<property name="maxActive" value="5" />
<property name="initialSize" value="2" />
<property name="maxIdle" value="2" />
<property name="maxWait" value="30000" />
<property name="validationQuery" value="select 1 from dual" />
<!-- <property name="connectionProperties" value="v$session.machine=${oracle.clientMachine};v$session.program=${project.name}"/>
--> </bean>
<!-- 账户库 -->
<bean id="naDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.na.driverClassName}" />
<property name="url" value="${jdbc.na.url}" />
<property name="username" value="${jdbc.na.username}" />
<property name="password" value="${jdbc.na.password}" />
<property name="testOnBorrow" value="true" />
<property name="maxActive" value="5" />
<property name="initialSize" value="2" />
<property name="maxIdle" value="2" />
<property name="maxWait" value="30000" />
<property name="validationQuery" value="select 1 from dual" />
<!-- <property name="connectionProperties" value="v$session.machine=${oracle.clientMachine};v$session.program=${project.name}"/>
--> </bean>
<!-- 转账库 -->
<bean id="ntDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.nt.driverClassName}" />
<property name="url" value="${jdbc.nt.url}" />
<property name="username" value="${jdbc.nt.username}" />
<property name="password" value="${jdbc.nt.password}" />
<property name="testOnBorrow" value="true" />
<property name="maxActive" value="5" />
<property name="initialSize" value="2" />
<property name="maxIdle" value="2" />
<property name="maxWait" value="30000" />
<property name="validationQuery" value="select 1 from dual" />
<!-- <property name="connectionProperties" value="v$session.machine=${oracle.clientMachine};v$session.program=${project.name}"/> -->
</bean>
<bean id="dynamicDataSource" class="com.test.base.dynamicdb.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="nsDataSource" value-ref="nsDataSource"></entry>
<entry key="collateDataSource" value-ref="collateDataSource"></entry>
<entry key="naDataSource" value-ref="naDataSource"></entry>
<entry key="ntDataSource" value-ref="ntDataSource"></entry>
</map>
</property>
<property name="defaultTargetDataSource" ref="nsDataSource">
</property>
</bean>
<!-- 数据库拦截器 -->
<bean id="dataSourceInterceptor" class="com.test.base.dynamicdb.DataSourceInterceptor" />
<aop:config>
<aop:aspect id="dataSourceAspect" ref="dataSourceInterceptor">
<aop:pointcut id="nsDataSourceAop" expression="execution(* com.test.dao.nsdao..*.*(..))" />
<aop:pointcut id="collateDataSourceAop" expression="execution(* com.test.dao.collatedao..*.*(..))" />
<aop:pointcut id="naDataSourceAop" expression="execution(* com.test.dao.nadao..*.*(..))" />
<aop:pointcut id="ntDataSourceAop" expression="execution(* com.test.dao.ntdao..*.*(..))" />
<aop:around method="nsDataSource" pointcut-ref="nsDataSourceAop"/>
<aop:around method="collateDataSource" pointcut-ref="collateDataSourceAop"/>
<aop:around method="naDataSource" pointcut-ref="naDataSourceAop"/>
<aop:around method="ntDataSource" pointcut-ref="ntDataSourceAop"/>
<aop:after-returning method="clearDBType" pointcut-ref="nsDataSourceAop"/>
<aop:after-returning method="clearDBType" pointcut-ref="collateDataSourceAop"/>
<aop:after-returning method="clearDBType" pointcut-ref="naDataSourceAop"/>
<aop:after-returning method="clearDBType" pointcut-ref="ntDataSourceAop"/>
</aop:aspect>
</aop:config>
<!-- 注解方式配置 -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource">
<ref bean="dynamicDataSource" />
</property>
<property name="namingStrategy">
<bean class="org.hibernate.cfg.ImprovedNamingStrategy" />
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.show_sql">${hibernate.showSql}</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.generateDdl">${hibernate.generateDdl}</prop>
<prop key="hibernate.cache.use_second_level_cache">true</prop>
<prop key="hibernate.cache.use_query_cache">true</prop>
<prop key="hibernate.autoReconnect">true</prop>
<prop key="hibernate.cache.region.factory_class">net.sf.ehcache.hibernate.EhCacheRegionFactory</prop>
<prop key="hibernate.query.substitutions">true 1, false 0</prop>
<prop key="hibernate.query.factory_class">org.hibernate.hql.classic.ClassicQueryTranslatorFactory</prop>
</props>
</property>
<!-- 自动扫描指定位置下的实体文件进行映射 -->
<property name="packagesToScan">
<list>
<value>com.test</value>
<!-- <value>com.test.model</value> -->
<!-- <value>com.test.verification.model</value> -->
</list>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref bean="sessionFactory" />
</property>
<property name="dataSource">
<ref bean="dynamicDataSource" />
</property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" p:dataSource-ref="dynamicDataSource" />
<bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
<constructor-arg ref="dynamicDataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />
</beans>