【问题标题】:Grails programmatic transaction handlingGrails 程序化事务处理
【发布时间】:2014-03-05 12:16:48
【问题描述】:

我的 Grails 应用程序有一个服务方法,可以从 last.fm 的网络服务更新艺术家列表。

@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 grails transactions grails-orm


    【解决方案1】:

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

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

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

    由于此功能难以实现且用途有限,因此决定使此类异常不可恢复。

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

    编辑:

    这里是除 Javadoc 之外的更多参考资料,可在 documentation 中找到:

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

    还有:

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

    【讨论】:

    • +1 但在这种特殊情况下,它不是一个未处理的异常,它是我在单个事务范围内捕获的异常。我发现这会使整个会话无效
    • 我添加了一些对此的引用,基于文档的建议是重新开始一个新会话,因为 Session 的某些方法不会在回滚的情况下使会话保持一致状态
    • 感谢您的帮助,但是,您上面的引用谈论的是 Hibernate 或 Hibernate 异常引发的异常,但我的案例不涉及其中任何一个。我想这是我对status.setRollbackOnly() 的调用,它使会话无效而不是抛出的异常
    • 问题是手动使事务无效会产生与引发 Hibernate 异常相同的结果,这句话是关键:'数据库状态和业务对象将不同步......你将拥有无论如何都要在回滚后重新开始'。这意味着无论是通过抛出异常还是通过 setRollbackOnly,会话都不是为了从回滚中恢复而设计的,与回滚的原因无关
    猜你喜欢
    • 2014-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-31
    • 2018-01-25
    • 1970-01-01
    相关资源
    最近更新 更多