分离通过 EntityManager 获取的特定 JPA 实体 Bean 的最简单方法是什么。或者,我是否可以让查询首先返回分离的对象,以便它们基本上充当“只读”?
我想这样做的原因是因为我想修改 bean 中的数据 - 仅在我的应用程序中,但从来没有将它持久化到数据库中。在我的程序中,我最终必须在 EntityManager 上调用 flush(),这会将所有从附加实体的更改持久化到基础数据库,但我想排除特定对象。
(回答可能为时已晚,但对其他人有用)
我现在正在使用 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 提供程序,这会中断,但我认为这最好是尝试进行深层复制。
不幸的是,在当前的 JPA 实现 AFAIR 中,无法断开一个对象与实体管理器的连接。
EntityManager.clear() 将断开所有JPA 对象,因此在所有情况下这可能不是一个合适的解决方案,如果您确实计划保持连接的其他对象。
所以最好的办法是克隆对象并将克隆传递给更改对象的代码。由于默认克隆机制以适当的方式处理原始和不可变对象字段,因此您不必编写大量管道代码(除了深度克隆您可能拥有的任何聚合结构)。
据我所知,唯一直接的方法是:
如果使用EclipseLink
你也有选项,
使用查询提示eclipselink.maintain-cache"="false
- 所有返回的对象都将被分离。
使用EclipseLink
JpaEntityManager
copy()
API 将对象复制到所需的深度。
如果 bean 中没有太多属性,您可能只需创建一个新实例并从持久化 bean 手动设置其所有属性。
这可以实现为复制构造函数,例如:
public Thing(Thing oldBean) {
this.setPropertyOne(oldBean.getPropertyOne());
// and so on
}
然后:
Thing newBean = new Thing(oldBean);
这既快又脏,但您也可以序列化和反序列化对象。
由于我使用的是 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();
}
}
}
}
在 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);
}
处理类似的情况,我创建了一个扩展持久实体对象的 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
如果你到达这里是因为你真的想通过一个远程边界传递一个实体,那么你只需输入一些代码来愚弄冬眠者。
for(RssItem i : result.getChannel().getItem()){
}
Cloneable 不起作用,因为它实际上复制了 PersistantBag。
并且忘记使用可序列化和字节数组流和管道流。创建线程以避免死锁会扼杀整个概念。
我认为有一种方法可以通过调用这个来从 EntityManager 中逐出单个实体
EntityManagerFactory emf;
emf.getCache().evict(Entity);
这将从缓存中删除特定实体。
我正在使用 entityManager.detach(returnObject); 这对我有用。
如果实体的主键没有更改,我认为您也可以使用方法 EntityManager.refresh(Object o) 。此方法将恢复实体的原始状态。