springMVC+Mybatis(使用AbstractRoutingDataSource实现多数据源切换时)事务管理未生效的解决办法

业务场景:

  A、B两个单位,系统部署同一套代码;

  A、B两系统能相互访问;

  要求将数据从A系统同步到B系统,再将反馈信息回发给A;

实际开发情况:

  因为系统比较小,最开始设计架构的时候没有考虑到消息互通的方式,也没有设计分布式部署,所以采用AbstractRoutingDataSource灵活切换数据源的方式直接在业务代码中实现数据交互。

  

项目代码:

applicationContext-common.xml:

<?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:context="http://www.springframework.org/schema/context"
	xmlns:util="http://www.springframework.org/schema/util" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.1.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd">
    <context:component-scan base-package="com.fms;com.job;com.jmda;">
        <context:exclude-filter type="regex" expression=".controller.*"/>
    </context:component-scan>
	<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">

		<property name="templateLoaderPaths">
			<list>
				<value>/WEB-INF/pages/</value>
				<value>/WEB-INF/template/</value>
				<value>classpath:/jmda-ftl/</value>
			</list>
		</property>
		<property name="freemarkerSettings">
			<props>
				<prop key="template_update_delay">0</prop>
				<prop key="default_encoding">UTF-8</prop>
				<prop key="number_format">0.##########</prop>
				<prop key="datetime_format">yyyy-MM-dd HH:mm:ss</prop>
				<prop key="classic_compatible">true</prop>
				<prop key="template_exception_handler">ignore</prop>
			</props>
		</property>
	</bean>

	<!-- 配置c3p0数据源 -->
	<bean id="dataSource1" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
		<property name="jdbcUrl">
		<value><![CDATA[jdbc:mysql://192.168.5.186:3306/fms-ybj?useUnicode=yes&characterEncoding=UTF8]]></value>
		</property>
		<property name="driverClass" value="com.mysql.jdbc.Driver" />
		<property name="user" value="root" />
		<property name="password" value="root" />
		<property name="maxPoolSize" value="200" />
		<property name="minPoolSize" value="1" />
		<property name="initialPoolSize" value="1" />
		<property name="maxIdleTime" value="30" />
		<property name="acquireIncrement" value="5" />
		<property name="maxStatements" value="0" />
		<property name="idleConnectionTestPeriod" value="60" />
		<property name="acquireRetryAttempts" value="30" />
		<property name="breakAfterAcquireFailure" value="true" />
		<property name="testConnectionOnCheckout" value="false" />
	</bean>
	<bean id="dataSource2" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
		<property name="jdbcUrl">
		 <value><![CDATA[jdbc:mysql://192.168.5.186:3306/fms-zhs?useUnicode=yes&characterEncoding=UTF8]]></value>
		</property>
		<property name="driverClass" value="com.mysql.jdbc.Driver" />
		<property name="user" value="root" />
		<property name="password" value="root" />
		<property name="maxPoolSize" value="200" />
		<property name="minPoolSize" value="1" />
		<property name="initialPoolSize" value="1" />
		<property name="maxIdleTime" value="30" />
		<property name="acquireIncrement" value="5" />
		<property name="maxStatements" value="0" />
		<property name="idleConnectionTestPeriod" value="60" />
		<property name="acquireRetryAttempts" value="30" />
		<property name="breakAfterAcquireFailure" value="true" />
		<property name="testConnectionOnCheckout" value="false" />
	</bean>

      <bean id="multipleDataSource" class="com.fms.common.datasource.MultipleDataSource">
          <property name="defaultTargetDataSource" ref="dataSource1"/>
          <property name="targetDataSources">
              <map>
                  <entry key="dataSource1" value-ref="dataSource1"/>
                  <entry key="dataSource2" value-ref="dataSource2"/>
              </map>
          </property>
      </bean>
	<bean id="msqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="multipleDataSource"/>
		<property name="configLocation" value="classpath:mybatis.xml" />
		<property name="mapperLocations">
			<list>
				<value>classpath*:/com/fms/**/dao/*Mapper.xml</value>
				<value>classpath*:/com/fms/**/dao/*DAO.xml</value>
			</list>
		</property>
	</bean>
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<property name="basePackage" value="com.fms.**.dao" />
		<property name="sqlSessionFactory" ref="msqlSessionFactory" />
	</bean>

	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="multipleDataSource" />
	</bean>
	<tx:annotation-driven transaction-manager="transactionManager"/>
       <!-- 文件上传配置 -->
       <bean id="multipartResolver"
    	  class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    	  <property name="maxUploadSize" value="10240000"/>
    	  <property name="maxInMemorySize" value="10240000" />
	</bean>

</beans>

  

springmvc-servlet.xml:

<?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:aop="http://www.springframework.org/schema/aop"
	xmlns:cache="http://www.springframework.org/schema/cache"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee"
	xmlns:jms="http://www.springframework.org/schema/jms" xmlns:lang="http://www.springframework.org/schema/lang"
	xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:oxm="http://www.springframework.org/schema/oxm"
	xmlns:p="http://www.springframework.org/schema/p" xmlns:task="http://www.springframework.org/schema/task"
	xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mongo="http://www.springframework.org/schema/data/mongo"
	xmlns:util="http://www.springframework.org/schema/util"
	xmlns:ehcache="http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring"
	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-4.1.xsd
        http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-4.1.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
        http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.1.xsd
        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.1.xsd
        http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-4.1.xsd
        http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-4.1.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
        http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-4.1.xsd
        http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.1.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.1.xsd">

    <context:component-scan base-package="com.fms;com.job;com.jmda;" />      
	<mvc:annotation-driven>
		<mvc:message-converters>
			<bean class="org.springframework.http.converter.StringHttpMessageConverter">
				<property name="supportedMediaTypes">
					<list>
					    <value>text/plain;charset=UTF-8</value>
						<value>text/html;charset=UTF-8</value>
					</list>
				</property>
			</bean>
		</mvc:message-converters>
	</mvc:annotation-driven>
		<bean id="viewResolver"
        class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
        <property name="cache" value="true" />
        <property name="suffix" value=".ftl" />
        <property name="contentType" value="text/html;charset=UTF-8"></property>
        <property name="requestContextAttribute" value="request" />
        <property name="exposeSpringMacroHelpers" value="true" />
        <property name="exposeRequestAttributes" value="true" />
        <property name="exposeSessionAttributes" value="true" />
    </bean>
	<mvc:resources mapping="/static/**" location="/static/" />
	<mvc:resources mapping="/jmda-static/**" location="/jmda-static/" />
	<mvc:resources mapping="/assets/**" location="/assets/" />

	<mvc:interceptors>
        <bean class="com.fms.common.listener.SecurityInterceptor"/>
    </mvc:interceptors>
</beans>

  web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app metadata-complete="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>
		classpath*:/spring/applicationContext*.xml
		</param-value>
	</context-param>
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	<listener>
		<listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
	</listener>
	<filter>
		<filter-name>CharacterEncodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
	</filter>

	<filter-mapping>
		<filter-name>CharacterEncodingFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

	<servlet>
		<servlet-name>spring4mvc</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath*:/spring/springmvc-servlet.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>spring4mvc</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
	<context-param>
		<param-name>log4jConfigLocation</param-name>
		<param-value>/WEB-INF/classes/log4j.properties</param-value>
	</context-param>
	<listener>
		<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
	</listener>
    <listener>
        <listener-class>com.fms.common.listener.CommListener</listener-class>
    </listener>
	<!-- 400错误 -->
	<error-page>
		<error-code>400</error-code>
		<location>/error</location>
	</error-page>
	<!-- 404 页面不存在错误 -->
	<error-page>
		<error-code>404</error-code>
		<location>/error</location>
	</error-page>
	<!-- 403 服务器拒绝请求 -->
	<error-page>
		<error-code>403</error-code>
		<location>/error</location>
	</error-page>
	<!-- 500 服务器内部错误 -->
	<error-page>
		<error-code>500</error-code>
		<location>/error</location>
	</error-page>
	<!-- 503 服务不可用 -->
	<error-page>
		<error-code>503</error-code>
		<location>/error</location>
	</error-page>
	<!-- java.lang.Exception -->
	<error-page>
		<exception-type>java.lang.Exception</exception-type>
		<location>/error</location>
	</error-page>
	<!-- java.lang.NullPointerException -->
	<error-page>
		<exception-type>java.lang.NullPointerException</exception-type>
		<location>/error</location>
	</error-page>
	<error-page>
		<exception-type>javax.servlet.ServletException</exception-type>
		<location>/error</location>
	</error-page>
	<welcome-file-list>
	<welcome-file></welcome-file>
	</welcome-file-list>
</web-app>

  

MultipleDataSource:

package com.fms.common.datasource;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class MultipleDataSource extends AbstractRoutingDataSource {

    private static final ThreadLocal<String> dataSourceKey = new InheritableThreadLocal<String>();

    public static void setDataSourceKey(String dataSource) {
        dataSourceKey.remove();
    }

    public static void setDataSource(String dataSource){
    	dataSourceKey.remove();
    	dataSourceKey.set(dataSource);
    }
    public static String getKey(){
    	return dataSourceKey.get();
    }
    @Override
    protected Object determineCurrentLookupKey() {
        return dataSourceKey.get();
    }

}

  

简略的业务代码

        @Transactional(rollbackFor = { Exception.class })
	@Override
	public void test() {
		//默认数据源为datasource1

		//do some SQL operate

		//切换数据源
		MultipleDataSource.setDataSource("datasource2");

		//do other SQL operate

		//…… ……
	}

  

  代码进行到上面的阶段,各项业务在正常情况下能够顺利执行,但是在发生异常时出现了事务无法回滚的情况,于是我在网上找各种方法尝试修改;

  一开始我以为是AbstractRoutingDataSource多数据源的问题,一直从这方面找答案,找了很多例子修改后都仍然无法正常开启事务管理,偶然一次看到一个帖子讲spring父子容器配置,看完后照着改完,然后重新启动项目,结果真的成功了。

  具体修改如下:

applicationContext-common.xml中:

<!--
 <context:component-scan base-package="com.fms;com.job;com.jmda;">
        <context:exclude-filter type="regex" expression=".controller.*"/>
    </context:component-scan>

-->

改为:

    <context:component-scan base-package="com.fms;com.job;com.jmda;">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
    </context:component-scan>

springmvc-servlet.xml中:

<!--
<context:component-scan base-package="com.fms;com.job;com.jmda;" />
-->

改为:

    <context:component-scan base-package="com.fms;com.job;com.jmda;" >
   		 <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service" />
	</context:component-scan>

原因:

Spring容器优先加载由ServletContextListener(对应applicationContext.xml)产生的父容器,而SpringMVC(对应mvc_dispatcher_servlet.xml)产生的是子容器。子容器Controller进行扫描装配时装配的@Service注解的实例是没有经过事务加强处理,即没有事务处理能力的Service,而父容器进行初始化的Service是保证事务的增强处理能力的。如果不在子容器中将Service exclude掉,此时得到的将是原样的无事务处理能力的Service,因为在多上下文的情况下,如果同一个bean被定义两次,后面一个优先。

---------注:此段内容摘自:http://blog.csdn.net/will_awoke/article/details/12002705

时间: 2024-10-09 20:19:39

springMVC+Mybatis(使用AbstractRoutingDataSource实现多数据源切换时)事务管理未生效的解决办法的相关文章

[Android Pro] Fragment中使用SurfaceView切换时闪一下黑屏的解决办法

方法一.在Activity的onCreate中添加如下代码 getWindow().setFormat(PixelFormat.TRANSLUCENT); reference to :  http://www.jb51.net/article/72719.htm

Magento 切换成中文后没有数据信息解决办法

一款基于Google Closure compiler压缩Js文件插件. 快捷键: Ctrl+Alt+M            当前文件内压缩Js代码(不推荐) Ctrl+Alt+Shift+M   压缩Js并生成压缩文件 *.min.js 安装成功重启,如果报错,在配置里改一个参数,"compiler": "uglify_js", { // the closure compiler adds new lines every 500 characters // fo

spring+springMVC+Mybatis架构下采用AbstractRoutingDataSource、atomikos、JTA实现多数据源灵活切换以及分布式事务管理

背景: 1.系统采用SSM架构.需要在10多个MYSQL数据库之间进行切换并对数据进行操作,上篇博文<springMVC+Mybatis(使用AbstractRoutingDataSource实现多数据源切换时)事务管理未生效的解决办法> 2.第一步先通过AbstractRoutingDataSource实现了多数据源的灵活切换,但是后来发现事务不起作用: 3.发现问题是因为重复扫描service包导致第二次扫入容器的BEAN没有纳入事务管理,因此在springMVC的配置文件中排除了对Ser

spring AbstractRoutingDataSource实现动态数据源切换

使用Spring 提供的 AbstractRoutingDataSource 实现 创建 AbstractRoutingDataSource 实现类,负责保存所有数据源与切换数据源策略:public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.ge

Spring(AbstractRoutingDataSource)实现动态数据源切换

一.前言 近期一项目A需实现数据同步到另一项目B数据库中,在不改变B项目的情况下,只好选择项目A中切换数据源,直接把数据写入项目B的数据库中.这种需求,在数据同步与定时任务中经常需要. 那么问题来了,该如何解决多数据源问题呢?不光是要配置多个数据源,还得能灵活动态的切换数据源.以spring+hibernate框架项目为例(引用:http://blog.csdn.net/wangpeng047/article/details/8866239博客的图片): 单个数据源绑定给sessionFacto

Spring(AbstractRoutingDataSource)实现动态数据源切换--转载

原始出处:http://linhongyu.blog.51cto.com/6373370/1615895 一.前言 近期一项目A需实现数据同步到另一项目B数据库中,在不改变B项目的情况下,只好选择项目A中切换数据源,直接把数据写入项目B的数据库中.这种需求,在数据同步与定时任务中经常需要. 那么问题来了,该如何解决多数据源问题呢?不光是要配置多个数据源,还得能灵活动态的切换数据源.以spring+hibernate框架项目为例(引用:http://blog.csdn.net/wangpeng04

MyEclipse中SVN切换新地址遇到的问题及解决办法

1.问题描述 由于SVN代码迁移到新的代码服务器上了,所以在MyEclipse中也需要切换地址,在项目结点的快捷菜单中点击了"Team | 切换...",出现以下界面 输入新地址(https://182.0.0.2/svn/SLReserviorManage)后,出现以下提示: switch https://182.0.0.2/svn/SLReserviorManage/trunk/web/sl D:/java/workspace/sl -r HEAD --force Invalid

企业分布式微服务云SpringCloud SpringBoot mybatis (十七)Spring Boot中的事务管理

快速入门 在Spring Boot中,当我们使用了spring-boot-starter-jdbc或spring-boot-starter-data-jpa依赖的时候,框架会自动默认分别注入DataSourceTransactionManager或JpaTransactionManager.所以我们不需要任何额外配置就可以用@Transactional注解进行事务的使用. 我们以之前实现的<用spring-data-jpa访问数据库>的示例Chapter3-2-2作为基础工程进行事务的使用常识

mybatis、Spring整合(eclipse)以及事务管理

1.项目目录 2.jar包 dbcp:连接池 pool:连接池 logging:日志 log4j:日志 mybatis-spring:用于SqlSession等相关操作 spring相关包 mybatis 3.web.xml配置 可以删除本配置文件,本次测试用的是JUnit,不涉及网络访问,所有该配置文件并不需要 <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http