Spring事务的传播行为分析

前言

最近项目有涉及到Spring事务,因此工做之余,想认真了解学习下Spring事务,查阅了若干资料,作了一个demo(PS:参考了大牛的)。html

现分享总结以下:mysql

一、Spring 事务的简介

理解事务以前,spring

先讲一个你平常生活中最常干的事:取钱。 
好比你去ATM机取1000块钱,大致有两个步骤:首先输入密码金额,银行卡扣掉1000元钱;而后ATM出1000元钱。这两个步骤必须是要么都执行要么都不执行。若是银行卡扣除了1000块可是ATM出钱失败的话,你将会损失1000元;若是银行卡扣钱失败可是ATM却出了1000块,那么银行将损失1000元。因此,若是一个步骤成功另外一个步骤失败对双方都不是好事,若是无论哪个步骤失败了之后,整个取钱过程都能回滚,也就是彻底取消全部操做的话,这对双方都是极好的。 
事务就是用来解决相似问题的。事务是一系列的动做,它们综合在一块儿才是一个完整的工做单元,这些动做必须所有完成,若是有一个失败的话,那么事务就会回滚到最开始的状态,仿佛什么都没发生过同样。 
在企业级应用程序开发中,事务管理必不可少的技术,用来确保数据的完整性和一致性。 
事务有四个特性:ACIDsql

  • 原子性(Atomicity):事务是一个原子操做,由一系列动做组成。事务的原子性确保动做要么所有完成,要么彻底不起做用。
  • 一致性(Consistency):一旦事务完成(无论成功仍是失败),系统必须确保它所建模的业务处于一致的状态,而不会是部分完成部分失败。在现实中的数据不该该被破坏。
  • 隔离性(Isolation):可能有许多事务会同时处理相同的数据,所以每一个事务都应该与其余事务隔离开来,防止数据损坏。
  • 持久性(Durability):一旦事务完成,不管发生什么系统错误,它的结果都不该该受到影响,这样就能从任何系统崩溃中恢复过来。一般状况下,事务的结果被写到持久化存储器中。

 具体能够参考:http://www.mamicode.com/info-detail-1248286.htmlide

二、事务的传播行为

事务的第一个方面是传播行为(propagation behavior)。当事务方法被另外一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在本身的事务中运行。Spring定义了七种传播行为:学习

一、PROPAGATION_REQUIRED:若是当前没有事务,就建立一个新事务,若是当前存在事务,就加入该事务,该设置是最经常使用的设置。测试

二、PROPAGATION_SUPPORTS:支持当前事务,若是当前存在事务,就加入该事务,若是当前不存在事务,就以非事务执行。‘ui

三、PROPAGATION_MANDATORY:支持当前事务,若是当前存在事务,就加入该事务,若是当前不存在事务,就抛出异常。spa

四、PROPAGATION_REQUIRES_NEW:建立新事务,不管当前存不存在事务,都建立新事务。3d

五、PROPAGATION_NOT_SUPPORTED:以非事务方式执行操做,若是当前存在事务,就把当前事务挂起。

六、PROPAGATION_NEVER:以非事务方式执行,若是当前存在事务,则抛出异常。

七、PROPAGATION_NESTED:若是当前存在事务,则在嵌套事务内执行。若是当前没有事务,则执行与PROPAGATION_REQUIRED相似的操做。

下面咱们来看下代码:

三、demo分析

事先插入一条记录

  @Override
    public void before() {
        jdbcTemplate.update("INSERT  INTO USER (name,password) VALUES (?,?)","xiang","11111112");
    }

  3.一、PROPAGATION_REQUIRED:若是当前没有事务,就建立一个新事务,若是当前存在事务,就加入该事务,该设置是最经常使用的设置。


@Override
public void txRollbackInnerTxRollbackPropagationRequires() { transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) { jdbcTemplate.update("INSERT INTO USER (name,password) VALUES (?,?)","Huang","1111231"); transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) { jdbcTemplate.update("insert into user name, password) values (?, ?)", "Huang", "1111112"); //内部事务设置了 setRollbackOnly transactionStatus.setRollbackOnly(); } }); } }); }

测试结果:

 /**
     * PROPAGATION_REQUIRES:内部事务设置了 {@link org.springframework.transaction.TransactionStatus#setRollbackOnly()} 来触发回滚,
     * 外部事务接受到了一个 {@link UnexpectedRollbackException} 也被回滚
     */
    @Test
    public void testTxRollbackInnerTxRollbackPropagationRequires() throws Exception {
        try {
            springTxService.txRollbackInnerTxRollbackPropagationRequires();
        } catch (UnexpectedRollbackException e) {

        }finally {
            Assert.assertEquals(1,springTxService.mysqlConnection());
        }
    }

3.二、PROPAGATION_SUPPORTS:支持当前事务,若是当前存在事务,就加入该事务,若是当前不存在事务,就以非事务执行。

  @Override
    public void txRollbackInnerTxRollbackPropagationSupports() {
        supportsTransactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                jdbcTemplate.update("insert into user (name, password) values (?, ?)", "Huang",
                        "1111112");
                throw new CustomRuntimeException();
            }
        });
    }

    @Override
    public void txRollbackInnerTxRollbackPropagationSupports2() {
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                jdbcTemplate.update("insert into user (name, password) values (?, ?)", "Huang",
                        "1111112");
                supportsTransactionTemplate.execute(new TransactionCallbackWithoutResult() {
                    @Override
                    protected void doInTransactionWithoutResult(TransactionStatus status) {
                        jdbcTemplate.update("insert into user (name, password) values (?, ?)",
                                "Huang", "1111112");
                        status.setRollbackOnly();
                    }
                });
            }
        });
    }

测试用例:

 /**
     * PROPAGATION_SUPPORTS:若是当前事务上下文中没有事务,
     * 那么就按照没有事务的方式执行代码
     */
    @Test
    public void testTxRollbackInnerTxRollbackPropagationSupports() throws Exception {
        try {
            springTxService.txRollbackInnerTxRollbackPropagationSupports();
        } catch (CustomRuntimeException e) {
            e.printStackTrace();
        }finally {
            Assert.assertEquals(2, springTxService.mysqlConnection());

        }

    }
    /**
     * PROPAGATION_SUPPORTS:若是当前事务上下文中存在事务,
     * 那么合并到当前上下文的事务中去,
     * 表现地和 {@link org.springframework.transaction.TransactionDefinition#PROPAGATION_REQUIRED} 同样
     */
    @Test(expected = UnexpectedRollbackException.class)
    public void testTxRollbackInnerTxRollbackPropagationSupports2() throws Exception {
        springTxService.txRollbackInnerTxRollbackPropagationSupports2();
    }

3.三、PROPAGATION_MANDATORY:支持当前事务,若是当前存在事务,就加入该事务,若是当前不存在事务,就抛出异常。

 @Override
    public void txRollbackInnerTxRollbackPropagationMandatory() {
        mandatoryTransactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                jdbcTemplate.update("insert into user (name, password) values (?, ?)", "Huang",
                        "1111112");
            }
        });
    }

    @Override
    public void txRollbackInnerTxRollbackPropagationMandatory2() {
        nestedTransactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                jdbcTemplate.update("insert into user (name, password) values (?, ?)", "Huang",
                        "1111112");
                mandatoryTransactionTemplate.execute(new TransactionCallbackWithoutResult() {
                    @Override
                    protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                        jdbcTemplate.update("insert into user (name, password) values (?, ?)",
                                "Huang", "1111112");
                        //内部事务回滚了,外部事务也跟着回滚
                        transactionStatus.setRollbackOnly();
                    }
                });
            }
        });
    }

测试用例:

  /**
     * PROPAGATION_MANDATORY:强制性的事务,当前的事务上下文中不存在事务的话,会抛出 {@link IllegalTransactionStateException}
     */
    @Test(expected = IllegalTransactionStateException.class)
    public void testTxRollbackInnerTxRollbackPropagationMandatory() throws Exception {
        springTxService.txRollbackInnerTxRollbackPropagationMandatory();

    }
    /**
     * PROPAGATION_MANDATORY:强制性的事务,内部的事务发生回滚,
     * 那么外部的事务也会发生回滚,表现地和 {@link org.springframework.transaction.TransactionDefinition#PROPAGATION_REQUIRED}
     * 同样,也会抛出 {@link UnexpectedRollbackException}
     */
    @Test(expected = UnexpectedRollbackException.class)
    public void testTxRollbackInnerTxRollbackPropagationMandatory2() throws Exception {
        springTxService.txRollbackInnerTxRollbackPropagationMandatory2();
    }

3.四、PROPAGATION_REQUIRES_NEW:建立新事务,不管当前存不存在事务,都建立新事务。

 @Override
    public void txRollbackInnerTxRollbackPropagationRequiresNew() {
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                requiresNewTransactionTemplate.execute(new TransactionCallbackWithoutResult() {
                    @Override
                    protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                        jdbcTemplate.update("insert into user (name, password) values (?, ?)",
                                "Huang", "1111112");
                    }
                });
                //外部事务发生回滚,内部事务应该不受影响仍是可以提交
                throw new RuntimeException();
            }
        });
    }

    @Override
    public void txRollbackInnerTxRollbackPropagationRequiresNew2() {
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                jdbcTemplate.update("insert into user (name, password) values (?, ?)", "Huang",
                        "1111112");
                requiresNewTransactionTemplate.execute(new TransactionCallbackWithoutResult() {
                    @Override
                    protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                        jdbcTemplate.update("insert into user (name, password) values (?, ?)",
                                "Huang", "1111112");
                        // 内部事务发生回滚,可是外部事务不该该发生回滚
                        transactionStatus.setRollbackOnly();
                    }
                });
            }
        });
    }

    @Override
    public void txRollbackInnerTxRollbackPropagationRequiresNew3() {
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                jdbcTemplate.update("insert into user (name, password) values (?, ?)", "Huang",
                        "1111112");
                requiresNewTransactionTemplate.execute(new TransactionCallbackWithoutResult() {
                    @Override
                    protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                        jdbcTemplate.update("insert into user (name, password) values (?, ?)",
                                "Huang", "1111112");
                        // 内部事务抛出 RuntimeException,外部事务接收到异常,依旧会发生回滚
                        throw new RuntimeException();
                    }
                });
            }
        });
    }

测试用例:

/**
     * PROPAGATION_REQUIRES_NEW:外部事务发生回滚,内部事务继续提交,不受影响
     */
    @Test
    public void testTxRollbackInnerTxRollbackPropagationRequiresNew() throws Exception {
        try {
            springTxService.txRollbackInnerTxRollbackPropagationRequiresNew();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            Assert.assertEquals(2,springTxService.mysqlConnection());
        }
    }
    /**
     * PROPAGATION_REQUIRES_NEW:内部事务经过设置 {@link org.springframework.transaction.TransactionStatus#setRollbackOnly()} 来触发回滚,
     * 外部事务依旧能够不受影响,正常提交
     */
    @Test
    public void testTxRollbackInnerTxRollbackPropagationRequiresNew2() throws Exception {
        springTxService.txRollbackInnerTxRollbackPropagationRequiresNew2();
        Assert.assertEquals(2,springTxService.mysqlConnection());
    }
    /**
     * PROPAGATION_REQUIRES_NEW:内部事务抛出了 {@link RuntimeException} 异常发生了回滚,外部事务接收到这个异常也会发生回滚
     */
    @Test
    public void testTxRollbackInnerTxRollbackPropagationRequiresNew3() throws Exception {
        try {
            springTxService.txRollbackInnerTxRollbackPropagationRequiresNew3();
        } catch (RuntimeException e) {
            e.printStackTrace();
        }finally {
            Assert.assertEquals(1,springTxService.mysqlConnection());

        }
    }

3.五、PROPAGATION_NOT_SUPPORTED:以非事务方式执行操做,若是当前存在事务,就把当前事务挂起。

 @Override
    public void txRollbackInnerTxRollbackPropagationNotSupport() {
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                jdbcTemplate.update("insert into user (name, password) values (?, ?)", "Huang",
                        "1111112");
                notSupportedTransactionTemplate.execute(new TransactionCallbackWithoutResult() {
                    @Override
                    protected void doInTransactionWithoutResult(TransactionStatus status) {
                        jdbcTemplate.update("insert into user (name, password) values (?, ?)",
                                "Huang", "1111112");
                    }
                });
                // 外部事务回滚,不会把内部的也连着回滚
                transactionStatus.setRollbackOnly();
            }
        });
    }

    @Override
    public void txRollbackInnerTxRollbackPropagationNotSupport2() {
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                try {
                    notSupportedTransactionTemplate.execute(new TransactionCallbackWithoutResult() {
                        @Override
                        protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                            jdbcTemplate.update("insert into user (name, password) values (?, ?)",
                                    "Huang", "1111112");
                            throw new CustomRuntimeException();
                        }
                    });
                } catch (CustomRuntimeException e) {
                }
            }
        });
    }

测试用例:

 /**
     * PROPAGATION_NOT_SUPPORTED:不支持事务,
     * 外围的事务回滚不会致使它包含的内容回滚
     */
    @Test
    public void testTxRollbackInnerTxRollbackPropagationNotSupport() throws Exception {
        springTxService.txRollbackInnerTxRollbackPropagationNotSupport();
        Assert.assertEquals(2,springTxService.mysqlConnection());

    }
    /**
     * PROPAGATION_NOT_SUPPORTED:不支持事务,内部发生异常,外部捕获,都不会发生回滚
     */
    @Test
    public void testTxRollbackInnerTxRollbackPropagationNotSupport2() throws Exception {
        springTxService.txRollbackInnerTxRollbackPropagationNotSupport2();
        Assert.assertEquals(3,springTxService.mysqlConnection());
    }

3.六、PROPAGATION_NEVER:以非事务方式执行,若是当前存在事务,则抛出异常。

 1   @Override
 2     public void txRollbackInnerTxRollbackPropagationNever() {
 3         neverTransactionTemplate.execute(new TransactionCallbackWithoutResult() {
 4             @Override
 5             protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
 6                 jdbcTemplate.update("insert into user (name, password) values (?, ?)", "Huang",
 7                         "1111112");
 8             }
 9         });
10     }
11 
12     @Override
13     public void txRollbackInnerTxRollbackPropagationNever2() {
14         transactionTemplate.execute(new TransactionCallbackWithoutResult() {
15             @Override
16             protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
17                 jdbcTemplate.update("insert into user (name, password) values (?, ?)", "Huang",
18                         "1111112");
19                 neverTransactionTemplate.execute(new TransactionCallbackWithoutResult() {
20                     @Override
21                     protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
22                         jdbcTemplate.update("insert into user (name, password) values (?, ?)",
23                                 "Huang", "1111112");
24                     }
25                 });
26             }
27         });
28     }
29 
30     @Override
31     public void txRollbackInnerTxRollbackPropagationNever3() {
32         neverTransactionTemplate.execute(new TransactionCallbackWithoutResult() {
33             @Override
34             protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
35                 jdbcTemplate.update("insert into user (name, password) values (?, ?)", "Huang",
36                         "1111112");
37                 neverTransactionTemplate.execute(new TransactionCallbackWithoutResult() {
38                     @Override
39                     protected void doInTransactionWithoutResult(TransactionStatus status) {
40                         jdbcTemplate.update("insert into user (name, password) values (?, ?)",
41                                 "Huang", "1111112");
42                     }
43                 });
44             }
45         });
46     }
View Code

测试代码:

 /**
     * PROPAGATION_NEVER:不容许当前事务上下文中存在事务,若是没有,就正常执行
     */
    @Test
    public void testTxRollbackInnerTxRollbackPropagationNever() throws Exception {
        springTxService.txRollbackInnerTxRollbackPropagationNever();
        Assert.assertEquals(2,springTxService.mysqlConnection());
    }
    /**
     * PROPAGATION_NEVER:不容许当前事务上下文中存在事务,
     * 若是有,则抛出 {@link IllegalTransactionStateException}
     */
    @Test(expected = IllegalTransactionStateException.class)
    public void testTxRollbackInnerTxRollbackPropagationNever2() throws Exception {
        springTxService.txRollbackInnerTxRollbackPropagationNever2();
    }
    /**
     * PROPAGATION_NEVER:不容许当前事务上下文中存在事务,
     * 当两个 NEVER 的嵌套在一块儿的时候,应该也是可以执行成功的。
     */
    @Test
    public void testTxRollbackInnerTxRollbackPropagationNever3() throws Exception {
        springTxService.txRollbackInnerTxRollbackPropagationNever3();
        Assert.assertEquals(3,springTxService.mysqlConnection());
    }

3.七、PROPAGATION_NESTED:若是当前存在事务,则在嵌套事务内执行。若是当前没有事务,则执行与PROPAGATION_REQUIRED相似的操做。

 @Override
    public void txRollbackInnerTxRollbackPropagationNested() {
        nestedTransactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                jdbcTemplate.update("insert into user (name, password) values (?, ?)", "Huang",
                        "1111112");
                nestedTransactionTemplate.execute(new TransactionCallbackWithoutResult() {
                    @Override
                    protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                        jdbcTemplate.update("insert into user (name, password) values (?, ?)", "Huang",
                                "1111112");
                        // 内部事务设置了 rollbackOnly,外部事务应该不受影响,能够继续提交
                        transactionStatus.setRollbackOnly();
                    }
                });
            }
        });
    }

    @Override
    public void txRollbackInnerTxRollbackPropagationNested2() {
        nestedTransactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                jdbcTemplate.update("insert into user (name, password) values (?, ?)", "Huang",
                        "1111112");
                nestedTransactionTemplate.execute(new TransactionCallbackWithoutResult() {
                    @Override
                    protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                        jdbcTemplate.update("insert into user (name, password) values (?, ?)",
                                "Huang", "1111112");
                    }
                });
                // 外部事务设置了 rollbackOnly,内部事务应该也被回滚掉
                transactionStatus.setRollbackOnly();
            }
        });
    }

测试代码:

/**
     * PROPAGATION_NESTED:内部事务经过设置 {@link org.springframework.transaction.TransactionStatus#setRollbackOnly()} 来触发回滚,
     * 外部事务依旧能够不受影响,正常提交
     */
    @Test
    public void testTxRollbackInnerTxRollbackPropagationNested() throws Exception {
        springTxService.txRollbackInnerTxRollbackPropagationNested();
        Assert.assertEquals(2, springTxService.mysqlConnection());

    }
    /**
     * PROPAGATION_NESTED:外部事务经过设置 {@link org.springframework.transaction.TransactionStatus#setRollbackOnly()} 来触发回滚,因为
     * savepoint 在外部事务的开头,因此内部事务应该也会被一块儿回滚掉
     */
    @Test
    public void testTxRollbackInnerTxRollbackPropagationNested2() throws Exception {
        springTxService.txRollbackInnerTxRollbackPropagationNested2();
        Assert.assertEquals(1, springTxService.mysqlConnection());

    }
相关文章
相关标签/搜索