spring事务回滚的多种⽅式
start 看下下⾯的说明,会对理解本⼈贴出的代码有帮助。
1.代码中事务控制的3种⽅式
编程式事务:就是直接在代码⾥⼿动开启事务,⼿动提交,⼿动回滚。优点就是可以灵活控制,缺点就是太⿇烦了,太多重复的代码了。声明式事务:就是使⽤SpringAop配置事务,这种⽅式⼤⼤的简化了编码。需要注意的是切⼊点表达式⼀定要写正确。
注解事务:直接在Service层的⽅法上⾯加上@Transactional注解,个⼈⽐较喜欢⽤这种⽅式。
2.事务不回滚的原因
在⼯作中,看过别⼈写的代码出现了事务不回滚的现象。当然,事务不回滚的都是采⽤的声明式事务或者是注解事务;编程式事务都是⾃⼰写代码⼿动回滚的,因此是不会出现不回滚的现象。
再说下声明式事务和注解事务回滚的原理:当被切⾯切中或者是加了注解的⽅法中抛出了RuntimeException异常时,Spring会进⾏事务回滚。默认情况下是捕获到⽅法的RuntimeException异常,也就是说抛出只要属于运⾏时的异常(即RuntimeException及其⼦类)都能回滚;但当抛出⼀个不属于运⾏时异常时,事务是不会回滚的。
下⾯说说我经常见到的3种事务不回滚的产⽣原因:
(1)声明式事务配置切⼊点表达式写错了,没切中Service中的⽅法
(2)Service⽅法中,把异常给try catch了,但catch⾥⾯只是打印了异常信息,没有⼿动抛出RuntimeException异常
(3)Service⽅法中,抛出的异常不属于运⾏时异常(如IO异常),因为Spring默认情况下是捕获到运⾏时异常就回滚
3.如何保证事务回滚
正常情况下,按照正确的编码是不会出现事务回滚失败的。下⾯说⼏点保证事务能回滚的⽅法
(1)如果采⽤编程式事务,⼀定要确保切⼊点表达式书写正确
(2)如果Service层会抛出不属于运⾏时异常也要能回滚,那么可以将Spring默认的回滚时的异常修改为Exception,这样就可以保证碰到什么异常都可以回滚。具体的设置⽅式也说下:
① 声明式事务,在配置⾥⾯添加⼀个rollback-for,代码如下
<tx:method name="update*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
② 注解事务,直接在注解上⾯指定,代码如下
@Transactional(rollbackFor=Exception.class)
(3)只有⾮只读事务才能回滚的,只读事务是不会回滚的
(4)如果在Service层⽤了try catch,在catch⾥⾯再抛出⼀个 RuntimeException异常,这样出了异常才会回滚
(5)如果你不喜欢(4)的⽅式,你还可以直接在catch后⾯写⼀句回滚代码
(TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); )来实现回滚,这样的话,就可以在抛异常后也能return 返回值;⽐较适合需要拿到Service层的返回值的场景。具体的⽤法可以参见考下⾯的伪代码
/** TransactionAspectSupport⼿动回滚事务:*/
@Transactional(rollbackFor = { Exception.class })
public boolean test() {
try {
doDbSomeThing();
} catch (Exception e) {
e.printStackTrace();
//就是这⼀句了, 加上之后抛了异常就能回滚(有这句代码就不需要再⼿动抛出运⾏时异常了)
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return false;
}
return true;
}
转 end
代码:
⽅式⼀、声明式事务
1.在配置数据源的 l中加⼊(加的时候注意头部是否有引⼊相应的xmlns命名空间):
<!-- ⽅式⼀声明式事务 begin -->
<!-- (事务管理)transaction manager, use JtaTransactionManager for global tx -->
<!-- 配置JDBC数据源的局部数据管理器,使⽤DataSourceTransactionManager类 --> <bean id="txManagerCommon"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" />
</bean>
<!-- 公共数据源注解拦截界⾯ -->
<tx:advice id="txAdviceCommon" transaction-manager="txManagerCommon">
<tx:attributes>
<tx:method name="*"
rollback-for="java.lang.Exception" />
</tx:attributes>
</tx:advice>
<!-- 公共数据源 AOP设置 -->
<aop:config proxy-target-class="true">
<aop:advisor order="1"
pointcut="execution(* com.user.service.UserService.*(..))" //aop 切⼊点
advice-ref="txAdviceCommon" /><!-- 切⼊点 -->
</aop:config>
<!-- 事务 end -->
配置完这个就可以对UserService中⽅法进⾏管理。
/*事务回滚新增两个⽤户信息*/
@RequestMapping("/transaction") //url
public @ResponseBody
String transaction( Model model){
String flag="0";
try {
spring aop应用场景ansaction();
} catch (Exception e) {
flag="0";
}
return flag;
}
3.service代码
public String transaction() {
String flag="0";
String aString=null;
try {
User user=new User();user1.setName("krystal222");user1.setPsd("222");
insertUser(user);
//异常回滚 beginaString.equals("aa");//异常回滚end
User user1=new User();user1.setName("krystal222");user1.setPsd("222");
insertUser(user1);
flag="1";
}
catch (Exception e) {
flag="0";
1//throw new NullPointerException("失败");//抛出⼀个 RuntimeException异常,这样出了异常才会回滚
2TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); //⼿动回滚
⼆选⼀都可以实现(注意try catch 只是捕获异常,spring 事务默认只
有发⽣runtimeexception并且抛出这个异常时候才会回滚,2为⼿动回滚,算个例外吧)
}
return flag;
}
⽅式⼆、注解事务
(中的@Transactional 放在类级别 和 ⽅法级别 上⼀样效果)
@Service
@Transactional
public class UserService {
@Transactional(rollbackFor = Exception.class, readOnly = false) 此处注释上⾯戒了@Transactional public String transaction() {
1.在配置数据源的 l中加⼊:
<!-- ⽅式⼀注解事务 begin -->
<!-- (事务管理)transaction manager, use JtaTransactionManager for global tx -->
<!-- 配置JDBC数据源的局部数据管理器,使⽤DataSourceTransactionManager类 -->
<bean id="txManagerCommon"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 公共数据源使⽤注解定义事务 -->
<tx:annotation-driven transaction-manager="txManagerCommon" />
<!-- 事务 end -->
/
*事务回滚新增两个⽤户信息*/
@RequestMapping("/transaction") //url
public @ResponseBody
String transaction( Model model){
String flag="0";
try {
ansaction();
} catch (Exception e) {
flag="0";
}
return flag;
}
3.service
@Transactional(rollbackFor = Exception.class, readOnly = false)
public String transaction() {
String flag="0";
String aString=null;
try {
User user=new User();user.setName("krystal111");user.setPsd("111");
insertUser(user);
//异常回滚 beginaString.equals("aa");//异常回滚end
User user1=new User();user1.setName("krystal222");user1.setPsd("222");
insertUser(user1);
flag="1";
} catch (Exception e) {
flag="0";
throw new NullPointerException("shibai");
//TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
return flag;
}
⽅式⼀⽅式⼆也可以共存,都在l中配置
说到这⾥,已经有了对 <tx:annotation-driven/> 的简单理解,那我们是否就可以在程序中所有被spring管理的类上都可以使⽤
@Transactional注解了呢,在Service上可以使⽤@Transactional 注解这个是肯定的了,那总有些⼈也想弄明⽩能否在Controller 使⽤?答案显然是“不⼀定”的(与时间配置有关),下⾯做下解释:
在 spring-framework-reference.pdf ⽂档上有这样⼀段话:
<tx:annotation-driven/> only looks for @Transactional on beans in the same application context it is defined in. This means that, if you put <tx:annotation-driven/> in a WebApplicationContext for a DispatcherServlet, it only checks for
@Transactional beans in your controllers, and not your services.
意思就是:<tx:annoation-driven/>只会查和它在相同的应⽤上下⽂件中定义的bean上⾯的@Transactional注解,如果你把它放在Dispatcher的应⽤上下⽂中,它只检查控制器(Controller)上的@Transactional注解,⽽不是你services上的@Transactional注解。
所以,可以确定的是我们是可以在Controller上使⽤事务注解的,但是我们不推荐这样做(本⼈也从来没有这样做过),这⾥只是为了说明spring对<tx:annotation-driven/>的使⽤。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论