Java JPA在异常/回滚情况下处理分离实体状态的正确方法

Java JPA在异常/回滚情况下处理分离实体状态的正确方法,java,hibernate,jpa,ejb,jpa-2.1,Java,Hibernate,Jpa,Ejb,Jpa 2.1,我有这个类,我想到了三种在持久性异常(在别处处理)的情况下处理分离实体状态的方法: @德拉甘博扎诺维奇 就这样!对第一点有很好的解释。二, 我希望你能对第三点做进一步的阐述。并给我一些关于真实世界用例的建议 然而,我绝对不会使用原子引用或类似的繁琐构造。JavaEE、Spring和其他框架和应用程序容器支持通过注释声明事务性方法:只需使用从事务性方法返回的结果 当您必须修改单个实体时,事务性方法只需将分离的实体作为参数并返回更新的实体,这很容易 public Document updateDo

我有这个类,我想到了三种在持久性异常(在别处处理)的情况下处理分离实体状态的方法:


@德拉甘博扎诺维奇 就这样!对第一点有很好的解释。二,

我希望你能对第三点做进一步的阐述。并给我一些关于真实世界用例的建议

然而,我绝对不会使用原子引用或类似的繁琐构造。JavaEE、Spring和其他框架和应用程序容器支持通过注释声明事务性方法:只需使用从事务性方法返回的结果

当您必须修改单个实体时,事务性方法只需将分离的实体作为参数并返回更新的实体,这很容易

public Document updateDocument(Document doc)
{
    Document managed = em.merge(doc);
    // managed.setXxx(...);
    // managed.setYyy(...);

    return managed;
}
但当您需要在单个事务中修改多个时,该方法可能会成为一个真正的难题:

public LinkTicketResult linkTicket(Node node, Ticket ticket)
{
    LinkTicketResult result = new LinkTicketResult();

    Node managedNode = em.merge(node);
    result.setNode(managedNode);

    // modify managedNode

    Ticket managedTicket = em.merge(ticket);
    result.setTicket(managedTicket);

    // modify managedTicket

    Remark managedRemark = createRemark(...);
    result.setRemark(managedemark);

    return result;
}
在这种情况下,我的痛苦:

  • 我必须创建一个专用的事务方法(也可能是一个专用的
    @EJB
  • 该方法将只被调用一次(只有一个调用者)——是一个“一次性”的不可重用的公共方法。丑陋的
  • 我必须创建虚拟类
    LinkTicketResult
  • 在该方法中,该类将只实例化一次,即“一次性”
  • 该方法可以有许多参数(或另一个虚拟类
    LinkTicketParameters
  • 在大多数情况下,JSF控制器操作将只调用EJB方法,从返回的容器中提取更新的实体,并将它们重新分配给本地字段
  • 我的代码将不断被“一个散客”污染,太多了,不合我的口味

  • 可能我没有看到眼前的大问题,如果您能为我指出正确的方向,我将非常感激。

    不确定这是否完全正确,但只有一种方法可以在异常后恢复:回滚并关闭EM。从

    实体管理器引发的异常意味着您必须回滚 启动数据库事务并立即关闭EntityManager (稍后将详细讨论)。如果您的EntityManager绑定到 应用程序,您必须停止应用程序。回退 数据库事务不会将业务对象放回 说明他们在交易开始时。这意味着 数据库状态和业务对象确实不同步。通常 这不是问题,因为异常是不可恢复的,并且 无论如何,回滚后必须重新开始工作单元

    --编辑-- 也看到

    附言:否决票不是我的

  • 是方法3。确实比方法2更安全。?

    对。它不仅更安全(参见第2点),而且在概念上更正确,因为只有在证明相关事务已成功时,才更改依赖于事务的状态

  • 是否存在在刷新[排除]和提交[包括]之间引发异常的情况?

    对。例如:

  • :

    乐观地假设事务不会经历争用 对于实体将在事务附近验证实体版本 结束。

    在单个事务中的每个刷新操作期间检查锁冲突既没有性能也没有实际用处

  • 延迟完整性约束(在提交时在db中强制执行)。不经常使用,但这是一个示例

  • 以后的维护和重构。在最后一次显式调用flush之后,您或其他人可能会引入其他更改

  • 是否有标准方法来处理此常见问题?

    是的,我想说你的第三种方法是标准方法:使用完整和成功交易的结果

    但是,我绝对不会使用
    AtomicReference
    或类似的繁琐构造。JavaEE、Spring和其他框架和应用程序容器支持通过注释声明事务性方法:只需使用从事务性方法返回的结果


  • @downvoter您能解释一下吗?我没有投反对票,但我可以想象这是因为不可编译的代码。@tak3shi这是一个改编为示例的代码片段,如果有人因为这个原因投了反对票,我很抱歉他思维有限;)我不明白整件事。。。。是否管理EM用户或容器?它的范围是什么?应用程序?@Gab em是CMT。控制器是一个JSF
    @ViewScoped
    @ManagedBean
    。请参阅更新的问题。谢谢,但这不是重点;我已经能够回滚事务并关闭EntityManager(
    PersistenceService#transact
    可以很好地完成),但我需要解决数据库状态和业务对象不同步的问题。通常这不是问题。在我的例子中,这就是问题所在,因为异常通常是由
    BeanValidator
    生成的,我希望用户输入正确的值并重试,而不会丢失他以前输入的值。@MicheleMariotti我看不出有任何理由在BeanValidator异常后丢失这些值。您的问题没有说明问题。@tak3shi我更新了问题,以便更好地解释代码中导致问题的原因。将“脏”实体分配给[entity]字段时,我将丢失该值,因为“脏”实体不再可合并(即具有递增的
    @Version
    值)。谢谢!“我对第三点做了一些阐述,如果你能在现实世界的案例中给出一些实用的建议,我将不胜感激。”MicheleMariotti说,这已经有点基于观点了。我认为单独提出一项质询较为合适。我想说,对于方法结果,一个单独的shotter类仍然比一个散乱的shotter
    AtomicReference
    s更具可读性
    public Document updateDocument(Document doc)
    {
        Document managed = em.merge(doc);
        // managed.setXxx(...);
        // managed.setYyy(...);
    
        return managed;
    }
    
    public LinkTicketResult linkTicket(Node node, Ticket ticket)
    {
        LinkTicketResult result = new LinkTicketResult();
    
        Node managedNode = em.merge(node);
        result.setNode(managedNode);
    
        // modify managedNode
    
        Ticket managedTicket = em.merge(ticket);
        result.setTicket(managedTicket);
    
        // modify managedTicket
    
        Remark managedRemark = createRemark(...);
        result.setRemark(managedemark);
    
        return result;
    }