2014-02-07 70 views
2

我的Grails应用程序有一个服务方法,它可以更新last.fm的web服务中的艺术家列表。Grails编程事务处理

@Transactional(propagation = Propagation.NOT_SUPPORTED) 
void updateLastFmArtists(Range idRange = null) { 

    Artist.list().each { Artist artist -> 

     // We could be updating a lot of artists here, the process could take up 
     // to an hour and we don't want to wrap all that in a single transaction 
     Artist.withTransaction { status -> 
      try { 
       updateArtistInfo(artist) 

      } catch (IOException ex) { 
       status.setRollbackOnly() 
      } 
     } 
    } 
} 

每个个体艺术家它自己的事务,如果IOException抛出应当回滚内更新。不过,我注意到以下行为:

如果要更新一个艺术家试图抛出一个IOException - 导致事务回滚 - 那么接下来艺术家的更新总是失败,因为以下错误

org.hibernate.LazyInitializationException:无法初始化懒洋洋地一 角色的集合:org.example.Artist.topTracks,没有会话或 会话关闭

如果我改变上面的代码,使每一位艺术家被更新w^ithin它自己的会话,这似乎是解决问题,

Artist.withNewSession { session -> 
     Artist.withTransaction { status -> 
      try { 
       updateArtistInfo(artist) 

      } catch (IOException ex) { 
       status.setRollbackOnly() 
      } 
     } 
    } 

但我不明白为什么我要做到这一点,即这是为什么回滚事务似乎关闭会话?

回答

4

回滚会导致会话不可用,这是正常的,因为它是像所有Hibernate异常一样的不可恢复的错误。参见例如ObjectNotFoundException类的Javadoc:

/* 
* ... 
* 
* Like all Hibernate exceptions, this exception is considered 
* unrecoverable. 
* 
*/ 

的原因是,该会话是在存储器中的数据库和对象之间的状态同步器组件。处理数据库中的回滚的方法是回滚内存中对象的更改。

由于此功能难以实施并且使用有限,因此决定采取措施使这些类型的异常无法恢复。

您可以尝试捕捉它并继续使用会话,但不能保证会话处于一致状态。

编辑:

这里比的Javadoc其他更多参考资料,在documentation发现:

由Hibernate抛出的异常意味着你必须立即回滚您 数据库事务,并关闭Session立即(这是 在本章后面更详细讨论)。如果您的会话绑定到应用程序的 ,则必须停止应用程序。滚动 返回数据库事务并不会将您的业务对象返回到 处于事务开始时的状态。这意味着 数据库状态和业务对象将不同步。 通常这不是问题,因为异常不可恢复 ,无论如何您必须重新开始。

也:

如果Session抛出异常,包括任何的SQLException, 立即回滚数据库事务,调用Session.close() ,丢弃该Session实例。会话的某些方法不会 使会话保持一致状态。 没有抛出异常Hibernate可以被视为可恢复。通过在finally块中调用close()来确保会话将会关闭 。

+0

+1但是,在这种特殊情况下,它不是一个无法解决的异常,这是我在单个事务范围内捕捉的一个例外。我觉得这很令人吃惊,这会使整个会话无效 –

+0

我增加了一些对此的引用,基于文档的建议是重新开始新会话,因为会话的某些方法不会使会话保持一致状态在回滚的情况下 –

+0

感谢您的帮助,但是,上面的引用是关于Hibernate或Hibernate异常抛出的异常,但我的情况并不涉及这两种异常。我猜这是我对'status.setRollbackOnly()'的调用,它会使会话失效而不是抛出的异常 –