JPA / Hibernateプロジェクション

1. 概要

このチュートリアルでは、JPAとHibernateを使用してエンティティプロパティを投影する方法を学習します*。

2. エンティティ

まず、この記事全体で使用するエンティティを見てみましょう。
@Entity
public class Product {
    @Id
    private long id;

    private String name;

    private String description;

    private String category;

    private BigDecimal unitPrice;

    // setters and getters
}
これは、さまざまなプロパティを持つ製品を表す単純なエンティティクラスです。

3. JPAプロジェクション

JPA仕様ではプロジェクションについて明示的に言及していませんが、コンセプトでそれらを見つけるケースが多くあります。
通常、JPQLクエリには候補エンティティクラスがあります。 クエリは、実行時に候補クラスのオブジェクトを作成し、取得したデータを使用してすべてのプロパティを設定します。
ただし、エンティティのプロパティのサブセット、または列データの_projection_を*取得することは可能です。
列データとは別に、*グループ化関数の結果を投影することもできます*。

* 3.1。 単一列の投影*

すべての製品の名前を一覧表示するとします。 JPQLでは、_select_句に_name_のみを含めることでこれを行うことができます。
Query query = entityManager.createQuery("select name from Product");
List<Object> resultList = query.getResultList();
または、_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();
*たまたま_String_型である単一の列を投影しているため、結果に__String__s *のリストが含まれることが予想されます。 したがって、_createQuery()_メソッドで_String_として候補クラスを指定しました。
単一のプロパティに投影したいので、* _ Query.select()_メソッドを使用しました。*ここで必要なプロパティはどのプロパティであるため、この場合、_Product_エンティティの_name_プロパティを使用します。 。
次に、上記の2つのクエリによって生成されたサンプル出力を見てみましょう。
Product Name 1
Product Name 2
Product Name 3
Product Name 4
*投影で_name_の代わりに_id_プロパティを使用した場合、クエリは_Long_オブジェクトのリストを返すことに注意してください。*

* 3.2。 マルチカラムプロジェクション*

JPQLを使用して複数の列に投影するには、必要なすべての列を_select_句に追加するだけです。
Query query = session.createQuery("select id, name, unitPrice from Product");
List resultList = query.getResultList();
ただし、_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();
ここでは、* _ select()_ *の代わりに_multiselect()_メソッドを使用しました。 この方法を使用すると、選択する複数のアイテムを指定できます。
別の重要な変更は、_Object [] _の使用です。 *複数のアイテムを選択すると、クエリは投影された各アイテムの値を含むオブジェクト配列*を返します。 これはJPQLにも当てはまります。
データを印刷したときのデータを見てみましょう。
[1, Product Name 1, 1.40]
[2, Product Name 2, 4.30]
[3, Product Name 3, 14.00]
[4, Product Name 4, 3.90]
ご覧のとおり、返されたデータの処理は少し面倒です。 しかし、幸いなことに* JPAはlink:/hibernate-query-to-custom-class [このデータをカスタムクラスに入力]にアクセスできます。*
また、_CriteriaBuilder.tuple()_または_CriteriaBuilder.construct()_を使用して、_Tuple_オブジェクトまたはカスタムクラスのオブジェクトのリストとしてそれぞれ結果を取得できます。

* 3.3。 集計関数の投影*

列データとは別に、データをグループ化し、_count_や_average._などの集計関数を使用する場合があります。
各カテゴリの製品の数を検索するとします。 JPQLの_count()_集約関数を使用してこれを行うことができます。
Query query = entityManager.createQuery("select p.category, count(p) from Product p group by p.category");
または、_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"));
ここでは、_CriteriaBuilder_â€〜s _count()_メソッドを使用しました。
上記のいずれかを使用すると、オブジェクト配列のリストが生成されます。
[category1, 2]
[category2, 1]
[category3, 1]
_count()_の他に、_CriteriaBuilder_は他のさまざまな集計関数を提供します。
  • avg –グループ内の列の平均値を計算します

  • max –グループ内の列の最大値を計算します

  • min –グループ内の列の最小値を計算します

  • least –列の値の最小値を検索します(たとえば、
    アルファベット順または日付順)

  • sum –グループ内の列の値の合計を計算します

*4. Hibernate Projections *

JPAとは異なり、Hibernateは、link:/hibernate-criteria-queries[_Criteria_ query] *で投影するための* _org.hibernate.criterion.Projection_を提供します。 また、_org.hibernate.criterion.Projections、_というクラスを提供します。_Projection_インスタンスのファクトリーです。

* 4.1。 単一列の投影*

最初に、単一の列を投影する方法を見てみましょう。 先ほど見た例を使用します。
Criteria criteria = session.createCriteria(Product.class);
criteria = criteria.setProjection(Projections.property("name"));
_Criteria.setProjection()_メソッドを使用して、クエリ結果に必要なプロパティを指定しました。 * _Projections.property()_は、選択する列を示すときに_Root.get()_ did *と同じ作業を行います。

* 4.2。 マルチカラムプロジェクション*

複数の列を投影するには、最初に_ProjectionListを作成する必要があります。 * _ProjectionList _ * _ *は特別な種類の_Projection_であり、他のプロジェクションをラップして複数の値を選択できるようにする__.__
_Project_Listsを作成するには、_Projections.projectionList()_ *メソッドを使用します。たとえば、_Product_の_id_および_name_を表示します。
Criteria criteria = session.createCriteria(Product.class);
criteria = criteria.setProjection(
  Projections.projectionList()
    .add(Projections.id())
    .add(Projections.property("name")));

* 4.3。 集計関数の投影*

_CriteriaBuilder_と同様に、_Projections_クラスも集計関数のメソッドを提供します。
先ほど見たcountの例を実装する方法を見てみましょう。
Criteria criteria = session.createCriteria(Product.class);
criteria = criteria.setProjection(
  Projections.projectionList()
    .add(Projections.groupProperty("category"))
    .add(Projections.rowCount()));
  • _Criteria_オブジェクトでGROUP BYを直接指定しなかったことに注意することが重要です。 _groupProperty_を呼び出すと、これがトリガーされます。

    _rowCount()_関数とは別に、* _ Projections_は先ほど見た集約関数も提供します。*

* 4.4。 投影にエイリアスを使用する*

Hibernate Criteria APIの興味深い機能は、プロジェクションのエイリアスの使用です。
これは、_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. 結論

この記事では、JPAとHibernateを使用してエンティティプロパティを投影する方法を説明しました。
  • HibernateはJPA CriteriaQuery API *を支持して、バージョン5.2からCriteria APIを非推奨にしていることに注意することが重要です。 しかし、これは、Hibernateチームが2つの異なるAPIを保持する時間がないためです。これらのAPIはほぼ同じことを同期します。

    そしてもちろん、この記事で使用されているコードはhttps://github.com/eugenp/tutorials/tree/master/persistence-modules/java-jpa[GitHub上]にあります。