前言:
公司最近一个项目用到Spring和Mybatis,发现用起来挺方便,比以前的那个struts+hibernate舒服多了。废话少说,直接摆问题,碰到的问题是,mybatis不在事务中运行,后台日志报 “Closing no transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@19006c9]”错误。无论是加了@Transactional注解和是没加都报这个信息。一个方法中插入多条数据,某次插入失败也不回滚。
问题描述:
环境: Spring 3.1.0 + Mybatis 3.1.0 + mybatis-spring 1.0.0 RC3 + DB2 9.5 + Tomcat 6.0.35
web工程名称: isap
配置文件:applicationContext.xml + isap-servlet.xml
先看配置信息:
applicationContext.xml
01 |
<? xml version = "1.0" encoding = "UTF-8" ?> |
02 |
< beans xmlns = "http://www.springframework.org/schema/beans" |
03 |
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" |
04 |
xmlns:tx = "http://www.springframework.org/schema/tx" |
05 |
xmlns:context = "http://www.springframework.org/schema/context" |
07 |
http://www.springframework.org/schema/beans |
08 |
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd |
09 |
http://www.springframework.org/schema/tx |
10 |
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd |
11 |
http://www.springframework.org/schema/context |
12 |
http://www.springframework.org/schema/context/spring-context-3.0.xsd"> |
16 |
< bean id = "dataSource" class = "org.springframework.jndi.JndiObjectFactoryBean" > |
17 |
< property name = "jndiName" > |
18 |
< value >java:comp/env/jndi_isap</ value > |
22 |
< bean id = "sqlSessionFactory" class = "org.mybatis.spring.SqlSessionFactoryBean" > |
23 |
< property name = "configLocation" value = "classpath:config/mybatis-config.xml" ></ property > |
24 |
< property name = "dataSource" ref = "dataSource" /> |
25 |
< property name = "mapperLocations" > |
27 |
< value >classpath:com/cosbulk/isap/*/dao/mapper/*Mapper.xml</ value > |
32 |
< bean id = "transactionManager" class = "org.springframework.jdbc.datasource.DataSourceTransactionManager" > |
33 |
< property name = "dataSource" ref = "dataSource" /> |
34 |
< qualifier value = "isap" /> |
38 |
< tx:annotation-driven transaction-manager = "transactionManager" /> |
40 |
< bean class = "org.mybatis.spring.mapper.MapperScannerConfigurer" > |
41 |
< property name = "sqlSessionFactory" ref = "sqlSessionFactory" /> |
42 |
< property name = "basePackage" value = "com.cosbulk.isap" ></ property > |
48 |
< bean id = "smisDataSource" class = "org.springframework.jndi.JndiObjectFactoryBean" > |
49 |
< property name = "jndiName" > |
50 |
< value >java:comp/env/jndi_smis</ value > |
53 |
< bean id = "smisSqlSessionFactory" class = "org.mybatis.spring.SqlSessionFactoryBean" > |
54 |
< property name = "configLocation" value = "classpath:config/mybatis-config.xml" ></ property > |
55 |
< property name = "dataSource" ref = "smisDataSource" /> |
56 |
< property name = "mapperLocations" > |
58 |
< value >classpath:com/cosbulk/smis/dao/mapper/*Mapper.xml</ value > |
62 |
< bean id = "smisTransactionManager" class = "org.springframework.jdbc.datasource.DataSourceTransactionManager" > |
63 |
< property name = "dataSource" ref = "smisDataSource" /> |
64 |
< qualifier value = "smis" /> |
67 |
< tx:annotation-driven transaction-manager = "smisTransactionManager" /> |
69 |
< bean class = "org.mybatis.spring.mapper.MapperScannerConfigurer" > |
70 |
< property name = "sqlSessionFactory" ref = "smisSqlSessionFactory" /> |
71 |
< property name = "basePackage" value = "com.cosbulk.smis" ></ property > |
在applicationContext.xml文件中,主要配置了数据源和dao接口以及mapper文件相关信息,其他的bean信息在isap-servlet.xml文件中定义。
配置文件: isap-servlet.xml
01 |
<? xml version = "1.0" encoding = "UTF-8" ?> |
02 |
< beans xmlns = "http://www.springframework.org/schema/beans" |
03 |
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xmlns:p = "http://www.springframework.org/schema/p" |
04 |
xmlns:context = "http://www.springframework.org/schema/context" |
05 |
xmlns:mvc = "http://www.springframework.org/schema/mvc" |
07 |
http://www.springframework.org/schema/beans |
08 |
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd |
09 |
http://www.springframework.org/schema/context |
10 |
http://www.springframework.org/schema/context/spring-context-3.0.xsd |
11 |
http://www.springframework.org/schema/mvc |
12 |
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd" |
13 |
default-autowire = "byName" > |
15 |
< span style = "color:#ff0000;" >< strong > |
16 |
< context:component-scan base-package = "com.cosbulk.isap" > |
17 |
< context:exclude-filter type = "regex" expression = "com.cosbulk.isap.*.*.model" /> |
18 |
< context:exclude-filter type = "regex" expression = "com.cosbulk.isap.*.*.dao.*" /> |
19 |
</ context:component-scan > |
21 |
< context:component-scan base-package = "com.cosbulk.smis" > |
22 |
< context:exclude-filter type = "regex" expression = "com.cosbulk.smis.*.*.model" /> |
23 |
< context:exclude-filter type = "regex" expression = "com.cosbulk.smis.*.*.dao.*" /> |
24 |
</ context:component-scan ></ strong ></ span > |
25 |
< mvc:annotation-driven /> |
29 |
< bean class = "com.cosbulk.isap.common.interceptor.CheckLoginInterceptor" /> |
33 |
< bean id = "multipartResolver" class = "org.springframework.web.multipart.commons.CommonsMultipartResolver" > |
34 |
< property name = "defaultEncoding" value = "UTF-8" /> |
39 |
< bean id = "exceptionResolver" class = "org.springframework.web.servlet.handler.SimpleMappingExceptionResolver" > |
40 |
< property name = "defaultErrorView" > |
41 |
< value >errorPages/error</ value > |
43 |
< property name = "defaultStatusCode" > |
46 |
< property name = "warnLogCategory" > |
47 |
< value >org.springframework.web.servlet.handler.SimpleMappingExceptionResolver</ value > |
49 |
< property name = "exceptionMappings" > |
51 |
< prop key = "org.springframework.web.multipart.MaxUploadSizeExceededException" >errorPages/maxUploadExceeded</ prop > |
57 |
< bean class = "org.springframework.web.servlet.view.InternalResourceViewResolver" > |
58 |
< property name = "viewClass" value = "org.springframework.web.servlet.view.JstlView" /> |
59 |
< property name = "prefix" value = "/view/" /> |
60 |
< property name = "suffix" value = ".jsp" /> |
在这个配置文件里进行了所有的注解(@Controller、@Service等)扫描和mvc相关配置。
事务场景:
Service层通过注解@Transactional注入事务
03 |
@Transactional ( "isap" ) |
04 |
public class UserService{ |
06 |
private UserDao userDao; |
13 |
return userDao.getId(); |
21 |
public void updateUser(User user,Map<String,Object> param) { |
23 |
userDao.updateUser(user); |
25 |
userDao.deleteUserRole(param); |
29 |
insertUserRole(param); |
测试过几次updateUser的调用,事务就是不起作用的,后台log信息如下:
1 |
DEBUG 2011 - 09 - 04 16 : 19 : 46 , 672 org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl: JDBC Connection [org.apache.commons.dbcp.PoolableConnection @67aece ] will not be managed by Spring |
3 |
DEBUG 2011 - 09 - 04 16 : 19 : 46 , 672 org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl: SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession @19006c9 ] was not registered for synchronization because synchronization is not active |
5 |
DEBUG 2011 - 09 - 04 16 : 19 : 46 , 687 org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl: Committing JDBC Connection [org.apache.commons.dbcp.PoolableConnection @67aece ] |
7 |
DEBUG 2011 - 09 - 04 16 : 19 : 46 , 687 org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl: Closing no transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession @19006c9 ] |
9 |
DEBUG 2011 - 09 - 04 16 : 19 : 46 , 687 org.springframework.jdbc.datasource.DataSourceUtils: Returning JDBC Connection to DataSource |
解决方案:
1、配置文件的问题吧? (见http://www.iteye.com/topic/1123069)
03 |
< context:component-scan base-package = "com.kimho" > |
04 |
< context:exclude-filter type = "annotation" expression = "org.springframework.stereotype.Controller" /> |
05 |
</ context:component-scan > |
07 |
2、servlet-context.xml: |
09 |
< context:component-scan base-package = "com.kimho" > |
10 |
< context:exclude-filter type = "annotation" expression = "org.springframework.stereotype.Service" /> |
11 |
</ context:component-scan > |
我做了相应的修改,把isap-servlet里的bean扫描拆成了两部分,分别放入applicationContext.xml和isap-servlet.xml文件中。
applicationContext.xml中加入:
2 |
< context:component-scan base-package = "com.cosbulk.isap" > |
3 |
< context:include-filter type = "annotation" expression = "org.springframework.stereotype.Service" /> |
4 |
</ context:component-scan > |
6 |
< context:component-scan base-package = "com.cosbulk.smis" > |
7 |
< context:include-filter type = "annotation" expression = "org.springframework.stereotype.Service" /> |
8 |
</ context:component-scan > |
isap-servlet.xml文件中,扫描bean部分换成:
2 |
< context:component-scan base-package = "com.cosbulk.isap" > |
3 |
< context:exclude-filter type = "annotation" expression = "org.springframework.stereotype.Service" /> |
4 |
</ context:component-scan > |
6 |
< context:component-scan base-package = "com.cosbulk.smis" > |
7 |
< context:exclude-filter type = "annotation" expression = "org.springframework.stereotype.Service" /> |
8 |
</ context:component-scan > |
重启服务器测试,发现效果和之前一样,事务还是没起作用。开始以为是和包扫描的位置有关,于是把扫描的位置放到文件末尾,发现还是没有起作用。最后偶然一个想法clean了下tomcat,然后重启,居然事务有效了。
总结: 修改完配置文件后,clean下工程,重启加载新的配置文件。OK,问题解决。
2、如果按照你的步骤设置为ID为null的话,那么就需要捕获mybatis抛出的异常,然后在catch语句中抛出一个Exception,这个时候Spring容器的事务管理就会起作用,会回滚事务。
3、如果用mysql数据库,数据库表你如果是自动建表,那么就需要把建表的Engine设置为InnoDB格式,自动建表的格式为:MyISAM,这中格式的是不支持事务管理的。
总结下: Spring在使用ContextLoadListener加载applicationContext.xml或其他名称的xml文件时,能进行数据源和相关事务注解的检查,启动事务特性。若在isap-servlet.xml文件中加载是,仅作为普通bean定义加载。所以一个良好的习惯就是,分层配置相关的bean。applicationContext.xml中配置数据库相关的bean(dao、service等), isap-servlet中配置mvc相关的bean(controller等)。
转自:http://blog.csdn.net/walkerjong/article/details/7839002
2012-04-26 11:17
1350人阅读
收藏
举报
用spring aop配置了事务,但是不起作用,困扰了好久。
事务配置如下:
<!-- 配置事务管理器,使用jdbc事务 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 申明式事务 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="*" read-only="true"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="allManagerMethod" expression="execution(* com.xiu.xclk.web.service.*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="allManagerMethod" />
</aop:config>
查网上,有的说是因为spring mvc自动扫描的时候,把包括@service的注解一下都扫描今天了,所以在开始的时候要先排除掉对@service注解的扫描,见帖子:http://www.linuxso.com/architecture/14904.html
配置好之后,事务还是不起作用,很奇怪,之后查找,一个帖子给了很大的启发:http://hi.baidu.com/jakoes/blog/item/7256efc2c7ec30190ff477dc.html,说是Spring的事务实现采用基于AOP的拦截器来实现,如果没有在事务配置的时候注明回滚的checked exception,我的代码中抛的是java.lang.Exception,改成抛RuntimeException,事务起作用了。帖子中说两种办法可以解决:
1,在事务属性后面加上需要回滚的checked exception。比如<prop key="save*">PROPAGATION_REQUIRED,-XXXXException</prop>(注意那个"-",对应的是"+")
2,
不改配置文件,将需要事务回滚的异常继承自
unchecked exception
类,也就是
RuntimeException
。
那么对于第二种方法,我的是起作用了,但是第一种方法我的是声明了对Exception回滚了啊。后来一想,是不是要写全java类名啊,于是改成:
<tx:method name="save*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>,这样就起作用了。看来还是自己大意了,spring的配置文件要写全类名的。