[spring] 事务简介

什么是事务

一个事务是有下列属性的一个工作单元:

  • 原子性(ATOMICITY):
    一个事务要被完全的无二义性的做完或撤消。在任何操作出现一个错误的情况下,构成事务的所有操作的效果必须被撤消,数据应被回滚到以前的状态。

  • 一致性(CONSISTENCY):
    一个事务应该保护所有定义在数据上的不变的属性(例如完整性约束)。在完成了一个成功的事务时,数据应处于一致的状态。换句话说,一个事务应该把系统从一个一致-状态转换到另一个一致状态。举个例子,在关系数据库的情况下,一个一致的事务将保护定义在数据上的所有完整性约束。

  • 隔离性(ISOLATION):
    在同一个环境中可能有多个事务并发执行,而每个事务都应表现为独立执行。串行的执行一系列事务的效果应该同于并发的执行它们。这要求两件事:
    在一个事务执行过程中,数据的中间的(可能不一致)状态不应该被暴露给所有的其他事务。
    两个并发的事务应该不能操作同一项数据。数据库管理系统通常使用锁来实现这个特征。

  • 持久性(DURABILITY):
    一个被完成的事务的效果应该是持久的。

传播行为 Propagation

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

传播行为 含义
PROPAGATION_REQUIRED 表示当前方法必须运行在事务中。如果当前事务存在,方法将会在该事务中运行。否则,会启动一个新的事务
PROPAGATION_SUPPORTS 表示当前方法不需要事务上下文,但是如果存在当前事务的话,那么该方法会在这个事务中运行
PROPAGATION_MANDATORY 表示该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常
PROPAGATION_REQUIRED_NEW 表示当前方法必须运行在它自己的事务中。一个新的事务将被启动。如果存在当前事务,在该方法执行期间,当前事务会被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager
PROPAGATION_NOT_SUPPORTED 表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager
PROPAGATION_NEVER 表示当前方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常
PROPAGATION_NESTED 表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与PROPAGATION_REQUIRED一样。注意各厂商对这种传播行为的支持是有所差异的。可以参考资源管理器的文档来确认它们是否支持嵌套事务

隔离级别

事务的第二个维度就是隔离级别(isolation level)。隔离级别定义了一个事务可能受其他并发事务影响的程度。

隔离级别 含义
ISOLATION_DEFAULT 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别.另外四个与JDBC的隔离级别相对应
ISOLATION_READ_UNCOMMITTED 这是事务最低的隔离级别,它充许令外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。
ISOLATION_READ_COMMITTED 保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据
ISOLATION_REPEATABLE_READ 这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。
ISOLATION_SERIALIZABLE 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻像读。

高层同步方式

我们可以使用持久化api或orm框架api来操作数据库,因此用户代码可以不关心资源的使用、清理、选择等,可以专注于非持久化的逻辑。

底层同步方式

  • JDBC 事务原理

事务是由一个connection开启,提交或者回滚的,也就是事务的管理是通过connection管理的,可以简化为一下代码:

获取连接 Connection con = DriverManager.getConnection()
开启事务 con.setAutoCommit(true/false);
执行CRUD
提交事务/回滚事务 con.commit() / con.rollback();
关闭连接 conn.close();

  • 其他

像DataSourceUtils (for JDBC), EntityManagerFactoryUtils (for JPA), SessionFactoryUtils (for Hibernate), PersistenceManagerFactoryUtils (for JDO),是底层api操作。如果你的应用直接调用持久化api,需要自己管理事务,无论是否使用使用了spring transaction。

当然,一旦你使用了Spring的jdbc support,最好不要使用DataSourceUtils或者其他工具类,因为spring会帮你做很多事情,效率更高。

事务回滚

spring默认的回滚方式是:如果方法抛出RuntimeExceptoin或者Errors,事务会回滚。Checked Exception不会导致事务回滚。

当然我们可以精确的配置那些异常回滚,包括Checked Exception。下面的配置自定义了一个Exception,用来做事务回滚检查。

<tx:advice id="txAdvice" transaction-manager="txManager">
  <tx:attributes>
  <tx:method name="get*" read-only="true" rollback-for="NoProductInStockException"/>
  <tx:method name="*"/>
  </tx:attributes>
</tx:advice>

如果异常抛出是,你不想事务回滚,可以定义非回滚角色,如下,及时抛出InstrumentNotFoundException异常,事务依然会提交。

<tx:advice id="txAdvice">
  <tx:attributes>
  <tx:method name="updateStock" no-rollback-for="InstrumentNotFoundException"/>
  <tx:method name="*"/>
  </tx:attributes>
</tx:advice>

如果同时配置了回滚和非回滚异常,spring会通过强匹配方式决定哪个更匹配。所以如下配置,只有InstrumentNotFoundException不会导致事务回滚。

<tx:advice id="txAdvice">
  <tx:attributes>
  <tx:method name="*" rollback-for="Throwable" no-rollback-for="InstrumentNotFoundException"/>
  </tx:attributes>
</tx:advice>

你也可以通过编程的方式回滚事务。虽然很简单,但是这对代码有很强的侵入性,使你的代码和spring 事务代码耦合在一起,不推荐使用。

public void resolvePosition() {
  try {
    // some business logic...
  } catch (NoProductInStockException ex) {
    // trigger rollback programmatically
    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
  }
}

参考

http://zht1933.iteye.com/blog/479808
https://www.ibm.com/developerworks/cn/java/j-master-spring-transactional-use/index.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。