从 JPA/EJB3 持久性上下文中分离实体

Posted

技术标签:

【中文标题】从 JPA/EJB3 持久性上下文中分离实体【英文标题】:Detach an entity from JPA/EJB3 persistence context 【发布时间】:2010-09-07 01:59:42 【问题描述】:

分离通过 EntityManager 获取的特定 JPA 实体 Bean 的最简单方法是什么。或者,我是否可以让查询首先返回分离的对象,以便它们本质上充当“只读”?

我想这样做的原因是因为我想修改 bean 中的数据 - 仅在我的应用程序中,但从来没有将它持久化到数据库中。在我的程序中,我最终必须在 EntityManager 上调用 flush(),这会将所有从附加实体的更改保存到基础数据库,但我想排除特定对象。

【问题讨论】:

【参考方案1】:

(可能来不及回答,但对其他人有用)

我现在正在使用 JPA 开发我的第一个系统。不幸的是,当这个系统快要完成时,我遇到了这个问题。

简单地说。使用 Hibernate,或等待 JPA 2.0。

在 Hibernate 中,您可以使用 'session.evict(object)' 从会话中删除一个对象。在JPA 2.0, in draft right now, 中,有一个'EntityManager.detach(object)' 方法可以将一个对象从持久化上下文中分离出来。

【讨论】:

现在正好是一年后。我有这个问题,我在 EntityManager 上找不到任何“分离”方法调用。是不是因为某种原因掉线了? 在这里download.oracle.com/javaee/6/api/javax/persistence/…【参考方案2】:

无论您使用哪种 JPA 实现,只需使用 entityManager.detach(object) 它现在是 JPA 2.0 和 JEE6 的一部分。

【讨论】:

【参考方案3】:

如果您需要从 EntityManager 中分离一个对象,并且您使用 Hibernate 作为您的底层 ORM 层,您可以访问 Hibernate Session 对象并使用 Mauricio Kanada 上面提到的 Session.evict(Object) 方法。

public void detach(Object entity) 
    org.hibernate.Session session = (Session) entityManager.getDelegate();
    session.evict(entity);

当然,如果您切换到另一个 ORM 提供程序,这会中断,但我认为这最好是尝试进行深层复制。

【讨论】:

【参考方案4】:

不幸的是,在当前 JPA 实现 AFAIR 中,无法断开一个对象与实体管理器的连接。

EntityManager.clear() 将断开 所有 JPA 对象,因此如果您确实计划保持连接的其他对象,这可能不是所有情况下的合适解决方案。

所以最好的办法是克隆对象并将克隆传递给更改对象的代码。由于默认克隆机制以适当的方式处理原始和不可变对象字段,因此您无需编写大量管道代码(除了深度克隆您可能拥有的任何聚合结构)。

【讨论】:

这不再正确,EM 现在提供了 detach(entity) 方法,从 JPA v2 开始,请参阅 javax.persistence.EntityManager#detach【参考方案5】:

据我所知,唯一直接的方法是:

    提交 txn - 可能不是一个合理的选择 清除持久性上下文 - EntityManager.clear() - 这很残酷,但会清除它 复制对象 - 大多数情况下,您的 JPA 对象是可序列化的,所以这应该很容易(如果不是特别有效的话)。

【讨论】:

提交txn如何导致实体分离?【参考方案6】:

如果使用EclipseLink,您也可以选择,

使用查询提示,eclipselink.maintain-cache"="false - 所有返回的对象都将被分离。

使用EclipseLink JpaEntityManager copy() API 将对象复制到所需的深度。

【讨论】:

【参考方案7】:

如果 bean 中没有太多属性,您可能只需创建一个新实例并从持久化 bean 手动设置其所有属性。

这可以实现为复制构造函数,例如:

public Thing(Thing oldBean) 
  this.setPropertyOne(oldBean.getPropertyOne());
  // and so on

然后:

Thing newBean = new Thing(oldBean);

【讨论】:

【参考方案8】:

这又快又脏,但你也可以序列化和反序列化对象。

【讨论】:

请注意,这可能会导致延迟加载和 JPA 关系出现问题。【参考方案9】:

由于我使用的是 SEAM 和 JPA 1.0,并且我的系统具有需要记录所有字段更改的功能,因此如果需要记录实体的相同字段,我创建了一个值对象或数据传输对象。新pojo的构造函数是:

    public DocumentoAntigoDTO(Documento documentoAtual) 
    Method[] metodosDocumento = Documento.class.getMethods();
    for(Method metodo:metodosDocumento)
        if(metodo.getName().contains("get"))
            try 
                Object resultadoInvoke = metodo.invoke(documentoAtual,null);
                Method[] metodosDocumentoAntigo = DocumentoAntigoDTO.class.getMethods();
                for(Method metodoAntigo : metodosDocumentoAntigo)
                    String metodSetName = "set" + metodo.getName().substring(3);
                    if(metodoAntigo.getName().equals(metodSetName))
                        metodoAntigo.invoke(this, resultadoInvoke);
                    
                
             catch (IllegalArgumentException e) 
                e.printStackTrace();
             catch (IllegalAccessException e) 
                e.printStackTrace();
             catch (InvocationTargetException e) 
                e.printStackTrace();
            
        
    

【讨论】:

【参考方案10】:

在 JPA 1.0(使用 EclipseLink 测试)中,您可以在事务之外检索实体。例如,使用容器管理的事务,您可以这样做:

public MyEntity myMethod(long id) 
    final MyEntity myEntity = retrieve(id);
    // myEntity is detached here


@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public MyEntity retrieve(long id) 
    return entityManager.find(MyEntity.class, id);

【讨论】:

【参考方案11】:

请处理类似的情况,我创建了一个扩展持久实体对象的 DTO 对象,如下所示:

class MyEntity

   public static class MyEntityDO extends MyEntity 


最后,标量查询将检索所需的非托管属性:

(Hibernate) select p.id, p.name from MyEntity P
(JPA)       select new MyEntity(p.id, p.name) from myEntity P

【讨论】:

【参考方案12】:

如果你到达这里是因为你真的想通过一个远程边界传递一个实体,那么你只需输入一些代码来愚弄冬眠者。

for(RssItem i : result.getChannel().getItem())

Cloneable 不起作用,因为它实际上复制了 PersistantBag。

忘记使用可序列化和字节数组流以及管道流。创建线程以避免死锁会扼杀整个概念。

【讨论】:

【参考方案13】:

我认为有一种方法可以通过调用此方法从 EntityManager 中逐出单个实体

EntityManagerFactory emf;
emf.getCache().evict(Entity);

这将从缓存中删除特定实体。

【讨论】:

这只会从二级缓存中驱逐对象,而不是从会话缓存中。【参考方案14】:

我正在使用 entityManager.detach(returnObject); 这对我有用。

【讨论】:

【参考方案15】:

如果实体的主键没有更改,我认为您也可以使用方法 EntityManager.refresh(Object o)。此方法将恢复实体的原始状态。

【讨论】:

以上是关于从 JPA/EJB3 持久性上下文中分离实体的主要内容,如果未能解决你的问题,请参考以下文章

实体框架分离实体和相关实体消失

一个托管对象上下文的两个持久存储 - 可能吗?

为啥 EF 在分离时会删除子实体?

使用 JPA/EJB3 进行批量插入

什么是持久性上下文?

EF6 无法将分离的实体附加到上下文