最近在配置spring架構時需要使用多資料源,其中遇到一些問題,特此記錄
無事務資料源切換
見 http://blog.csdn.net/shadowsick/article/details/8878448
主要說一下帶事務資料源切換的配置
首先在spring-database.xml中配置兩個資料源(預設資料源),和應用的切面
<!-- backend資料源 -->
<bean id="dataSourceBackend" class="org.springframework.jdbc.datasource.DriverManagerDataSource" >
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="${backend.url}"></property>
<property name="username" value="${backend.username}"></property>
<property name="password" value="${backend.password}"></property>
</bean>
<!-- pi資料源 -->
<bean id="dataSourcePIDB" class="org.springframework.jdbc.datasource.DriverManagerDataSource" >
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="${pi.url}"></property>
<property name="username" value="${pi.username}"></property>
<property name="password" value="${pi.password}"></property>
</bean>
<!-- 多資料源配置 -->
<bean id="dynamicDataSource" class="com.test.base.dao.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry value-ref="dataSourceBackend" key="dataSourceBackend"></entry>
<entry value-ref="dataSourcePIDB" key="dataSourcePIDB"></entry>
</map>
</property> <!--預設資料源-->
<property name="defaultTargetDataSource" ref="dataSourceBackend">
</property>
</bean>
<aop:config><!--切面-->
<aop:aspect id="dataSourceAspect" ref="dataSourceInterceptor">
<aop:pointcut id="daoBackend" expression="execution(* com.test.backend.service.ReportService.*(..))" />
<aop:before pointcut-ref="daoBackend" method="setdataSourceBackend" />
<aop:pointcut id="daopidb" expression="execution(* com.test.backend.service.MacIpService.*(..))" />
<aop:before pointcut-ref="daopidb" method="setdataSourcePIDB" /><!--切面before調用的方法-->
<aop:after pointcut-ref="daopidb" method="cleardataSource" /><!--切面after調用的方法-->
</aop:aspect>
</aop:config>
多資料源route類
public class DynamicDataSource extends AbstractRoutingDataSource{
@Override
protected Object determineCurrentLookupKey() {
return DatabaseContextHolder.getCustomerType();
}
}
資料源内容配置類
public class DatabaseContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
public static void setCustomerType(String customerType) {
contextHolder.set(customerType);
}
public static String getCustomerType() {
return contextHolder.get();
}
public static void clearCustomerType() {
contextHolder.remove();
}
}
@Component
public class DataSourceInterceptor {
public void setdataSourceBackend(JoinPoint jp) {
DatabaseContextHolder.setCustomerType("dataSourceBackend");
}
public void setdataSourcePIDB(JoinPoint jp) {
DatabaseContextHolder.setCustomerType("dataSourcePIDB");
}
public void cleardataSource(JoinPoint jp){
DatabaseContextHolder.clearCustomerType();
}
}
上邊xml中配置的 dataSourceInterceptor 會找到class DataSourceInterceptor,并執行set**方法和clear**方法,并設定CustomerType(資料源)給contextHolder,以便于
在determineCurrentLookupKey方法中可以獲得到改變後的資料源.
然後是事務的配置
在spring-application.xml中
加上如下配置
</pre></p><p><pre name="code" class="html"> <import resource="spring-database.xml"/>
<!-- 配置SessionFactory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dynamicDataSource" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.autoReconnect">true</prop>
<prop key="hibernate.connection.autocommit">true</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
<property name="packagesToScan" value="com.test.backend.entity" />
</bean>
<!-- 配置一個事務管理器 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<!-- 事務管理 start-->
<tx:annotation-driven transaction-manager="transactionManager" />
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
<tx:method name="update*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
<tx:method name="del*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
<tx:method name="find*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
<tx:method name="save*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
</tx:attributes>
</tx:advice>
<aop:config>
<!-- 實施事務 -->
<aop:pointcut id="txPointcut" expression="execution(* com.test.backend.service.*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" />
</aop:config>
<!-- 事務管理 end-->
在web.xml中加上了spring管理hibernate的session
<filter>
<filter-name>openSessionInView</filter-name>
<filter-class>org.springframework.orm.hibernate4.support.OpenSessionInViewFilter</filter-class>
<init-param>
<param-name>sessionFactoryBeanName</param-name>
<param-value>sessionFactory</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>openSessionInView</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
注意:加上事務時候,我這的切面切的是service層,當切換資料源時,一定要把所有的方法加到你要實施事務的attributes中,或者寫成*(任何方法都實施事務)也可以.否則不能切換資料源,詳情可見http://blog.csdn.net/wangpeng047/article/details/43450189的部落格
這裡一句話帶過,事務和資料源是綁定的,當你在service層加上事務,你想在dao層切換資料源是不可以的,由于在進入該層之前事務已經通過攔截器開啟,是以在該層切換資料源也是不行的.
以上是我本地配置的帶事務管理的資料源切換執行個體,如有問題感謝大家提出指正互相學習.