Types of JPA Queries – JPA查询的类型

最后修改: 2019年 4月 5日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’ll discuss the different types of JPA queries. Moreover, we’ll focus on comparing the differences between them and expanding on each one’s pros and cons.

在本教程中,我们将讨论不同类型的JPA>查询。此外,我们将重点比较它们之间的差异,并对每一种的优点和缺点进行扩展。

2. Setup

2.设置

Firstly, let’s define the UserEntity class we’ll use for all examples in this article:

首先,让我们定义UserEntity类,我们将在本文的所有示例中使用。

@Table(name = "users")
@Entity
public class UserEntity {

    @Id
    private Long id;
    private String name;
    //Standard constructor, getters and setters.

}

There are three basic types of JPA Queries:

JPA查询有三种基本类型:

  • Query, written in Java Persistence Query Language (JPQL) syntax
  • NativeQuery, written in plain SQL syntax
  • Criteria API Query, constructed programmatically via different methods

Let’s explore them.

让我们来探讨一下它们。

3. Query

3.查询

A Query is similar in syntax to SQL, and it’s generally used to perform CRUD operations:

查询在语法上与SQL类似,它通常用于执行CRUD操作:

public UserEntity getUserByIdWithPlainQuery(Long id) {
    Query jpqlQuery = getEntityManager().createQuery("SELECT u FROM UserEntity u WHERE u.id=:id");
    jpqlQuery.setParameter("id", id);
    return (UserEntity) jpqlQuery.getSingleResult();
}

This Query retrieves the matching record from the users table and also maps it to the UserEntity object.

这个Queryusers表中检索匹配的记录,同时将其映射到UserEntity对象。

There are two additional Query sub-types:

有两个额外的Query子类型。

  • TypedQuery
  • NamedQuery

Let’s see them in action.

让我们看看他们的行动。

3.1. TypedQuery

3.1.TypedQuery

We need to pay attention to the return statement in our previous example. JPA can’t deduce what the Query result type will be, and, as a result, we have to cast.

我们需要注意我们前面例子中的return语句。JPA无法推断出Query的结果类型,因此,我们必须进行转换。

But, JPA provides a special Query sub-type known as a TypedQuery. This is always preferred if we know our Query result type beforehand. Additionally, it makes our code much more reliable and easier to test.

但是,JPA提供了一个特殊的Query子类型,被称为TypedQuery。如果我们事先知道我们的Query结果类型,这始终是首选。此外,它使我们的代码更可靠,更容易测试。

Let’s see a TypedQuery alternative, compared to our first example:

让我们看看一个TypedQuery替代品,与我们的第一个例子相比。

public UserEntity getUserByIdWithTypedQuery(Long id) {
    TypedQuery<UserEntity> typedQuery
      = getEntityManager().createQuery("SELECT u FROM UserEntity u WHERE u.id=:id", UserEntity.class);
    typedQuery.setParameter("id", id);
    return typedQuery.getSingleResult();
}

This way, we get stronger typing for free, avoiding possible casting exceptions down the road.

这样,我们就能免费获得更强的打字能力,避免了接下来可能出现的铸造异常。

3.2. NamedQuery

3.2.NamedQuery

While we can dynamically define a Query on specific methods, they can eventually grow into a hard-to-maintain codebase. What if we could keep general usage queries in one centralized, easy-to-read place?

虽然我们可以在特定的方法上动态地定义一个Query,但它们最终会发展成一个难以维护的代码库。如果我们能把一般的使用查询放在一个集中的、容易阅读的地方,会怎么样呢?

JPA’s also got us covered on this with another Query sub-type known as a NamedQuery.

JPA还为我们提供了另一种Query子类型,称为NamedQuery

We can define NamedQueries in orm.xml or a properties file.

我们可以在orm.xml或属性文件中定义NamedQueries

Also, we can define NamedQuery on the Entity class itself, providing a centralized, quick and easy way to read and find an Entity‘s related queries.

此外,我们还可以在Entity类本身上定义NamedQuery,为读取和查找Entity的相关查询提供一种集中、快速和简单的方式。

All NamedQueries must have a unique name.

所有NamedQueries必须有一个唯一的名字。

Let’s see how we can add a NamedQuery to our UserEntity class:

让我们看看如何将NamedQuery添加到我们的UserEntity类。

@Table(name = "users")
@Entity
@NamedQuery(name = "UserEntity.findByUserId", query = "SELECT u FROM UserEntity u WHERE u.id=:userId")
public class UserEntity {

    @Id
    private Long id;
    private String name;
    //Standard constructor, getters and setters.

}

The @NamedQuery annotation has to be grouped inside a @NamedQueries annotation if we’re using Java before version 8. From Java 8 forward, we can simply repeat the @NamedQuery annotation at our Entity class.

如果我们使用的是版本8之前的Java,那么@NamedQuery注解必须被分组在@NamedQueries注解内。从Java 8开始,我们可以简单地在我们的Entity类中重复@NamedQuery注解。

Using a NamedQuery is very simple:

使用NamedQuery是非常简单的。

public UserEntity getUserByIdWithNamedQuery(Long id) {
    Query namedQuery = getEntityManager().createNamedQuery("UserEntity.findByUserId");
    namedQuery.setParameter("userId", id);
    return (UserEntity) namedQuery.getSingleResult();
}

4. NativeQuery

4.NativeQuery

A NativeQuery is simply an SQL query. These allow us to unleash the full power of our database, as we can use proprietary features not available in JPQL-restricted syntax.

一个NativeQuery只是一个SQL查询。这些允许我们释放我们数据库的全部力量,因为我们可以使用JPQL限制的语法中没有的专有功能。

This comes at a cost. We lose the database portability of our application with NativeQuery because our JPA provider can’t abstract specific details from the database implementation or vendor anymore.

这是有代价的。我们失去了使用NativeQuery的应用程序的数据库可移植性,因为我们的JPA提供者不能再从数据库实现或供应商那里抽象出具体细节。

Let’s see how to use a NativeQuery that yields the same results as our previous examples:

让我们看看如何使用一个NativeQuery,产生与我们之前的例子相同的结果。

public UserEntity getUserByIdWithNativeQuery(Long id) {
    Query nativeQuery
      = getEntityManager().createNativeQuery("SELECT * FROM users WHERE id=:userId", UserEntity.class);
    nativeQuery.setParameter("userId", id);
    return (UserEntity) nativeQuery.getSingleResult();
}

We must always consider if a NativeQuery is the only option. Most of the time, a good JPQL Query can fulfill our needs and most importantly, maintain a level of abstraction from the actual database implementation.

我们必须始终考虑NativeQuery是否是唯一的选择。大多数情况下,一个好的JPQL Query可以满足我们的需求,最重要的是,可以从实际的数据库实现中保持一个抽象的水平。

Using NativeQuery doesn’t necessarily mean locking ourselves to one specific database vendor. After all, if our queries don’t use proprietary SQL commands and are using only a standard SQL syntax, switching providers should not be an issue.

使用NativeQuery并不一定意味着将我们锁定在一个特定的数据库供应商。毕竟,如果我们的查询不使用专有的SQL命令,只使用标准的SQL语法,切换供应商不应该是一个问题。

5. Query, NamedQuery, and NativeQuery

5.Query, NamedQuery, 和NativeQuery

So far, we’ve learned about Query, NamedQuery, and NativeQuery.

到目前为止,我们已经了解了QueryNamedQueryNativeQuery

Now, let’s revisit them quickly and summarize their pros and cons.

现在,让我们快速重温一下它们,总结一下它们的优点和缺点。

5.1. Query

5.1. 查询

We can create a query using entityManager.createQuery(queryString).

我们可以使用entityManager.createQuery(queryString)创建一个查询。

Next, let’s explore the pros and cons of Query:

接下来,让我们探讨一下Query的优点和缺点。

Pros:

优点。

  • When we create a query using EntityManager, we can build dynamic query strings
  • Queries are written in JPQL, so they’re portable

Cons:

弊端。

  • For a dynamic query, it may be compiled into a native SQL statement multiple times depending on the query plan cache
  • The queries may scatter into various Java classes and they’re mixed in with Java code. Therefore, it could be difficult to maintain if a project contains many queries

5.2. NamedQuery

5.2 NamedQuery

Once a NamedQuery has been defined, we can reference it using the EntityManager:

一旦定义了NamedQuery,我们就可以使用EntityManager引用它。

entityManager.createNamedQuery(queryName);

Now, let’s look at the advantages and disadvantages of NamedQueries:

现在,让我们看看NamedQueries的优点和缺点。

Pros:

优点。

  • NamedQueries are compiled and validated when the persistence unit is loaded. That is to say, they’re compiled only once
  • We can centralize NamedQueries to make them easier to maintain – for example, in orm.xml, in properties files, or on @Entity classes

Cons:

弊端。

  • NamedQueries are always static
  • NamedQueries can be referenced in Spring Data JPA repositories. However, dynamic sorting is not supported

5.3. NativeQuery

5.3.NativeQuery

We can create a NativeQuery using EntityManager:

我们可以使用EntityManager创建一个NativeQuery

entityManager.createNativeQuery(sqlStmt);

Depending on the result mapping, we can also pass the second parameter to the method, such as an Entity class, as we’ve seen in a previous example.

根据结果映射,我们也可以将第二个参数传递给该方法,比如一个Entity类,我们在之前的例子中已经看到了。

NativeQueries have pros and cons, too. Let’s look at them quickly:

NativeQueries也有优点和缺点。让我们快速地看一下它们。

Pros:

优点。

  • As our queries get complex, sometimes the JPA-generated SQL statements aren’t the most optimized. In this case, we can use NativeQueries to make the queries more efficient
  • NativeQueries allow us to use database vendor-specific features. Sometimes, those features can give our queries better performance

Cons:

弊端。

  • Vendor-specific features can bring convenience and better performance, but we pay for that benefit by losing the portability from one database to another

6. Criteria API Query

6.标准 API查询

Criteria API queries are programmatically-built, type-safe queries – somewhat similar to JPQL queries in syntax:

Criteria API查询是通过编程建立的、类型安全的查询–在语法上有点类似于JPQL查询:

public UserEntity getUserByIdWithCriteriaQuery(Long id) {
    CriteriaBuilder criteriaBuilder = getEntityManager().getCriteriaBuilder();
    CriteriaQuery<UserEntity> criteriaQuery = criteriaBuilder.createQuery(UserEntity.class);
    Root<UserEntity> userRoot = criteriaQuery.from(UserEntity.class);
    UserEntity queryResult = getEntityManager().createQuery(criteriaQuery.select(userRoot)
      .where(criteriaBuilder.equal(userRoot.get("id"), id)))
      .getSingleResult();
    return queryResult;
}

It can be daunting to use Criteria API queries first-hand, but they can be a great choice when we need to add dynamic query elements or when coupled with the JPA Metamodel.

亲手使用CriteriaAPI查询可能令人生畏,但当我们需要添加动态查询元素或与JPA Metamodel.结合时,它们可能是一个不错的选择。

7. Conclusion

7.结论

In this quick article, we learned what JPA Queries are, along with their usage.

在这篇快速的文章中,我们了解了什么是JPA查询,以及它们的用法。

JPA Queries are a great way to abstract our business logic from our data access layer as we can rely on JPQL syntax and let our JPA provider of choice handle the Query translation.

JPA查询是将我们的业务逻辑从数据访问层中抽象出来的好方法,因为我们可以依靠JPQL语法,让我们选择的JPA提供商处理查询翻译。

All code presented in this article is available over on GitHub.

本文介绍的所有代码都可以在GitHub上找到。