前文提到,最新换了框架,新项目用SpringMVC + Spring JdbcTemplate。搭框架时,发现了一个事务无法正常回滚的问题,记录如下:
首先展示问题:
Spring applicationContext.xml配置:
- <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配置:
- <!-- 自动扫描的包名 -->
- <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:
- @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增加:
- <context:component-scan base-package="com.will">
- <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
- </context:component-scan>
在 Spring mvc.dispatcher.xml增加:
- <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中配置:
- <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。
经过以上分析,故可以优化上述配置:
在 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。
相关推荐
8. **分布式事务**:Spring Cloud Data Flow和Spring Cloud Task可以帮助处理微服务环境中的复杂分布式事务问题。 9. **消息队列**:Spring Cloud Stream和RabbitMQ、Kafka等消息中间件结合,实现异步通信和解耦,...
3. 在session 中完成对数据的增删改查和事务提交等. 4. 在用完之后关闭session 。 5. 在java 对象和 数据库之间有做mapping 的配置文件,也通常是xml 文件。 mybatis实战教程(mybatis in action)之一:开发环境搭建 ...
它简化了配置,支持多种数据访问技术,包括JDBC、Hibernate等,并提供了事务管理、安全管理等企业级服务。 【Hibernate框架】Hibernate3.3是一个持久化框架,它简化了Java对象与数据库之间的交互。通过ORM(Object-...
【Spring】Spring框架是企业级应用开发的基石,面试可能围绕IoC(Inversion of Control)和AOP(Aspect-Oriented Programming)展开,包括Bean管理、依赖注入、事务处理、Spring MVC、Spring Boot、Spring Data JPA...
1. Spring Boot:Spring框架的简化版,为快速开发Java Web应用提供便利,内置了许多默认配置。 2. Django:Python的Web开发框架,强调简洁、实用和快速开发,提供完整的MVC结构。 3. Vue.js / React / Angular:...
11. SpringBoot/SpringCloud:SpringBoot简化了Spring应用的配置和部署,而SpringCloud则是云环境下微服务架构的一整套解决方案。 12. Hibernate:作为Java对象关系映射工具,Hibernate简化了数据持久化的复杂性,...
Spring负责整个系统的依赖注入与事务管理,SpringMVC处理前端请求和视图的跳转,而MyBatis则进行数据库的持久层操作。HTML5作为前端开发的主要技术,为用户提供了良好的交互体验和丰富的页面元素展示。 系统的用户...
- **Spring框架**:依赖注入(DI)、AOP、事务管理等核心概念。 - **MyBatis**:持久层框架,动态SQL和映射文件的使用。 - **Maven或Gradle**:构建工具的使用和配置。 9. **数据库** - **SQL语句**:增删改查...