最近项目有涉及到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()); }