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

什么是分离通过EntityManager获取的特定JPA实体Bean最简单的方法。 或者,我可以有一个查询返回分离的对象在第一个地方,所以他们将本质上作为“只读”?

我想这样做的原因是因为我想修改bean中的数据 – 只在我的应用程序中,但没有永久保存到数据库。 在我的程序中,我最终不得不在EntityManager上调用flush(),这会将附加实体的所有更改保存到解除数据库,但是我想排除特定的对象。

不幸的是,在当前的JPA实现AFAIR中,没有办法从实体pipe理器中断开一个对象。

EntityManager.clear()将断开所有的JPA对象,所以如果你有其他的对象,你可能不想保持连接。

所以你最好的select是克隆对象并将克隆传递给改变对象的代码。 由于原始的和不可变的对象字段由默认的克隆机制以适当的方式来处理,所以你不必编写大量的pipe道代码(除了深度克隆你可能拥有的任何聚合结构之外)。

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

我正在用JPA开发我的第一个系统。 不幸的是,当这个系统几乎完成时,我正面临着这个问题。

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

在Hibernate中,你可以使用'session.evict(object)'从会话中删除一个对象。 在JPA 2.0中, 现在正在草稿中 ,有一个“EntityManager.detach(object)”方法将一个对象从持久化上下文中分离出来。

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

如果您需要从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提供程序,这将会中断,但我认为这最好是尝试做一个深层的副本。

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

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

如果使用EclipseLink你也可以select,

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

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

如果bean中没有太多的属性,你可以创build一个新的实例,并从持久化bean中手动设置所有的属性。

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

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

然后:

 Thing newBean = new Thing(oldBean); 

Mauricio Kanada,感谢您的build议,方法evict()很好。 我使用SEAM的JPA,内build支持JPA实体pipe理器,并有可能访问Hibernate的委托会话,因此这种方法“驱逐”。

非常感谢,Zmicer

这是快速和肮脏的,但你也可以序列化和反序列化的对象。

由于我使用的是SEAM和JPA 1.0,而且我的系统有一个需要logging所有字段更改的function,如果需要logging的实体的相同字段,我创build了一个值对象或数据传输对象。 新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(); } } } } 

在JPA 1.0中(使用EclipseLink进行testing),您可以检索事务以外的实体。 例如,对于容器pipe理的事务,你可以这样做:

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

处理类似的情况我已经创build了一个DTO对象来扩展持久化实体对象,如下所示:

 class MyEntity { public static class MyEntityDO extends MyEntity {} } 

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

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

如果你到达这里是因为你实际上想要通过一个远程边界的实体,那么你只是把一些代码欺骗的Hibernazi。

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

可复制不会工作,因为它实际上复制PersistantBag。

忘记使用可序列化和bytearraystream和pipe道stream。 创build线程以避免死锁杀死整个概念。

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