SpringMVC 声明式事务配置以及问题解决

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/angelbill3/article/details/84448674
百度定义:
声明式事务:声明式事务(declarative transaction management)是Spring提供的对程序事务管理的方式之一。
Spring的声明式事务顾名思义就是采用声明的方式来处理事务。这里所说的声明,就是指在配置文件中申明。用在Spring配置文件中声明式的处理事务来代替代码式的处理事务。这样的好处是,事务管理不侵入开发的组件,具体来说,业务逻辑对象就不会意识到正在事务管理之中,事实上也应该如此,因为事务管理是属于系统层面的服务,而不是业务逻辑的一部分,如果想要改变事务管理策划的话,也只需要在定义文件中重新配置即可;在不需要事务管理的时候,只要在设定文件上修改一下,即可移去事务管理服务,无需改变代码重新编译,这样维护起来极其方便。
Spring使用AOP来完成声明式的事务管理,因而声明式事务是以方法为单位。

首先来看看正确的完整配置:
Spring核心配置文件 applicationContext.xml

<!-- 声明事务 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>

<aop:config>
<aop:pointcut id="serviceOperation" expression="execution(* com.service.impl.*.*(..))" />
<aop:advisor pointcut-ref="serviceOperation" advice-ref="txAdvice" />
</aop:config>

<!--启动spring注解功能-->
<tx:annotation-driven transaction-manager="transactionManager" />

<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="create*" propagation="REQUIRED"/>
<tx:method name="do*" propagation="REQUIRED"/>
<tx:method name="del*" propagation="REQUIRED"/>
<tx:method name="remove*" propagation="REQUIRED"/>
<tx:method name="get*" read-only="true" />
<tx:method name="query*" read-only="true" />
<tx:method name="find*" read-only="true" />
<tx:method name="*"/>
</tx:attributes>
</tx:advice>

<context:component-scan base-package="com.test">
<!--将Controller的注解排除掉 -->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>


SpringMVC配置文件 spring-servlet.xml

<!-- 注解模式 -->
<context:component-scan base-package="com.test" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>


Service层示例:(底层使用了Mybatis,在此篇不作详细介绍,具体请看下面相关URL。
TestServiceImpl.java

@Service
public class TestServiceImpl implements TestService{
@Resource
private UserMapper userMapper;

public void addUser() throws Exception{
User useri = new User();
useri.setUsername("222");
useri.setPassword("222");
this.userMapper.insert(useri);
throw new RuntimeException();//主动抛错,为了测试有没新增。若成功,则说明事务无效,若不能新增,则说明已回滚。
}
}

Controller测试类省略。经测试能回滚。

-------------------------------------
问题一:起始因为没有配置applicationContext.xml最后一个配置,导致事务失效,网上查了下原因,[b][color=red]如果带上事务,那么用annotation方式的事务注解和bean配置,事务会失效,要将service bean配置到xml文件中才行。
因 为spring的context是父子容器,所以会产生冲突,Controller会先进行扫描装配,而此时的Service还没有进行事务的增强处理, 得到的将是原样的Service(没有经过事务加强处理,故而没有事务处理能力) ,最后才是applicationContext.xml中的扫描配置进行事务处理。[/color][/b]

即:

mvc 的只扫描controller组件 注意使用 use-default-filters="false"
<context:component-scan base-package="com.fengzhiyin" use-default-filters="false" >
<context:include-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
</context:component-scan>

主体的扫描除controller外的所有组件
<context:component-scan base-package="com.fengzhiyin" >
<context:exclude-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
</context:component-scan>


补充:对于use-default-filters="false"的解释:
如果不设置use-default-filters="false",则Spring会扫描并优先注册默认的bean(当然包括标记为@Service的bean),这样,标记为@Transactional的service由于transaction manager尚未注册而未能生效,导致事务管理失效。
原理是:标记为@Transactional的service会wrap为经过transactional proxied(不管是CGLIB based或是JDK based)的bean,而不再是纯的service;

问题二:若要测试事务,有很多种方式,(只要测出会回滚就行),[b][color=red]但千万别在Service里抛出一个Exception,来做为测试事务的方式。[/color][/b]
规则如下:
[b][color=red]默认遇到throw new RuntimeException("...");会回滚
需要捕获的throw new Exception("...");不会回滚[/color][/b]

原因是抛出的Exception,在数据库层默认是“超时”错误。
当然这个可以通过类似@Transactional(rollbackFor=Exception.class)的来重新配置。

-----------------------------
参考资料:
[url]http://www.cnblogs.com/rushoooooo/archive/2011/08/28/2155960.html[/url]
展开阅读全文

没有更多推荐了,返回首页