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

时间:2008-08-28 00:53:22

标签: java orm jpa

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

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

15 个答案:

答案 0 :(得分:56)

(可能为时已晚,但可能对其他人有用)

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

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

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

答案 1 :(得分:26)

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

答案 2 :(得分:19)

如果您需要从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提供商,这会破坏,但我认为这最好是尝试进行深层复制。

答案 3 :(得分:18)

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

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

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

答案 4 :(得分:3)

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

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

答案 5 :(得分:3)

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

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

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

答案 6 :(得分:1)

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

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

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

然后:

Thing newBean = new Thing(oldBean);

答案 7 :(得分:1)

这很快且很脏,但您也可以序列化和反序列化对象。

答案 8 :(得分:1)

由于我使用的是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();
            }
        }
    }
}

答案 9 :(得分:1)

在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);
}

答案 10 :(得分:1)

处理类似的情况我创建了一个扩展持久化实体对象的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

答案 11 :(得分:0)

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

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

克隆无法工作,因为它实际上复制了PersistantBag。

忘记使用可序列化和bytearray流和管道流。创建线程以避免死锁会破坏整个概念。

答案 12 :(得分:0)

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

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

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

答案 13 :(得分:0)

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

答案 14 :(得分:-1)

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