简体   繁体   English

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

[英]Detach an entity from JPA/EJB3 persistence context

What would be the easiest way to detach a specific JPA Entity Bean that was acquired through an EntityManager.分离通过 EntityManager 获取的特定 JPA 实体 Bean 的最简单方法是什么。 Alternatively, could I have a query return detached objects in the first place so they would essentially act as 'read only'?或者,我是否可以首先让查询返回分离的对象,以便它们基本上充当“只读”?

The reason why I want to do this is becuase I want to modify the data within the bean - with in my application only, but not ever have it persisted to the database.我想要这样做的原因是因为我想修改 bean 中的数据 - 仅在我的应用程序中使用,但从未将其持久化到数据库中。 In my program, I eventually have to call flush() on the EntityManager, which would persist all changes from attached entities to the underyling database, but I want to exclude specific objects.在我的程序中,我最终必须在 EntityManager 上调用 flush(),这会将所有更改从附加实体保存到底层数据库,但我想排除特定对象。

(may be too late to answer, but can be useful for others) (回答可能为时已晚,但对其他人有用)

I'm developing my first system with JPA right now.我现在正在用 JPA 开发我的第一个系统。 Unfortunately I'm faced with this problem when this system is almost complete.不幸的是,当这个系统几乎完成时,我遇到了这个问题。

Simply put.简单的说。 Use Hibernate, or wait for JPA 2.0.使用 Hibernate,或等待 JPA 2.0。

In Hibernate, you can use 'session.evict(object)' to remove one object from session.在 Hibernate 中,您可以使用 'session.evict(object)' 从会话中删除一个对象。 In JPA 2.0, in draft right now , there is the 'EntityManager.detach(object)' method to detach one object from persistence context.JPA 2.0 中, 现在的草案中 有 'EntityManager.detach(object)' 方法可以从持久化上下文中分离一个对象。

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

If you need to detach an object from the EntityManager and you are using Hibernate as your underlying ORM layer you can get access to the Hibernate Session object and use the Session.evict(Object) method that Mauricio Kanada mentioned above.如果您需要从 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);
}

Of course this would break if you switched to another ORM provider but I think this is preferably to trying to make a deep copy.当然,如果您切换到另一个 ORM 提供程序,这会中断,但我认为这最好是尝试进行深度复制。

Unfortunately, there's no way to disconnect one object from the entity manager in the current JPA implementation, AFAIR.不幸的是,在当前的 JPA 实现 AFAIR 中,无法将一个对象与实体管理器断开连接。

EntityManager.clear() will disconnect all the JPA objects, so that might not be an appropriate solution in all the cases, if you have other objects you do plan to keep connected. EntityManager.clear() 将断开所有JPA 对象的连接,因此这可能不是所有情况下的合适解决方案,如果您确实计划保持连接其他对象。

So your best bet would be to clone the objects and pass the clones to the code that changes the objects.因此,最好的办法是克隆对象并将克隆传递给更改对象的代码。 Since primitive and immutable object fields are taken care of by the default cloning mechanism in a proper way, you won't have to write a lot of plumbing code (apart from deep cloning any aggregated structures you might have).由于原始和不可变对象字段由默认克隆机制以适当的方式处理,因此您不必编写大量管道代码(除了深度克隆您可能拥有的任何聚合结构)。

As far as I know, the only direct ways to do it are:据我所知,唯一直接的方法是:

  1. Commit the txn - Probably not a reasonable option提交交易 - 可能不是一个合理的选择
  2. Clear the Persistence Context - EntityManager.clear() - This is brutal, but would clear it out清除持久化上下文 - EntityManager.clear() - 这很残酷,但会清除它
  3. Copy the object - Most of the time your JPA objects are serializable, so this should be easy (if not particularly efficient).复制对象 - 大多数时候您的 JPA 对象是可序列化的,所以这应该很容易(如果不是特别有效的话)。

If using EclipseLink you also have the options,如果使用EclipseLink您还可以选择,

Use the Query hint, eclipselink.maintain-cache"="false - all returned objects will be detached.使用查询提示, eclipselink.maintain-cache"="false - 所有返回的对象都将被分离。

Use the EclipseLink JpaEntityManager copy() API to copy the object to the desired depth.使用EclipseLink JpaEntityManager copy() API 将对象复制到所需深度。

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

In JPA 1.0 (tested using EclipseLink) you could retrieve the entity outside of a transaction.在 JPA 1.0(使用 EclipseLink 测试)中,您可以在事务之外检索实体。 For example, with container managed transactions you could do:例如,使用容器管理的事务,您可以执行以下操作:

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

Do deal with a similar case I have created a DTO object that extends the persistent entity object as follows:处理类似的情况我创建了一个 DTO 对象,它扩展了持久实体对象,如下所示:

class MyEntity
{
   public static class MyEntityDO extends MyEntity {}

}

Finally, an scalar query will retrieve the desired non managed attributes:最后,标量查询将检索所需的非托管属性:

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

If there aren't too many properties in the bean, you might just create a new instance and set all of its properties manually from the persisted bean.如果 bean 中没有太多属性,您可能只需创建一个新实例并从持久化 bean 手动设置其所有属性。

This could be implemented as a copy constructor, for example:这可以实现为复制构造函数,例如:

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

Then:然后:

Thing newBean = new Thing(oldBean);

Since I am using SEAM and JPA 1.0 and my system has a fuctinality that needs to log all fields changes, i have created an value object or data transfer object if same fields of the entity that needs to be logged.由于我使用的是 SEAM 和 JPA 1.0,并且我的系统具有需要记录所有字段更改的功能,如果需要记录实体的相同字段,我创建了一个值对象或数据传输对象。 The constructor of the new pojo is:新 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();
            }
        }
    }
}

I think there is a way to evict a single entity from EntityManager by calling this我认为有一种方法可以通过调用它来从 EntityManager 中驱逐单个实体

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

This will remove particular entity from cache.这将从缓存中删除特定实体。

If you get here because you actually want to pass an entity across a remote boundary then you just put some code in to fool the hibernazi.如果你来到这里是因为你真的想通过一个远程边界来传递一个实体,那么你只需输入一些代码来欺骗冬眠者。

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

Cloneable wont work because it actually copies the PersistantBag across. Cloneable 不起作用,因为它实际上复制了 PersistantBag。

And forget about using serializable and bytearray streams and piped streams.忘记使用可序列化和字节数组流以及管道流。 creating threads to avoid deadlocks kills the entire concept.创建线程来避免死锁会扼杀整个概念。

Im using entityManager.detach(returnObject);我使用 entityManager.detach(returnObject); which worked for me.这对我有用。

I think you can also use method EntityManager.refresh(Object o) if primary key of the entity has not been changed.如果实体的主键未更改,我认为您也可以使用 EntityManager.refresh(Object o) 方法。 This method will restore original state of the entity.此方法将恢复实体的原始状态。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM