文档章节

Spring事务总结---传播级别以及REQUIRED_NEW及NESTED的使用场景

sluggarddd
 sluggarddd
发布于 2016/03/28 18:52
字数 2138
阅读 475
收藏 0

超过了字数限制,强行被分割。。。。


三、Spring事务的传播性与隔离级别

    Spring它对JDBC的隔离级别作出了补充和扩展,其提供了7种事务传播行为。(通俗解释原址

    1、PROPAGATION_REQUIRED:默认事务类型,如果没有,就新建一个事务;如果有,就加入当前事务。适合绝大多数情况。

    2、PROPAGATION_REQUIRES_NEW:如果没有,就新建一个事务;如果有,就将当前事务挂起。

    3、PROPAGATION_NESTED:如果没有,就新建一个事务;如果有,在当前事务中嵌套其他事务。

    4、PROPAGATION_SUPPORTS:如果没有,就以非事务方式执行;如果有,就使用当前事务。

    5、PROPAGATION_NOT_SUPPORTED:如果没有,就非事务方式执行;如果有,就将当前事务挂起。即无论如何不支持事务。

    6、PROPAGATION_NEVER:如果没有,就非事务方式执行;如果有,就抛出异常。

    7、PROPAGATION_MANDATORY:如果没有,就抛出异常;如果有,就使用当前事务。

    第4、5、6、7种特性很好理解了,主要是前三种特性比较容易混淆或用错。

    那么PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_NESTED的区别在哪呢?

    什么有就创建一个什么嵌套挂起,很明显不如一些使用场景清晰,那就直接上例子。

    先定义一些实验性的方法。(例子代码:https://git.oschina.net/sluggarddd/spring-tx-demo.git)

@Service
public class IUserServiceImpl implements IUserService {

    @Resource
    IUserDAO userDAO;

    @Resource
    IUserService2 userService2;

    //不带事务的方法
    public void funNone() throws Exception {
        save(new UserEntity("zhw"));

    }


    //启动默认事务的方法
    @Transactional(propagation = Propagation.REQUIRED)
    public void funRequire() throws Exception {
        save(new UserEntity("wlj"));

    }

    //启动默认事务的方法
    @Transactional(propagation = Propagation.REQUIRED)
    public void funRequire2() throws Exception {
        save(new UserEntity("shifang"));

    }

    //启动默认事务的方法,抛出RuntimeException
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void funRequireException() throws Exception {
        save(new UserEntity("max"));

        throwExcp();

    }

    //启动嵌套事务的方法
    @Transactional(propagation = Propagation.NESTED)
    public void funNest() throws Exception {
        save(new UserEntity("yunxinghe"));

    }


    //启动嵌套事务的方法,但会抛出异常
    @Override
    @Transactional(propagation = Propagation.NESTED)
    public void funNestException() throws Exception {
        save(new UserEntity("edward"));
        throwExcp();
    }

    //REQUIRES_NEW事务的方法
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void funRequireNew() throws Exception {
        save(new UserEntity("kb"));

    }

    //REQUIRES_NEW事务的方法,但会抛出异常
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void funRequireNewException() throws Exception {
        save(new UserEntity("laura"));
        throwExcp();
    }


    //抛出异常
    private void throwExcp() throws Exception {
        throw new RuntimeException("boom");
    }

    //保存数据
    public int save(UserEntity userEntity) throws Exception {
        userDAO.save(userEntity);
        return userEntity.getId();
    }
}

    我们定义了两个镜像(就是一样的Serivce)分别是UserService,UserService2,是一样的,只是相互调用,不重复上代码。

    

@Override
@Transactional
public void fun1() throws Exception {

    //数据库操作
    funNone();
    //调用另一个service的方法
    userService2.funNest();

    //当调用另一个Service的method的时候,想要将他的事务加到现在这个事务中,很可能自然而然想到了嵌套
    //这么想就错了,Required的定义里已经说明了如果没有,就新建一个事务;如果有,就加入当前事务。
    //那么直接使用Required就满足需求
    //这样在方法中任何地方发生unchecked异常将触发整个方法的回滚
    //而Nested的使用场景下面再介绍

}

    NESTED事务使用场景

@Override
@Transactional
public void fun2() throws Exception {
    //嵌套事务的使用场景
    funNone();

    try {
        //当所调用的方法为NESTED事务,该事务的回滚可以不影响到调用者的事务
        //当然如果没有catch exception,异常冒泡而出,就将触发调用者事务的回滚
        userService2.funNestException();
    } catch (Exception e) {

        //do something
    }

    userService2.funRequire();


}

//执行结果:
//userService2.funNestException()被回滚
//其他插入成功

    外部的异常能触发所调用的NESTED事务回滚

@Override
@Transactional
public void fun3() throws Exception {


    //嵌套事务的使用场景
    funNone();
    try {
        //调用的事务为NESTED事务的方法
        userService2.funNest();
    } catch (Exception e) {

        //do something
    }

    userService2.funRequire();

    //此时在调用者处,触发一个unchecked异常
    throwExcp();


    //此时会发现包括调用的userService2.funNest()也被回滚了
    //也就是说,当调用的方法是NESTED事务,该方法抛出异常如果得到了处理(try-catch),那么该方法发生异常不会触发整个方法的回滚
    //而调用者出现unchecked异常,却能触发所调用的nested事务的回滚.
}

//执行结果
//全部被回滚

    REQUIRES_NEW的使用场景

@Override
@Transactional
public void fun4() throws Exception {
    //而REQUIRES_NEW,当被调用时,就相当于暂停(挂起)当前事务,先开一个新的事务去执行REQUIRES_NEW的方法,如果REQUIRES_NEW中的异常得到了处理
    //那么他将不影响调用者的事务,同时,调用者之后出现了异常,同样也不会影响之前调用的REQUIRES_NEW方法的事务.


    //不会回滚
    funNone();
    try {
        //当异常得到处理,外部不会触发回滚
        userService2.funRequireNewException();
    } catch (Exception e) {

    }
}

//执行结果
//funNone()正常持久化
// userService2.funRequireNewException()回滚
@Override
@Transactional
public void fun5() throws Exception {


    //数据库操作
    funNone();
    //调用RequireNew类型事务的方法,调用者的异常回滚不会影响到它
    userService2.funRequireNew();
    //数据库操作
    funNone();

    //抛出unchecked异常,触发回滚
    throwExcp();


}

//执行结果
//userService2.funRequireNew();正常持久化
//其他操作被回滚

如果调用的是REQUIRED类型,即使处理了被调用方法抛出的异常仍然会被回滚。

@Override
@Transactional
public void fun6() throws Exception {

    funNone();

    try {
        //当调用的是Required时,就算异常被处理了,整个方法也将会回滚
        userService2.funRequireException();
    } catch (Exception e) {
        System.out.println(e.getMessage());
    }
}

//执行结果
//被回滚

    总结:附上一段我觉得很好的总结(Jurgen Hoeller原话翻译)(翻译从这里拷的

    PROPAGATION_REQUIRES_NEW 启动一个新的, 不依赖于环境的 "内部" 事务. 这个事务将被完全 commited 或 rolled back 而不依赖于外部事务, 它拥有自己的隔离范围, 自己的锁, 等等. 当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行. 
     另一方面, PROPAGATION_NESTED 开始一个 "嵌套的" 事务,  它是已经存在事务的一个真正的子事务. 潜套事务开始执行时,  它将取得一个 savepoint. 如果这个嵌套事务失败, 我们将回滚到此 savepoint. 潜套事务是外部事务的一部分, 只有外部事务结束后它才会被提交. 
    由此可见, PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大区别在于, PROPAGATION_REQUIRES_NEW 完全是一个新的事务, 而 PROPAGATION_NESTED 则是外部事务的子事务, 如果外部事务 commit, 潜套事务也会被 commit, 这个规则同样适用于 roll back. 
    
    

四、Spring事务自我调用的坑

    当Spring的事务在同一个类时,它的自我调用时事务就完犊子了!(不知道这个的时候被坑出翔)

    当同一个类的方法之间事务发生自我调用,其事务的特性将失效。

    

@Override
@Transactional
public void fun7() throws Exception {

    funRequire();

    try {
        //本应回滚这个方法,但发生了异常并没有回滚
        funNestException();
    } catch (Exception e) {
        System.out.println(e.getMessage());

    }

    funRequire();

}

    例如上面Nested的特性就没了,其运行结果是三个发生insert的语句都成功插入到数据库了。

    原因我自己肯定没别人总结的好(连问题都说的不太清楚),就直接放链接了(原因点此),不想看的直接上解决方法。

    1、首先引入

<dependency>
   
<groupId>org.aspectj</groupId>
   
<artifactId>aspectjweaver</artifactId>
   
<version>${aspectj.version}</version>
</dependency>

    2、开启暴露AOP代理到ThreadLocal支持

<aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>

    3、在自我调用的时候这么写

fun8() Exception {
    ((IUserService) AopContext.()).funRequire();

    {
        ((IUserService) AopContext.()).funNestException();
    } (Exception e) {
        System..println(e.getMessage());

    }

    ((IUserService) AopContext.()).funRequire();
}

    这样,就能让配置在该方法上的事务发挥应有的特性啦。

    原因我也来总结一句,因为开启事务和事务回滚,实际这个过程是aop代理帮忙完成的,当调用一个方法时,他会先检查时候有事务,有则开启事务,当调用本类的方法是,他并没有将其视为proxy调用,而是方法的直接调用,所以也就没有检查该方法是否含有事务这个过程,那么他生命的事务也就不成立了。

    另外,除了这个解决方法,开涛大神那个博客还提供了其他解决方式,可以根据自己的需求选择。



    总结一下对自己理解还是蛮有帮助的,希望也能稍微帮到有需要的人。

    完。

    

© 著作权归作者所有

共有 人打赏支持
sluggarddd
粉丝 7
博文 14
码字总数 11821
作品 0
程序员
私信 提问
Spring 事务相关知识(转载)

Spring 事务的传播属性 所谓spring事务的传播属性,就是定义在存在多个事务同时存在的时候,spring应该如何处理这些事务的行为。这些属性在TransactionDefinition中定义,具体常量的解释见下...

晨猫
2017/11/09
0
0
Spring 深入理解事务原理

IT虾米的博客 一、事务的基本原理 Spring事务 的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。对于纯JDBC操作数据库,想要用到事务,可以按照以下步...

hgqxjj
2018/06/26
0
0
Spring系列之三 Spring 事务

Spring事务这个东西比之前的AOP和MVC都要复杂的多,而且Spring声明式的事务是非常常用的一个功能. 一.事务定义和并发问题 事务是指一段操作由几个部分组成,要么全完成,要不全部撤销.事务的特性...

Lubby
2015/03/28
0
0
Spring事务传播机制

概述 当我们调用一个基于Spring的Service接口方法(如UserService#addUser())时,它将运行于Spring管理的事务 环境中,Service接口方法可能会在内部调用其它的Service接口方法以共同完成一个...

rouway
2011/07/04
0
0
spring事务和jdbc事务

Spring事务的基本原理 Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。对于纯JDBC操作数据库,想要用到事务,可以按照以下步骤进行: 获取...

TonyStarkSir
2018/04/16
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Mac java多版本控制

vim ~/.bash_profile ORACLE_JAVA_6_HOME="/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home"ORACLE_JAVA_7_HOME="/Library/Java/JavaVirtualMachines/jdk1.7.0_80.jdk/Contents......

晨猫
11分钟前
1
0
[转载]Qlist的用法

QList是一种表示链表的模板类。 QList是Qt的一种泛型容器类。它以链表方式存储一组值,并能对这组数据进行快速索引,还提供了快速插入和删除等操作。 QList、QLinkedList和QVector提供的操作...

shzwork
11分钟前
0
0
谷歌的JavaScript编写风格中 13点值得我们注意的!

  对于那些还不熟悉JavaScript的编写风格的人,谷歌提供了编写JavaScript的编写风格指南,谷歌风格指南 其中列出了编写干净、可理解代码的最佳风格实践。   对于编写有效的JavaScript来说...

peakedness丶
26分钟前
3
0
Spring Boot2中配置HTTPS

1.生成证书 使用jdk,jre中的keytool.exe生成自签名的证书,需要配置JAVA_HOME和path环境变量,即jdk的环境变量。命令如下: keytool -genkey -alias tomcat -storetype PKCS12 -keyalg RSA ...

voole
33分钟前
4
0
Spring Cloud Alibaba 新版本发布:众多期待内容整合打包加入!

在Nacos 1.0.0 Release之后,Spring Cloud Alibaba也终于发布了最新的版本。该版本距离上一次发布,过去了整整4个月!下面就随我一起看看,这个大家期待已久的版本都有哪些内容值得我们关注。...

程序猿DD
36分钟前
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部