前言
最近项目有涉及到Spring事务,所以工作之余,想认真了解学习下Spring事务,查阅了若干资料,做了一个demo(PS:参考了大牛的)。
现分享总结如下:
1、Spring 事务的简介
理解事务之前,
先讲一个你日常生活中最常干的事:取钱。
比如你去ATM机取1000块钱,大体有两个步骤:首先输入密码金额,银行卡扣掉1000元钱;然后ATM出1000元钱。这两个步骤必须是要么都执行要么都不执行。如果银行卡扣除了1000块但是ATM出钱失败的话,你将会损失1000元;如果银行卡扣钱失败但是ATM却出了1000块,那么银行将损失1000元。所以,如果一个步骤成功另一个步骤失败对双方都不是好事,如果不管哪一个步骤失败了以后,整个取钱过程都能回滚,也就是完全取消所有操作的话,这对双方都是极好的。
事务就是用来解决类似问题的。事务是一系列的动作,它们综合在一起才是一个完整的工作单元,这些动作必须全部完成,如果有一个失败的话,那么事务就会回滚到最开始的状态,仿佛什么都没发生过一样。
在企业级应用程序开发中,事务管理必不可少的技术,用来确保数据的完整性和一致性。
事务有四个特性:ACID
- 原子性(Atomicity):事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用。
- 一致性(Consistency):一旦事务完成(不管成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不会是部分完成部分失败。在现实中的数据不应该被破坏。
- 隔离性(Isolation):可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。
- 持久性(Durability):一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来。通常情况下,事务的结果被写到持久化存储器中。
具体可以参考:http://www.mamicode.com/info-detail-1248286.html
2、事务的传播行为
事务的第一个方面是传播行为(propagation behavior)。当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。Spring定义了七种传播行为:
1、PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。
2、PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。‘
3、PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。
4、PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。
5、PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
6、PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
7、PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
下面我们来看下代码:
3、demo分析
事先插入一条记录
@Override public void before() { jdbcTemplate.update("INSERT INTO USER (name,password) VALUES (?,?)","xiang","11111112"); }
3.1、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.2、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.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.4、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.5、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.6、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 }
测试代码:
/** * 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.7、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()); }