日期:2014-05-19  浏览次数:20732 次

J2EE事务陷阱

详见: http://www.ibm.com/developerworks/cn/java/j-ts1.html

?

1. 基于 ORM 的框架需要一个事务来触发对象缓存与数据库之间的同步,如Spring框架中

解决方法: @Transactional 注释加在CRUD方法上,同时在Spring配置文件中声明事务管理bean如下:

<tx:annotation-driven transaction-manager="transactionManager"/>

?

?

2. @Transactional 只读标志陷阱

?

@Transactional(readOnly = true, propagation=Propagation.SUPPORTS)
在Spring JDBC(non-ORM)中,由于传播模式被设置为 SUPPORTS,所以不会启动任何事务,因此该方法有效地利用了一个本地(数据库)事务。只读标志只在事务启动时应用。在本例中,因为没有启动任何事务,所以只读标志被忽略。

注:本地事务,一般也称为数据库事务,是数据库做的。本地事务很适合执行单一插入、更新或删除语句的逻辑工作单元(LUW)。我理解的是默认的JDBC自动提交就是这里指的本地事务。

?

@Transactional(readOnly = true, propagation=Propagation.REQUIRED)

在Spring JDBC中,会抛出一个异常,表示您正在试图对一个只读连接执行更新。因为启动了一个事务(REQUIRED),所以连接被设置为只读。毫无疑问,在试图执行 SQL 语句时,您会得到一个异常,告诉您该连接是一个只读连接。

?

在Spring JPA中,上面的事务声明可以正常工作。因为使用基于 ORM 的框架时,只读标志只是对数据库的一个提示,并且一条基于 ORM 框架的指令(本例中是 Hibernate)将对象缓存的 flush 模式设置为 NEVER,表示在这个工作单元中,该对象缓存不应与数据库同步。不过,REQUIRED 传播模式会覆盖所有这些内容,允许事务启动并工作,就好像没有设置只读标志一样。

?

@Transactional 注释的默认传播模式是 REQUIRED。这意味着事务会在不必要的情况下启动。根据使用的数据库,这会引起不必要的共享锁,可能会使数据库中出现死锁的情况。此外,启动和停止事务将消耗不必要的处理时间和资源。总的来说,在使用基于 ORM 的框架时,只读标志基本上毫无用处,在大多数情况下会被忽略。但如果您坚持使用它,请记得将传播模式设置为 SUPPORTS(如清单 9 所示),这样就不会启动事务。

?

文中强调针对读取操作,避免使用 @Transactional 注释,也就是不启动事物。我表示怀疑,就像多线程操作同一个数据一样,需不需要同时对get/set 进行synchronized

?

3. REQUIRES_NEW 事务属性陷阱

?

如果方法1调用方法2, 方法1,2 都声明成Require_new. 那么 方法1中成功调用方法2后,如果代码中出现异常,方法1会回滚,方法2不会。所以Require_new 少用。一般用 MANDATORYREQUIRED 属性。但一种特殊的业务环境下需要这种声明。如:对审计方法使用 REQUIRES_NEW 属性,不管事务的结果如何,审计数据都会被保存

?

4. 事务回滚陷阱

?

?Spring中,运行时异常(即非受控异常)自动强制执行整个逻辑工作单元的回滚,但checked Exception不会

?

亦可声明指定需要回滚的异常:

@Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)

在回滚事务这一点上,EJB 的工作方式与 Spring Framework 稍微有点不同。EJB 3.0 规范中的 @TransactionAttribute 注释不包含指定回滚行为的指令。必须使用 SessionContext.setRollbackOnly() 方法将事务标记为执行回滚,