我的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()
}
}
}
但我不明白为什么我需要这样做,即为什么回滚交易似乎会关闭会议?
答案 0 :(得分:4)
回滚使会话无法使用是正常的,因为它是一个不可恢复的错误,就像所有Hibernate异常一样。例如,参见类ObjectNotFoundException
的javadoc:
/*
* ...
*
* Like all Hibernate exceptions, this exception is considered
* unrecoverable.
*
*/
原因是会话是数据库和内存中对象之间的状态同步器组件。处理数据库中回滚的方法是回滚内存中对象的更改。
由于此功能难以实施且使用有限,因此决定使这些类型的异常无法恢复。
您可以尝试捕获它并继续使用会话,但无法保证会话将处于一致状态。
修改强>
以下是documentation中的Javadoc以外的其他参考资料:
Hibernate引发的异常意味着你必须回滚你的 数据库事务并立即关闭Session(这是 本章稍后将详细讨论。如果您的会话是 绑定到应用程序,您必须停止应用程序。滚动 返回数据库事务不会使您的业务对象返回 进入交易开始时的状态。这意味着 数据库状态和业务对象将不同步。 通常这不是问题,因为异常是不可恢复的 无论如何你必须在回滚后重新开始。
还有:
如果Session抛出异常,包括任何SQLException, 立即回滚数据库事务,调用Session.close() 并丢弃Session实例。 Session的某些方法不会 使会话保持一致状态。没有例外 Hibernate可以被视为可恢复的。确保会话将 通过在finally块中调用close()来关闭。