何时在JPA中使用EntityManager.find()与EntityManager.get

我遇到了一种情况(我认为这很奇怪,但可能很正常),在这种情况下,我使用EntityManager.getReference(LObj.getClass(),LObj.getId())获取数据库实体,然后将返回的对象传递给保留在另一个表中。


所以基本上流程是这样的:


TFacade类{


  createT(FObj,AObj){

    T TObj =新的T();

    TObj.setF(FObj);

    TObj.setA(AObj);

    ...

    EntityManager.persist(TObj);

    ...

    L LObj = A.getL();

    FObj.setL(LObj);

    FFacade.editF(FObj);

  }

}


@ TransactionAttributeType.REQUIRES_NEW

FFacade类{


  editF(FObj){

    L LObj = FObj.getL();

    LObj = EntityManager.getReference(LObj.getClass(),LObj.getId());

    ...

    EntityManager.merge(FObj);

    ...

    FLHFacade.create(FObj,LObj);

  }

}


@ TransactionAttributeType.REQUIRED

FLHFacade类{


  createFLH(FObj,LObj){

    FLH FLHObj =新的FLH();

    FLHObj.setF(FObj);

    FLHObj.setL(LObj);

    ....

    EntityManager.persist(FLHObj);

    ...

  }

}


我收到以下异常“ java.lang.IllegalArgumentException:未知实体:com.my.persistence.L $$ EnhancerByCGLIB $$ 3e7987d0”


经过一段时间的研究,我终于发现这是因为我在使用EntityManager.getReference()方法时遇到了上述异常,因为该方法正在返回代理。


这使我感到奇怪,什么时候才建议使用EntityManager.getReference()方法而不是EntityManager.find()方法?


如果EntityManager.getReference()无法找到要搜索的实体,该实体本身非常方便,则抛出EntityNotFoundException。如果EntityManager.find()方法找不到实体,则仅返回null。


关于事务边界,在我看来,您需要在将新发现的实体传递到新事务之前使用find()方法。如果使用getReference()方法,则可能会遇到类似于我的情况,但有上述例外。


森栏
浏览 1069回答 3
3回答

郎朗坤

正如我在本文中所解释的,假设您具有父Post实体和子实体,PostComment如下图所示:如果find在尝试设置@ManyToOne post关联时致电:PostComment comment = new PostComment();comment.setReview("Just awesome!");Post post = entityManager.find(Post.class, 1L);comment.setPost(post);entityManager.persist(comment);Hibernate将执行以下语句:SELECT p.id AS id1_0_0_,       p.title AS title2_0_0_FROM   post pWHERE p.id = 1INSERT INTO post_comment (post_id, review, id)VALUES (1, 'Just awesome!', 1)这次SELECT查询没有用,因为我们不需要获取Post实体。我们只想设置基础的post_id外键列。现在,如果您使用getReference:PostComment comment = new PostComment();comment.setReview("Just awesome!");Post post = entityManager.getReference(Post.class, 1L);comment.setPost(post);entityManager.persist(comment);这次,Hibernate将仅发出INSERT语句:INSERT INTO post_comment (post_id, review, id)VALUES (1, 'Just awesome!', 1)与不同find,getReference只会返回仅设置了标识符的实体Proxy。如果您访问代理,则只要EntityManager仍处于打开状态,就会触发关联的SQL语句。但是,在这种情况下,我们不需要访问实体代理。我们只希望将外键传播到基础表记录,因此对于此用例而言,加载代理就足够了。加载代理时,需要注意的是,如果在关闭EntityManager后尝试访问代理引用,则可能引发LazyInitializationException。

交互式爱情

这使我感到奇怪,什么时候才建议使用EntityManager.getReference()方法而不是EntityManager.find()方法?EntityManager.getReference()这确实是一种容易出错的方法,而且实际上很少有客户端代码需要使用它的情况。就个人而言,我从来不需要使用它。EntityManager.getReference()和EntityManager.find():在开销方面没有差异我不同意接受的答案,尤其是:如果我调用find方法,那么JPA提供程序将在后台调用SELECT NAME, AGE FROM PERSON WHERE PERSON_ID = ?UPDATE PERSON SET AGE = ? WHERE PERSON_ID = ?如果我调用getReference方法,那么JPA提供程序将在后台调用UPDATE PERSON SET AGE = ? WHERE PERSON_ID = ?这不是我在Hibernate 5中得到的行为,而javadoc getReference()却没有这样说:获取一个实例,其状态可能会延迟获取。如果请求的实例在数据库中不存在,则在首次访问实例状态时将引发EntityNotFoundException。(调用getReference时,允许持久性提供程序运行时引发EntityNotFoundException。)应用程序不应期望实例状态在分离后可用,除非在打开实体管理器时由应用程序访问了实例状态。EntityManager.getReference() 在两种情况下不使用查询来检索实体:1)如果实体存储在Persistence上下文中,那就是第一级缓存。并且此行为并非特定于EntityManager.getReference(),EntityManager.find()如果实体存储在Persistence上下文中, 还将节省查询以检索实体。您可以使用任何示例检查第一点。您还可以依赖实际的Hibernate实现。确实,EntityManager.getReference()依靠类的createProxyIfNecessary()方法org.hibernate.event.internal.DefaultLoadEventListener来加载实体。这是它的实现:private Object createProxyIfNecessary(        final LoadEvent event,        final EntityPersister persister,        final EntityKey keyToLoad,        final LoadEventListener.LoadType options,        final PersistenceContext persistenceContext) {    Object existing = persistenceContext.getEntity( keyToLoad );    if ( existing != null ) {        // return existing object or initialized proxy (unless deleted)        if ( traceEnabled ) {            LOG.trace( "Entity found in session cache" );        }        if ( options.isCheckDeleted() ) {            EntityEntry entry = persistenceContext.getEntry( existing );            Status status = entry.getStatus();            if ( status == Status.DELETED || status == Status.GONE ) {                return null;            }        }        return existing;    }    if ( traceEnabled ) {        LOG.trace( "Creating new proxy for entity" );    }    // return new uninitialized proxy    Object proxy = persister.createProxy( event.getEntityId(), event.getSession() );    persistenceContext.getBatchFetchQueue().addBatchLoadableEntityKey( keyToLoad );    persistenceContext.addProxy( keyToLoad, proxy );    return proxy;}有趣的部分是:Object existing = persistenceContext.getEntity( keyToLoad );2)如果我们没有有效地操纵实体,则回显懒惰地获取的Javadoc。实际上,为了确保实体的有效加载,需要在其上调用方法。那么,这种收益是否与我们想加载一个实体而不需要使用它的情况有关?在应用程序框架中,这种需求确实很少见,此外,getReference()如果您阅读下一部分,其行为也很容易引起误解。为什么比EntityManager.getReference()更偏爱EntityManager.find()在开销方面,getReference()并不比find()上一点更好。那么为什么要使用一个或另一个呢?调用getReference()可能会返回延迟获取的实体。在这里,延迟获取不是指实体的关系,而是指实体本身。这意味着,如果我们调用getReference()然后关闭Persistence上下文,则该实体可能永远不会加载,因此结果实际上是不可预测的。例如,如果代理对象已序列化,则可以获取null引用作为序列化结果,或者如果在代理对象上调用方法,LazyInitializationException则会引发诸如之类的异常。这意味着抛出该异常是用于处理数据库中不存在的实例的EntityNotFoundException主要原因,getReference()因为在实体不存在时可能永远不会执行错误情况。EntityManager.find()EntityNotFoundException如果找不到该实体,则不会抛出该异常。它的行为既简单又清晰。您永远不会感到惊讶,因为它总是返回一个已加载的实体或null(如果未找到该实体),但永远不会返回未有效加载的代理形式的实体。因此EntityManager.find(),在大多数情况下都应受到青睐。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java