Grails程序化事务处理



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

@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,无会话或会话已关闭

如果我更改上面的代码,以便每个艺术家都在自己的会话中更新,这似乎可以解决问题,

Artist.withNewSession { session ->
Artist.withTransaction { status ->
try {
updateArtistInfo(artist)
} catch (IOException ex) {
status.setRollbackOnly()
}
}
}

但我不明白为什么我需要这样做,也就是说,为什么回滚事务似乎会关闭会话?

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

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

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

由于此功能很难实现且使用有限,因此决定使这些类型的异常不可恢复。

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

编辑:

以下是文档中除了Javadoc之外的其他参考文献:

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

以及:

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

最新更新