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());

    }
相關文章
相關標籤/搜索