【问题标题】:JPA EntityManager: 'find' vs. 'createQuery' and 'getResultList'JPA EntityManager:“find”与“createQuery”和“getResultList”
【发布时间】:2016-12-13 12:14:27
【问题描述】:

我正在开发一个使用 JPA(不是 JPA-2)的遗留代码库,并且在 DAO 实现类中遇到了以下方法来通过 ID 检索单个实体(这也是它的主键):

public EmailTemplate findEmailTemplateById(long id) {

    LOG.debug("Entering findEmailTemplateById(id='" + id + "')");
    // Construct JPQL query       
    String queryString = "SELECT a FROM EmailTemplate a " +
            "WHERE templateId = :templateId";
    Query query = entityManager.createQuery(queryString);

    query.setParameter("templateId", id);

    LOG.debug("Using query " + queryString);

    List<EmailTemplate> resultList = query.getResultList();

    LOG.debug("Exiting findEmailTemplateByName(id='" + id + "') results size " + resultList.size() + " ( returns null if 0 )");

    if (resultList.isEmpty() || resultList.size() == 0) {
        return null;
    } else {
        return resultList.get(0);
    }

}

我现在需要为不同的实体编写一个类似的 DAO 类,而我通过主键查找实体的方法看起来要简单得多! :

@Override
public EmailTemplateEdit findEmailTemplateEditById(long id) {
    LOG.debug("Entering findEmailTemplateEditById(id={})", id);
    return entityManager.find(EmailTemplateEdit.class, id);
}

原作者不在问,所以我想知道是否有人可以就他构建 JPQL 查询而不是简单地使用EntityManager#find(Class&lt;T&gt; entityClass, Object primaryKey) 提出原因?

find 方法的 javadoc 说:

如果实体实例包含在持久化上下文中,它是 从那里返回。

这表明某种形式的缓存和/或延迟写入。 createQuerygetResultList 方法的 javadoc 没有这样说。

我不知道此应用程序中有任何会妨碍缓存的业务或技术要求,或者因过时实体或类似情况而导致的任何问题。如果可用,我将与项目团队的其他成员一起检查这些内容,但我只是想征求 SO 社区的意见,看看是否有其他原因导致构造和执行查询而不是简单地使用 find

(我见过这个:When use createQuery() and find() methods of EntityManager?。虽然它回答了这个问题:createQuery 和 find 之间的区别,但它没有在通过主键查找实体的上下文中回答它)

更新了更多信息

从查看原始 DAO 类中的其他方法来看,似乎已经有意/有意识地决定不利用 JPA 托管对象。如上所述,通过主键查找的方法使用 JPQL 查询。删除实体的方法也使用 JPQL 查询。更新实体的方法会复制传入的实体对象并使用该副本调用EntityManager#merge(因此该副本是托管对象,但从未使用或从该方法返回)
奇怪....

【问题讨论】:

  • 听起来以前的开发人员不知道 JPA 的真正工作原理,因此做了一些变通方法。我自己也遇到过这种情况。
  • 如果您在下面查看我关于持久性上下文的答案,我可以补充说,合并是将分离的实体添加到持久性上下文的操作。大多数人认为它的行为就像坚持,但有一个关键的细节。当您调用 merge 时,参数仍然是一个分离实体,返回值是一个托管实体(这是因为持久性上下文中的每个实体只能由一个 java 对象表示,如果分离实体已加载到上下文中,则它是已经是托管实体,因此返回,并且将是与要合并的参数不同的实例)

标签: java entity-framework hibernate jpa


【解决方案1】:

简短的回答,find 和 select 查询没有区别。

您的问题表明您并不完全熟悉 EntityManager 和 Persistence 上下文是什么。 EntityManager 实现不需要是线程安全的。如果 EntityManager 由 Spring 或 EJB 容器注入,它是线程安全的(因为它是线程本地代理),如果它是应用程序管理的(您通过调用 EntityManagerFactory.createEntityManager() 创建它,它不是线程安全的,而且你不能将它存储在一个变量中,但必须每次都创建一个新的。

持久性上下文是实体所在的地方,每当您创建一个新的 EntityManager 时,您都会获得一个新的持久性上下文(此规则有例外)。当您持久化实体或从数据库加载现有实体(使用查找或查询)时,它将由持久化上下文管理。当您提交事务时,JPA 会运行由 Persistence 上下文管理的所有实体,并检查实体的状态以找出应将哪些查询发送到数据库。

PersistenceContext 可以看作是数据库之上的一级缓存。它的生命周期很短,通常不会比交易长。如果您对多个事务重复使用相同的 entityManager,则大小可能会随着加载更多数据而增长,这很糟糕,因为每个事务都必须在持久化上下文中运行所有实体。

【讨论】:

  • 谢谢,是的,正如你所发现的,我对 JPA 不是 100% 熟悉 :) 你的回答完美地解释了。至于为什么它最初是用 createQuery 等编写的,我现在已经和最初的开发人员谈过了,这与一些无法正确使用 find 或 Criteria 获取查询的监控工具有关,所以他告诉我用 createQuery 完成所有操作和 getResultList
猜你喜欢
  • 2016-05-02
  • 2013-03-20
  • 1970-01-01
  • 2011-03-08
  • 2012-06-01
  • 2012-04-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多