首先看了配置文件关于事务管理都没有问题,然后在service的实现业务类方法加上@Transactional注解,发现 事务没有生效,正常情况下应该会有数据库回滚操作。
配置文件:
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app 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"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>Archetype Created Web Application</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:conf/spring-mybatis.xml;
</param-value>
</context-param>
<context-param>
<param-name>webAppRootKey</param-name>
<param-value>wmsservice.root</param-value>
</context-param>
<filter>
<filter-name>SpringEncodingFilter</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>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>SpringEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>classpath:conf/log4j.properties</param-value>
</context-param>
<context-param>
<param-name>log4jRefreshInterval</param-name>
<param-value>6000</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<filter>
<filter-name>loginFilter</filter-name>
<filter-class>com.XXX.filter.LoginFilterController</filter-class>
</filter>
<filter-mapping>
<filter-name>loginFilter</filter-name>
<url-pattern>*.shtml</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:conf/spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<servlet>
<description>This is the description of my J2EE component</description>
<display-name>This is the display name of my J2EE component</display-name>
<servlet-name>LoginFilter</servlet-name>
<servlet-class>filter.LoginFilter</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>*.shtml</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>ImgServlet</servlet-name>
<servlet-class>com.XXX.servlet.ImgServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ImgServlet</servlet-name>
<url-pattern>/CreateImage</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>login.shtml</welcome-file>
</welcome-file-list>
<!-- 404 页面不存在错误 -->
<error-page>
<error-code>404</error-code>
<location>/errorPage404.jsp</location>
</error-page>
<!-- 500 服务器内部错误 -->
<error-page>
<error-code>500</error-code>
<location>/errorPage500.jsp</location>
</error-page>
<context-param>
<param-name>webAppRootKey</param-name>
<param-value> app.root </param-value>
</context-param>
</web-app>
spring-mybatis.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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<!-- 自动扫描com.XXX下的service.impl -->
<context:component-scan base-package="com.XXX" >
</context:component-scan>
<!-- 引入配置文件 -->
<!-- <bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:conf/jdbc.properties,conf/redis.properties" />
</bean> -->
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:conf/jdbc.properties</value>
</list>
</property>
</bean>
<!-- <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean" scope="singleton">
<property name="jndiName" value="jdbc/MySQLDB"></property>
<property name="resourceRef" value="true"></property>
</bean> -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
<!-- 初始化连接大小 -->
<property name="initialSize" value="${initialSize}"></property>
<!-- 连接池最大数量 -->
<property name="maxActive" value="${maxActive}"></property>
<!-- 连接池最大空闲 -->
<property name="maxIdle" value="${maxIdle}"></property>
<!-- 连接池最小空闲 -->
<property name="minIdle" value="${minIdle}"></property>
<!-- 获取连接最大等待时间 -->
<property name="maxWait" value="${maxWait}"></property>
</bean>
<!-- spring和MyBatis完美整合,不需要mybatis的配置映射文件 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- 自动扫描mapping.xml文件 -->
<property name="configLocation" value="classpath:conf/mybatis-config.xml"></property>
<property name="mapperLocations" value="classpath*:com/XXX/service/**/mapper/*.xml"></property>
</bean>
<!-- DAO接口所在包名,Spring会自动查找其下的类 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.XXX.service.**.dao" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
<!-- (事务管理)transaction manager, use JtaTransactionManager for global tx -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 事务控制 -->
<tx:annotation-driven transaction-manager="transactionManager" />
<!-- 拦截器方式配置事物 -->
<tx:advice id="transactionAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED" read-only="false"
rollback-for="java.lang.RuntimeException" />
<tx:method name="add*" propagation="REQUIRED" read-only="false"
rollback-for="java.lang.RuntimeException" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="delete*" propagation="REQUIRED" />
<tx:method name="get*" propagation="SUPPORTS" read-only="true" />
<tx:method name="find*" propagation="SUPPORTS" read-only="true" />
<tx:method name="select*" propagation="SUPPORTS" read-only="true" />
</tx:attributes>
</tx:advice>
<!-- Spring aop事务管理 -->
<aop:config>
<aop:pointcut id="transactionPointcut"
expression="execution(public * com.XXX.service.*.*.*.*(..))" />
<aop:advisor pointcut-ref="transactionPointcut" advice-ref="transactionAdvice" />
</aop:config>
<!-- 日志管理 -->
<aop:config>
<aop:aspect id="aspect" ref="aspectBean">
<aop:pointcut id="logService"
expression="execution(* com.XXX.service.*.*.*.*(..))" />
<aop:before pointcut-ref="logService" method="doBefore"/>
<aop:after pointcut-ref="logService" method="doAfter"/>
<aop:around pointcut-ref="logService" method="doAround"/>
<aop:after-throwing pointcut-ref="logService" method="doAfterThrow"/>
</aop:aspect>
</aop:config>
<bean id="aspectBean" class="com.XXX.common.Aspect" />
</beans>
spring-mvc.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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<!-- 扫描controller(controller层注入) -->
<!-- 扫描service(service层注入) -->
<context:component-scan base-package="com.XXX"/>
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="com.XXX.common.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>application/json;charset=UTF-8</value>
<value>text/html;charset=UTF-8</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<bean id="springContextHolder" class="com.XXX.common.SpringContextHolder" lazy-init="false" />
<!-- 避免IE在ajax请求时,返回json出现下载 -->
<bean id="jacksonMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/html;charset=UTF-8</value>
</list>
</property>
</bean>
<!-- 对模型视图添加前后缀 -->
<!-- 视图解析器 -->
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 使用JSP页面进行输出 -->
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<!-- 这个配置是配置JSP页面的位置 -->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!-- 指定了表示层的后缀 -->
<property name="suffix" value=".jsp"></property>
</bean>
<mvc:resources location="/js/" mapping="/js/**" />
<mvc:resources location="/css/" mapping="/css/**" />
<mvc:resources location="/img/" mapping="/img/**" />
<mvc:resources location="/fonts/" mapping="/fonts/**" />
<mvc:resources location="/hht/" mapping="/hht/**" />
<!-- <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="10485760" />
</bean> -->
</beans>
查到原因为:
Spring容器优先加载由ServletContextListener(对应spring-mybatis.xml)产生的父容器,而SpringMVC(对应spring-mvc.xml)产生的是子容器。子容器Controller进行扫描装配时装配的@Service注解的实例是没有经过事务加强处理,即没有事务处理能力的Service,而父容器进行初始化的Service是保证事务的增强处理能力的。如果不在子容器中将Service exclude掉,此时得到的将是原样的无事务处理能力的Service。
修改 spring-mvc.xml
<context:component-scan base-package="com.XXX">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service" />
</context:component-scan>
测试事务有效:
@Override
@Transactional
public void testTransactional() {
// TODO Auto-generated method stub
//正确的sql语句
ckFormDao.testTransactional1();
//错误的sql语句
ckFormDao.testTransactional2();
}
控制台日志可以看到回滚 rollback
2016-08-04 09:37:27 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7166a119]
2016-08-04 09:37:27 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7166a119]
2016-08-04 09:37:27 [org.springframework.jdbc.datasource.DataSourceTransactionManager]-[DEBUG] Initiating transaction rollback
2016-08-04 09:37:27 [org.springframework.jdbc.datasource.DataSourceTransactionManager]-[DEBUG] Rolling back JDBC transaction on Connection