1. Overview
1.概述
In this tutorial, we’ll learn how to project entity properties using JPA and Hibernate.
在本教程中,我们将学习如何使用JPA和Hibernate投影实体属性。
2. The Entity
2.实体
First, let’s look at the entity we will be using throughout this article:
首先,让我们看看我们将在本文中使用的实体。
@Entity
public class Product {
@Id
private long id;
private String name;
private String description;
private String category;
private BigDecimal unitPrice;
// setters and getters
}
This is a simple entity class representing a product with various properties.
这是一个简单的实体类,代表一个具有各种属性的产品。
3. JPA Projections
3.JPA的预测
Though the JPA spec doesn’t mention projections explicitly, there are many cases where we find them in concept.
虽然JPA规范没有明确提到投影,但在很多情况下,我们会在概念中发现它们。
Typically, a JPQL query has a candidate entity class. The query, on execution, creates objects of the candidate class — populating all their properties using the data retrieved.
通常情况下,JPQL查询有一个候选实体类。在执行时,查询会创建候选类的对象–使用检索到的数据填充其所有属性。
But, it’s possible to retrieve a subset of the properties of the entity, or, that is, a projection of column data.
但是,我们可以检索实体属性的一个子集,或者说,是列数据的投影。
Apart from column data, we can also project the results of grouping functions.
除了列数据之外,我们还可以预测分组函数的结果。
3.1. Single-Column Projections
3.1.单柱预测
Let’s suppose we want to list the names of all products. In JPQL, we can do this by including only the name in the select clause:
让我们假设我们想列出所有产品的名称。在JPQL中,我们可以通过在select子句中只包括name来做到这一点。
Query query = entityManager.createQuery("select name from Product");
List<Object> resultList = query.getResultList();
Or, we can do the same with CriteriaBuilder:
或者,我们可以用CriteriaBuilder做同样的事情。
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<String> query = builder.createQuery(String.class);
Root<Product> product = query.from(Product.class);
query.select(product.get("name"));
List<String> resultList = entityManager.createQuery(query).getResultList();
Because we are projecting a single column that happens to be of type String, we expect to get a list of Strings in the result. Hence, we’ve specified the candidate class as String in the createQuery() method.
因为我们正在投射一个恰好是String类型的单列,我们期望在结果中得到一个Strings列表。因此,我们在createQuery()方法中指定候选类为String。
Since we want to project on a single property, we’ve used the Query.select() method. What goes here is which property we want, so in our case, we’d use the name property from our Product entity.
由于我们想在一个单一的属性上进行预测,我们使用了Query.select()方法。这里需要的是我们想要的属性,所以在我们的案例中,我们将使用我们的Product 实体中的name 属性。
Now, let’s look at a sample output generated by the above two queries:
现在,让我们看一下上述两个查询产生的输出样本。
Product Name 1
Product Name 2
Product Name 3
Product Name 4
Note that if we’d used the id property in the projection instead of name, the query would have returned a list of Long objects.
请注意,如果我们在投影中使用id属性而不是name,查询将返回一个Long对象的列表。
3.2. Multi-Column Projections
3.2.多列投影
To project on multiple columns using JPQL, we only have to add all the required columns to the select clause:
要使用JPQL对多个列进行预测,我们只需要将所有需要的列添加到select子句中。
Query query = session.createQuery("select id, name, unitPrice from Product");
List resultList = query.getResultList();
But, when using a CriteriaBuilder, we’ll have to do things a bit differently:
但是,当使用CriteriaBuilder时,我们必须以一种不同的方式做事。
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<Object[]> query = builder.createQuery(Object[].class);
Root<Product> product = query.from(Product.class);
query.multiselect(product.get("id"), product.get("name"), product.get("unitPrice"));
List<Object[]> resultList = entityManager.createQuery(query).getResultList();
Here, we’ve used the method multiselect() instead of select(). Using this method, we can specify multiple items to be selected.
这里,我们使用了multiselect()方法,而不是select()。使用这个方法,我们可以指定多个项目被选中。
Another significant change is the use of Object[]. When we select multiple items, the query returns an object array with value for each item projected. This is the case with JPQL as well.
另一个重大变化是使用Object[]。当我们选择多个项目时,查询会返回一个对象数组,并为每个项目预测值。这也是JPQL的情况。
Let’s see what the data looks like when we print it:
让我们看看打印出来的数据是什么样子的。
[1, Product Name 1, 1.40]
[2, Product Name 2, 4.30]
[3, Product Name 3, 14.00]
[4, Product Name 4, 3.90]
As we can see, the returned data is a bit cumbersome to process. But, fortunately, we can get JPA to populate this data into a custom class.
我们可以看到,返回的数据处理起来有点麻烦。但是,幸运的是,我们可以让JPA来将这些数据填充到一个自定义类中。
Also, we can use CriteriaBuilder.tuple() or CriteriaBuilder.construct() to get the results as a list of Tuple objects or objects of a custom class respectively.
另外,我们可以使用CriteriaBuilder.tuple()或CriteriaBuilder.construct()来获取结果,分别作为Tuple对象或自定义类的对象列表。
3.3. Projecting Aggregate Functions
3.3.聚合函数的投影
Apart from column data, we might sometimes want to group the data and use aggregate functions, like count and average.
除了列数据,我们有时可能想对数据进行分组,并使用聚合函数,如count和average.。
Let’s say we want to find the number of products in each category. We can do this using the count() aggregate function in JPQL:
比方说,我们想找到每个类别中的产品数量。我们可以使用JPQL中的count()聚合函数来做这件事。
Query query = entityManager.createQuery("select p.category, count(p) from Product p group by p.category");
Or we can use CriteriaBuilder:
或者我们可以使用CriteriaBuilder。
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Object[]> query = builder.createQuery(Object[].class);
Root<Product> product = query.from(Product.class);
query.multiselect(product.get("category"), builder.count(product));
query.groupBy(product.get("category"));
Here, we’ve used CriteriaBuilder‘s count() method.
这里,我们使用了CriteriaBuilder的count()方法。
Using either of the above will produce a list of object arrays:
使用上述任何一种方法都会产生一个对象数组的列表。
[category1, 2]
[category2, 1]
[category3, 1]
Apart from count(), CriteriaBuilder provides various other aggregate functions:
除了count()之外,CriteriaBuilder还提供了其他各种聚合函数。
- avg – Calculates the average value for a column in a group
- max – Calculates the maximum value for a column in a group
- min – Calculates the minimum value for a column in a group
- least – Finds the least of the column values (for example, alphabetically or by date)
- sum – Calculates the sum of the column values in a group
4. Hibernate Projections
4.休眠的预测
Unlike JPA, Hibernate provides org.hibernate.criterion.Projection for projecting with a Criteria query. It also provides a class called org.hibernate.criterion.Projections, a factory for Projection instances.
与JPA不同,Hibernate提供了org.hibernate.criterion.Projection,用于用Criteria查询。它还提供了一个名为org.hibernate.criterion.Projections的类,一个用于Projection实例的工厂。
4.1. Single-Column Projections
4.1.单柱预测
First, let’s see how we can project a single column. We’ll use the example we saw earlier:
首先,让我们看看如何投射一个单列。我们将使用我们之前看到的例子。
Criteria criteria = session.createCriteria(Product.class);
criteria = criteria.setProjection(Projections.property("name"));
We’ve used the Criteria.setProjection() method to specify the property that we want in the query result. Projections.property() does the same work for us as Root.get() did when indicating the column to select.
我们使用了Criteria.setProjection()方法来指定我们希望在查询结果中的属性。Projections.property()为我们做了与Root.get()相同的工作,在指示要选择的列时。
4.2. Multi-Column Projections
4.2.多列投影
To project multiple columns, we’ll have to first create a ProjectionList. ProjectionList is a special kind of Projection that wraps other projections to allow selecting multiple values.
为了投射多个列,我们必须首先创建一个ProjectionList。ProjectionList是一种特殊的Projection,它包裹着其他的投影,以允许选择多个值。
We can create a ProjectionList using the Projections.projectionList() method, like showing the Product‘s id and name:
我们可以使用Projections.projectionList()方法创建一个ProjectionList,比如显示Product的id和name。
Criteria criteria = session.createCriteria(Product.class);
criteria = criteria.setProjection(
Projections.projectionList()
.add(Projections.id())
.add(Projections.property("name")));
4.3. Projecting Aggregate Functions
4.3.聚合函数的投影
Just like CriteriaBuilder, the Projections class also provides methods for aggregate functions.
就像CriteriaBuilder,Projections类也提供了聚合函数的方法。
Let’s see how we can implement the count example we saw earlier:
让我们看看如何实现我们之前看到的计数例子。
Criteria criteria = session.createCriteria(Product.class);
criteria = criteria.setProjection(
Projections.projectionList()
.add(Projections.groupProperty("category"))
.add(Projections.rowCount()));
It’s important to note that we didn’t directly specify the GROUP BY in the Criteria object. Calling groupProperty triggers this for us.
值得注意的是,我们没有直接在Criteria 对象中指定GROUP BY。调用groupProperty为我们触发了这个。
Apart from the rowCount() function, Projections also provides the aggregate functions we saw earlier.
除了rowCount()函数外,Projections还提供了我们之前看到的聚合函数。
4.4. Using an Alias for a Projection
4.4.为投影使用别名
An interesting feature of the Hibernate Criteria API is the use of an alias for a projection.
Hibernate Criteria API的一个有趣的特点是对一个投影使用别名。
This is especially useful when using an aggregate function, as we can then refer to the alias in the Criterion and Order instances:
这在使用聚合函数时特别有用,因为我们可以在Criterion和Order实例中引用该别名。
Criteria criteria = session.createCriteria(Product.class);
criteria = criteria.setProjection(Projections.projectionList()
.add(Projections.groupProperty("category"))
.add(Projections.alias(Projections.rowCount(), "count")));
criteria.addOrder(Order.asc("count"));
5. Conclusion
5.结论
In this article, we saw how to project entity properties using JPA and Hibernate.
在这篇文章中,我们看到了如何使用JPA和Hibernate来预测实体属性。
It is important to note that Hibernate has deprecated its Criteria API from version 5.2 onwards in favor of the JPA CriteriaQuery API. But, this is only because the Hibernate team doesn’t have the time to keep two different APIs, that pretty much do the same thing, in sync.
需要注意的是,Hibernate已经从5.2版本开始废弃了其Criteria API,而改用JPA CriteriaQuery API。但是,这只是因为Hibernate团队没有时间让两个不同的API保持同步,而这两个API几乎都是做同样的事情。
And of course, the code used in this article can be found over on GitHub.
当然,本文中使用的代码可以在GitHub上找到,。