JPA 为啥使用 createNamedQuery

Posted

技术标签:

【中文标题】JPA 为啥使用 createNamedQuery【英文标题】:JPA why use createNamedQueryJPA 为什么使用 createNamedQuery 【发布时间】:2010-10-24 09:10:58 【问题描述】:

我正在将我的 DAO 层从使用 Hibernate API 更改为使用纯 JPA API 实现。看起来推荐的方法是使用实​​体管理器中的 createNamedQuery 。命名查询存储在模型/实体类的注释中。这对我来说没有意义。为什么要在模型对象中定义 JPA 查询,但在 DAO 中使用它们。仅在 DAO 本身中使用 createQuery 并在 DAO 中定义查询,甚至只是在 DAO 本身中定义命名查询,难道不是更有意义吗?

对于那些使用 JPA API 实现 DAO 层的人,您是如何定义查询的?

【问题讨论】:

【参考方案1】:

我使用命名查询。

这样做有两个原因:

    它将它们放在更中心的位置,而不是分散在随机调用 createQuery() 的代码中;和 构建过程可以验证查询(非常有用)。

【讨论】:

为什么中心位置应该是模型对象而不是实际使用它的 DAO? 我忘了提到这些命名查询我通常放在实体上。如果查询跨越多个实体,您只需选择最相关的一个。【参考方案2】:

您可以查看Spring Data JPA。它允许您简单地定义一个接口并执行查询,而无需手动执行。

实体:

@Entity
@NamedQuery(id="User.findByLastname" query="from User u where u.lastname = ?1")
public class User implements Persistable<Long> 

  @Id
  private Long id;
  private String username;
  private String lastname;
  private int age;

存储库:

public interface UserRepository extends CrudRepository<User, Long> 

  // Will trigger the NamedQuery due to a naming convention
  List<User> findByLastname(String lastname);

  // Will create a query from the methodname
  // from User u where u.username = ?
  User findByUsername(String username);

  // Uses query annotated to the finder method in case you
  // don't want to pollute entity with query info
  @Query("from User u where u.age > ?1")
  List<User> findByAgeGreaterThan(int age);

设置:

EntityManager em = Persistence.getEntityManagerFactory().createEntityManager();
JpaRepositoryFactory factory = new JpaRepositoryFactory(em);

UserRepository repository = factory.getRepository(UserRepository.class);

如您所见,您可以选择不同的方法来从方法中派生要执行的查询。虽然直接从方法名称派生它对于简单查询是可行的,但您可能会在 @NamedQuery(JPA 标准)或 @Query(Spring Data JPA 注释)之间进行选择,具体取决于您对标准的坚持程度。

Spring Data JPA 为您提供数据访问层实现的其他各个方面的支持,允许为方法提供自定义实现并与 Spring 很好地集成。

【讨论】:

问题是关于 JPA 解决方案,而不是使用哪个持久性 API。 问题是“对于那些使用 JPA API 实现了 DAO 层的人,您是如何定义查询的?”我只是想指出一个可行的解决方案。 Hades 为使用普通的 EntityManager 提供了一种更易于访问的替代方案,并且基于普通的 JPA,所以我真的不明白你的意思。 我认为奥利弗的回答和推理是有道理的。我个人认为他的Hades例子非常有趣,并且确实回答了这个问题。实际上,我想我会对框架有更深入的了解...... :)【参考方案3】:

我的体验与 cletus 完全相反——我发现没有任何好处,而且使用起来也很尴尬。琐碎查询在哪里定义没有区别,但非琐碎查询通常很难与任何单个实体关联,但使用面向业务的方法很容易。

如果您在 DAO 中使用或多或少复杂的基础架构(为了重用和一致性),那么命名查询的使用往往会使实现和可读性变得复杂,并且没有明显的好处。

通过构建过程验证查询听起来很有趣 - 我想知道更多它的真正含义......我的查询几乎没有出错的余地,因为每个 DAO 方法都经过了尽可能多的单元测试。

【讨论】:

我也觉得他们很尴尬,这就是我提出这些问题的原因。有一个查询的中心位置对于可维护性是有意义的,但是我不知道将它们放在模型对象中是否有意义。如果我要维护一个 DAO,我首先会转到需要更新的 DAO 方法,现在我必须转到模型对象才能开始查看查询。 验证其实相当不错。我使用 eclipse 进行开发,当我写了几个查询时,我关闭了 AS 关键字,并且在构建过程中收到了错误。 我应该补充一点,我通常不会将它们用于琐碎的查询(例如“select p from Party p where name = :name”),只是更复杂的查询。【参考方案4】:

不要只在任何地方使用命名查询,在某些情况下它们是不合适的,例如当它很少使用的查询时,如果根据需要构建它可能会更有效。

命名查询在复杂且频繁执行时更有意义。

[更新]

您可以改为在映射时编写命名查询。

【讨论】:

为什么执行查询的频率使它更适合成为命名查询。 命名查询被解析一次并永远使用。因此,如果我们编写了错误的查询,它会立即引发异常。除非缓存,否则您在代码中构建的查询会一直被解析。 顺便说一下,给定的链接建议相同。 oracle.com/technology/products/ias/toplink/doc/1013/main/_html/…【参考方案5】:

拥抱和的力量 :) 如果您有一个有意义的查询可以放入模型中,那么就这样做。如果你不这样做,不要。 最好问“你为什么用 JPS 编写 DAO?”如果答案是“从数据库中隔离我的代码”。这是由实现 JPA 的库完成的。如果答案是“将我的代码与我坚持事物的方式的变化隔离开来”,那么 JPA 通过允许您拥有不同的实现来做到这一点。 我将查询对象用于复杂查询,并且在可能的情况下仍然使用命名查询,而且我喜欢命名查询被编译的事实,因此我可以更快地发现它们中的错误。我没有 DAO 层。

【讨论】:

以上是关于JPA 为啥使用 createNamedQuery的主要内容,如果未能解决你的问题,请参考以下文章

用于读取的 JPA 锁定实体

使用jpa报No query defined for that name错误

JPA:仅当结果集不为空时才缓存查询

JPA NamedQuery - 当传递的字段为空时忽略行

为啥在看到 PersistentBag 的实体中使用 JPA 列出(@OneToMany)

为啥 JPA 默认使用 FetchType EAGER 来处理 @ManyToOne 关系