最近項目有涉及到Spring事務,因此工做之餘,想認真瞭解學習下Spring事務,查閱了若干資料,作了一個demo(PS:參考了大牛的)。html
現分享總結以下:mysql
理解事務以前,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相似的操做。
下面咱們來看下代碼:
事先插入一條記錄
@Override public void before() { jdbcTemplate.update("INSERT INTO USER (name,password) VALUES (?,?)","xiang","11111112"); }
@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()); } }
@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(); }
@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(); }
@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()); } }
@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()); }
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 }
測試代碼:
/** * 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()); }