spring mybatis 多數據源切換 事務添加


1、背景

最近項目里需要添加事務回滾處理,采用了spring多數據源,繼承了AbstractRoutingDataSource來實現多數據源配置,之前其他人配置的事務不起作用(手動攤手),只能自己重新配置,記錄下踩過的坑。目前只能在同一個數據源中進行回滾,暫不支持一個service層里面實現多個數據源回滾。

由於涉及到數據源切換,利用自定義注解,然后通過切面動態切換數據源,如下所示

1.1自定義注解

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {
    String value();
}

1.2 Dao持久層進行數據源切換

 

2、實現方式

 由於需要在service層實現事務處理,而事務處理必須在切換數據源之后,目前是在Dao層切換數據源,所以事務會失效。如果全部切換到service層進行數據源切換,修改的地方很多(無語啊),自己只能重新一個切面,並在service層切換數據源,代碼及配置如下:

2.1 多數據源配置

<!--數據源-鏈接數據庫的基本信息,這里直接寫,不放到*.properties資源文件中 -->
    <context:property-placeholder location="classpath:application.properties"></context:property-placeholder>
	<bean id="dataSource1" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="${ora1_jdbc.driver}" />
		<property name="url" value="${ora1_jdbc.url}" />
		<property name="username" value="${ora1_jdbc.username}"/>
		<property name="password" value="${ora1_jdbc.password}"/>
	</bean>
	
	<bean id="dataSource2" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="${ora2_jdbc.driver}" />
		<property name="url" value="${ora2_jdbc.url}" />
		<property name="username" value="${ora2_jdbc.username}"/>
		<property name="password" value="${ora2_jdbc.password}" />
	</bean>
	
	<bean id="dataSource" class="com.essbase.util.dbSourcesConfig.DynamicDataSource">
		<property name="targetDataSources">
			<map key-type="java.lang.String">
				<!-- 指定lookupKey和與之對應的數據源 -->
				<entry key="dataSource1" value-ref="dataSource1"></entry>
				<entry key="dataSource2" value-ref="dataSource2"></entry>
			</map>
		</property>
		<!-- 指定默認的數據源 -->
		<property name="defaultTargetDataSource" ref="dataSource1"></property>
	</bean>

2.2 aop配置

 

 1 <!-- 掃描包Service實現類 -->
 2     <context:component-scan base-package="com.essbase.service"></context:component-scan>
 3     <bean id="dynamicDataSourceAop" class="com.essbase.util.dbSourcesConfig.DynamicDataSourceAop"/>
 4     <aop:config>
 5         <aop:pointcut expression="execution(* com.essbase.dao..*.*(..))||execution(* com.essbase.service..*.*(..))" id="cut1"/>
 6         <!--事務切面-->
 7         <aop:pointcut expression="execution(* com.essbase.service..*.*(..))" id="cut2"/>
 8         <aop:advisor advice-ref="dynamicDataSourceAop" pointcut-ref="cut1" order="1"/>
 9         <aop:advisor advice-ref="txAdvice" pointcut-ref="cut2" order="2" />
10     </aop:config>
11     <!--事務配置-->
12     <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
13         <property name="dataSource" ref="dataSource" />
14     </bean>
15     <tx:advice id="txAdvice" transaction-manager="transactionManager"></tx:advice>

 

DynamicDataSourceAop類是掃描dao層和service層進行數據源切換,這里有一個坑,在掃描service層時,一直進不去切面類,經過排查原來是在spring.xml中沒有掃描到service包(默認掃描本xml中的配置,寫到其他xml中會導致掃描不到),
添加<context:component-scan base-package="com.essbase.service"></context:component-scan>后正常。
 1 public class DynamicDataSourceAop implements MethodBeforeAdvice, AfterReturningAdvice {
 2 
 3     @Override
 4     public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
 5         if(method.isAnnotationPresent(DataSource.class)) {
 6             DynamicDataSource.clearDataSource();
 7             System.out.println("**********************************數據源已移除*************************************");
 8         }
 9     }
10 
11     @Override
12     public void before(Method method, Object[] objects, Object o) throws Throwable {
13         if(method.isAnnotationPresent(DataSource.class)){
14             DataSource dataSource = method.getAnnotation(DataSource.class);
15             DynamicDataSource.setDataSource(dataSource.value());
16             System.out.println("*******************************數據源切換至:"+DynamicDataSource.getDatasource()+"**************************************");
17         }
18 
19     }
20 }
 1 public class DynamicDataSource extends AbstractRoutingDataSource{
 2     /**
 3      * 數據源標識,保存在線程變量中,避免多線程操作數據源時互相干擾
 4      */
 5     private static final ThreadLocal<String> key = new ThreadLocal<String>();
 6 
 7     @Override
 8     protected Object determineCurrentLookupKey() {
 9          // 從自定義的位置獲取數據源標識
10         return key.get();
11     }
12     /**
13      * 設置數據源
14      * @param dataSource 數據源名稱
15      */
16     public static void setDataSource(String dataSource){
17         key.set(dataSource);
18     }
19     /**
20      * 獲取數據源
21      * @return
22      */
23     public static String getDatasource() {
24         return key.get();
25     }
26 
27     /**
28      * 清除數據源
29      */
30     public static void clearDataSource(){
31         key.remove();
32     }
33 }

在這里要注意

<aop:advisor advice-ref="txAdvice" pointcut-ref="cut2" order="2" /> 事務切面的order值要大於數據源切面,order值越小權重越高

2.3 service層
1 //service層實現類
2 @Transactional()
3     public void test() {
4 
5 }
//service接口類
@DataSource("dataSource2")
  List<TbmFrdayEntity> test();

  通過以上配置就可以實現多數據源事務回滾(目前只能在同一個數據源中進行回滾,暫不支持一個service層里面實現多個數據源回滾)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM