首先展現問題:html
Spring applicationContext.xml配置:java
-
- <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
- <property name="jndiName">
- <value>java:comp/env/jdbc/will</value>
- </property>
- </bean>
-
- <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
- <property name="dataSource" ref="dataSource" />
- </bean>
-
- <bean id="txManager"
- class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
- <property name="dataSource" ref="dataSource" />
- </bean>
-
- <tx:annotation-driven transaction-manager="txManager" />
Spring mvc.dispatcher.xml配置:mysql
- <context:component-scan base-package="com.will" >
- </context:component-scan>
-
- <mvc:annotation-driven />
-
- <mvc:default-servlet-handler/>
-
-
- <!-- 攔截器
- <mvc:interceptors>
- <bean class="com.will.mvc.MyInteceptor" />
- </mvc:interceptors>
- -->
-
- <bean id="viewResolver"
- class="org.springframework.web.servlet.view.UrlBasedViewResolver">
- <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
- <property name="prefix" value="/WEB-INF/pages/" />
- <property name="suffix" value=".jsp" />
- </bean>
而後在Service層模擬了一個事務回滾的method case:web
- @Transactional
- public boolean save(Person person)
- {
- for(int id: new int[]{2,3})
- {
- personDao.del(id);
- int j = 1/0;
- }
-
- return false;
- }
本覺得大功告成,在運行save方法時,因爲1/0 拋出 java.lang.ArithmeticException: / by zero RuntimeException,致使事務迴歸。However,no way! So crazy~
查了下,發現Spring MVC對於事務配置比較講究,須要額外的配置。解決辦法以下:
須要在 applicationContext.xml增長:spring
- <context:component-scan base-package="com.will">
- <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
- </context:component-scan>
在 Spring mvc.dispatcher.xml增長:sql
- <context:component-scan base-package="com.will" >
- <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
- <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service" />
- </context:component-scan>
因爲web.xml中配置:express
- <context-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>
- classpath:applicationContext.xml
- </param-value>
- </context-param>
- <listener>
- <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
- </listener>
- <servlet>
- <servlet-name>dispatcher</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- <init-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>classpath*:/mvc_dispatcher_servlet.xml</param-value>
- </init-param>
- <load-on-startup>1</load-on-startup>
- </servlet>
- <servlet-mapping>
- <servlet-name>dispatcher</servlet-name>
- <url-pattern>*.do</url-pattern>
- </servlet-mapping>
Spring容器優先加載由ServletContextListener(對應applicationContext.xml)產生的父容器,而SpringMVC(對應mvc_dispatcher_servlet.xml)產生的是子容器。子容器Controller進行掃描裝配時裝配的@Service註解的實例是沒有通過事務增強處理,即沒有事務處理能力的Service,而父容器進行初始化的Service是保證事務的加強處理能力的。若是不在子容器中將Service exclude掉,此時獲得的將是原樣的無事務處理能力的Service。mvc
( update 2014.05.27 今天看見一種說法:key word =雙親上下文。不使用ContextLoaderListener監聽器來加載spring的配置,改用DispatcherServlet來加載spring的配置,不要雙親上下文,只使用一個DispatcherServlet就不會出現上述問題。筆者這裏未測過這個辦法,由於我本身的業務須要一個extends ContextLoaderListener的selfListener,有興趣的朋友能夠本身測試下這個說法,並歡迎把測試的結果與我交流 :) )app
通過以上分析,故能夠優化上述配置:jsp
在 applicationContext.xml增長:
- <context:component-scan base-package="com.will">
- </context:component-scan>
在 Spring mvc.dispatcher.xml增長:
- <context:component-scan base-package="com.will" >
- <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service" />
- </context:component-scan>
通過如上配置,能夠發現事務控制部分的日誌以下:
- 2013-09-25 09:53:13,031 [http-8080-2] DEBUG [org.springframework.transaction.annotation.AnnotationTransactionAttributeSource] - Adding transactional method 'save' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
- 2013-09-25 09:53:13,037 [http-8080-2] DEBUG [org.springframework.beans.factory.support.DefaultListableBeanFactory] - Returning cached instance of singleton bean 'txManager'
- 2013-09-25 09:53:13,050 [http-8080-2] DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] - Creating new transaction with name [com.will.service.impl.PersonServiceImpl.save]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
- 2013-09-25 09:53:13,313 [http-8080-2] DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] - Acquired Connection [jdbc:mysql://localhost:3306/mvc?useUnicode=true&characterEncoding=UTF-8, UserName=root@localhost, MySQL-AB JDBC Driver] for JDBC transaction
- 2013-09-25 09:53:13,323 [http-8080-2] DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] - Switching JDBC Connection [jdbc:mysql://localhost:3306/mvc?useUnicode=true&characterEncoding=UTF-8, UserName=root@localhost, MySQL-AB JDBC Driver] to manual commit
- 2013-09-25 09:53:13,327 [http-8080-2] DEBUG [org.springframework.jdbc.core.JdbcTemplate] - Executing prepared SQL update
- 2013-09-25 09:53:13,328 [http-8080-2] DEBUG [org.springframework.jdbc.core.JdbcTemplate] - Executing prepared SQL statement [delete from person where id=?]
- 2013-09-25 09:53:13,348 [http-8080-2] DEBUG [org.springframework.jdbc.core.JdbcTemplate] - SQL update affected 1 rows
- 2013-09-25 09:53:13,363 [http-8080-2] DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] - Initiating transaction rollback
- 2013-09-25 09:53:13,364 [http-8080-2] DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] - Rolling back JDBC transaction on Connection [jdbc:mysql://localhost:3306/mvc?useUnicode=true&characterEncoding=UTF-8, UserName=root@localhost, MySQL-AB JDBC Driver]
- 2013-09-25 09:53:13,377 [http-8080-2] DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] - Releasing JDBC Connection [jdbc:mysql://localhost:3306/mvc?useUnicode=true&characterEncoding=UTF-8, UserName=root@localhost, MySQL-AB JDBC Driver] after transaction
- 2013-09-25 09:53:13,378 [http-8080-2] DEBUG [org.springframework.jdbc.datasource.DataSourceUtils] - Returning JDBC Connection to DataSource
在2013-09-25 09:53:13,363處進行了rollback。