【问题标题】:Manual Rollback of transactions in Seam在 Seam 中手动回滚事务
【发布时间】:2012-03-24 20:56:32
【问题描述】:

这是与Forcing a transaction to rollback on validation error 类似的问题 场景是这样的: 用户编辑一个页面,事务被设置为 MANUAL 所以只有当我们调用flush时它才会被提交到数据库。现在用户想要取消更改。很简单,因为你还没有冲洗它。

现在考虑这种情况:用户编辑页面上有很多 ajax。其中一些 ajax 回调需要数据库查询(例如,使用 RichFaces 建议框等)。还完成了一些需要数据库查找的验证。问题是当您进行查询时,Hibernate 会自动发出刷新。所以用户没有按下保存按钮(这将刷新交易)他按下了取消按钮。你现在做什么?

如果您不执行任何操作,更改将被写入数据库 - 而不是用户期望的。

你可以抛出一个带有

注释的异常
@ApplicationException(rollback=true)

这将回滚事务。然后,您可以重定向到另一个页面。但是在这里我遇到了另一个问题,在您重定向到的某些页面上,您会遇到延迟初始化异常。我已经指定了

<exception class="com.mycomp.BookingCancelException">
    <end-conversation before-redirect="true"/>  
    <redirect view-id="/secure/Bookings.xhtml">
        <message severity="INFO">#{messages['cancel.rollback']}</message>
    </redirect>
</exception>

在 pages.xml 中,因此对话应该在我们进行重定向之前结束。应该开始新的对话(通过新交易),但似乎并非在所有情况下都会发生?为什么?

我在其他地方读过,你可以简单地使用

Transaction.instance().rollback();

这会更好,因为您不必通过异常(Seam 处理异常时重定向总是需要很长时间),但问题是事务实际上并没有回滚。我不知道为什么。如果我检查事务的状态,它会说它没有处于回滚状态。

您将如何最好地处理取消请求。在这种情况下,纯手动冲洗不起作用。您可以使用分离的实体,但页面包含多个链接实体,所以这会变得混乱。

更新:我现在发现抛出 ApplicationException 并不会在所有情况下回滚事务。现在很困惑。

更新 2:当然,当您有一个使用 ajax 更新值的页面时,回滚事务将不起作用。每个事务只涵盖一个请求。所以,如果你这样做,例如使用 ajax 请求进行 5 次编辑,回滚事务只会回滚上次 ajax 请求的更改,而不是前 4 次的更改。

所以真正的解决方法是使用冲洗模式MANUAL。

即使您指定了 MANUAL,也有一些事情会导致刷新。

  • ajax 请求中的查询可能会触发刷新 - 对查询使用 setFlushMode(FlushMode.COMMIT) 来避免这种情况。
  • 根据所使用的 ID 生成(例如,如果您使用策略 IDENTITY),持久化实体可能会触发刷新。您可以使用 Cascades 解决此问题。如果您需要在编辑期间创建与您正在编辑的主要实体没有任何实际关系的实体,只需将它们添加到列表中,并在您保存时将所有实体保留在该列表中。
  • 当您开始嵌套对话或另一个 bean 加入对话时,如果您未指定 @Begin(join=true,flushMode=FlushModeType.MANUAL),该会话上的刷新模式将设置回 AUTO

您可能希望在 components.xml 中将 MANUAL 指定为默认模式

<core:manager concurrent-request-timeout="10000"
  conversation-id-parameter="cid" conversation-timeout="600000" default-flush-mode="MANUAL"/>

【问题讨论】:

  • 你试过Transaction.instance().setRollbackOnly()吗?

标签: java hibernate transactions seam rollback


【解决方案1】:

你试过了吗

@Begin(flushMode=MANUAL)
someRandomValidationMethodHere(){ ... }

或设置

<core:manager conversation-timeout="120000" default-flush-mode="manual" />

在 components.xml 中?

【讨论】:

  • 我已经在使用手动冲洗模式。问题是,如果它需要,休眠将自动执行刷新。通常当您执行数据库查询时。所以到那时你不能简单地离开对话来取消一切。
  • 如何更改休眠刷新模式?如果 hibernate flushMode 设置为 AUTO 或 ALWAYS,它将在查询运行时刷新数据。如果您将其更改为 COMMIT 将不会在没有提交的情况下刷新数据......或者至少应该如此。添加一些调试语句以在您的数据库调用周围输出当前的休眠刷新模式以进行验证会很有启发性。
  • 感谢您的提示。我试试看
  • 你知道如何设置你的休眠状态吗?如果您在 components.xml 中声明它,您可以像 entityManager 一样注入或访问它。查看this stack overflow question,虽然我不确定 cfg-resource-name 的东西,但我必须检查我的代码,但我不记得使用它。
  • 可以从(Session)entityManager.getDelegate()获取Session
【解决方案2】:

知道您可以使用属性bypassUpdates 告诉ajax 验证组件不要刷新可能会有所帮助。这是一篇文章explaining吧。

【讨论】:

  • 这仍然无法解决您进行查找的问题(例如 Richfaces 建议框),不是吗?
  • 但是查找不会更新数据模型,也不应该在事务中完成
  • @Ocars Lopez:你使用 Seam 吗?每个数据库访问都是在事务中完成的。 Seam(如果您使用 Seam 托管事务)或 EJB 容器将在从 Web 层调用 bean 时自动为您启动它。如果您使用长时间运行的对话,则此事务涵盖整个对话。
  • @Ben 部分正确;您可以使用 @Transactional(NEVER) 注释方法以将其从任何活动事务的上下文中排除,这是只读搜索/查询/查找方法的好习惯
  • 每个数据库访问都是在一个事务中完成的。来自“Java Persistence with Hibernate”一书(第 10.3.1 节):“许多应用程序开发人员认为他们可以在事务之外与数据库通信。这显然是不可能的;不能将 SQL 语句发送到事务之外的数据库数据库事务。术语非事务性数据访问是指没有明确的事务边界,没有系统事务,并且数据访问的行为是自动提交模式的行为。并不意味着不涉及物理数据库事务。”跨度>
【解决方案3】:

您可以管理您的交易并通过激活按钮进行管理。

    protected void doGet(HttpServletRequest request,
                         HttpServletResponse response, String butonPressed)
            throws ServletException, IOException {

        try {
            // Begin unit of work
            HibernateUtil.getSessionFactory()
                    .getCurrentSession().beginTransaction();

        } catch (Exception ex) {
            HibernateUtil.getSessionFactory()
                    .getCurrentSession().getTransaction().rollback();
            throw new ServletException(ex);
        }
        finally
        {
            if ("SAVE".equals(butonPressed))
            {

            // Process request and render page...

            // End unit of work
            HibernateUtil.getSessionFactory()
                    .getCurrentSession().getTransaction().commit();
            }
            else
            {
                HibernateUtil.getSessionFactory()
                        .getCurrentSession().getTransaction().rollback();
            }
        }

    }

【讨论】:

    猜你喜欢
    • 2010-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-24
    • 2015-06-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多