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クエリには候補エンティティクラスがあります。 クエリは、実行時に候補クラスのオブジェクトを作成し、取得したデータを使用してすべてのプロパティにデータを入力します。

ただし、エンティティのプロパティのサブセット、、つまり列データのプロジェクション取得することは可能です。

列データとは別に、グループ化関数の結果を予測することもできます。

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型の単一の列を投影しているため、結果にStringsのリストが含まれると予想されます。 したがって、 createQuery()メソッドで候補クラスをStringとして指定しました。

単一のプロパティに投影したいので、 Query.select()メソッドを使用しました。 ここで行うのは、必要なプロパティです。したがって、この場合は、 名前私たちの財産製品実在物。

次に、上記の2つのクエリによって生成されたサンプル出力を見てみましょう。

Product Name 1
Product Name 2
Product Name 3
Product Name 4

名前の代わりにプロジェクションで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()を使用しました。 この方法を使用すると、選択する複数のアイテムを指定できます。

もう1つの重要な変更は、 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にこのデータをカスタムクラスに移入させることができます。

また、 CriteriaBuilder.tuple()または CriteriaBuilder.construct()を使用して、Tupleオブジェクトまたはカスタムクラスのオブジェクトのリストとして結果を取得できます。それぞれ。

3.3. 集計関数の射影

列データとは別に、データをグループ化し、countaverageなどの集計関数を使用したい場合があります。

各カテゴリの製品数を調べたいとしましょう。 これは、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"));

ここでは、 CriteriaBuildercount()メソッドを使用しました。

上記のいずれかを使用すると、オブジェクト配列のリストが生成されます。

[category1, 2]
[category2, 1]
[category3, 1]

count()とは別に、CriteriaBuilderは他のさまざまな集計関数を提供します。

  • avg –グループ内の列の平均値を計算します
  • max –グループ内の列の最大値を計算します
  • min –グループ内の列の最小値を計算します
  • less –列の値の最小値を検索します(たとえば、アルファベット順または日付順)
  • sum –グループ内の列値の合計を計算します

4. Hibernateプロジェクション

JPAとは異なり、HibernateはCriteriaクエリで投影するための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()が行ったと同じ作業を行います。

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

複数の列を投影するには、最初に作成する必要があります ProjectionList。 ProjectionList 他のプロジェクションをラップする特別な種類のプロジェクションです複数の値を選択できるようにする

Productidの名前を表示するように、Projections.projectionList()メソッドを使用して ProjectionListを作成できます。

Criteria criteria = session.createCriteria(Product.class);
criteria = criteria.setProjection(
  Projections.projectionList()
    .add(Projections.id())
    .add(Projections.property("name")));

4.3. 集計関数の射影

CriteriaBuilder と同様に、Projectionsクラスも集計関数のメソッドを提供します。

前に見たカウントの例を実装する方法を見てみましょう。

Criteria criteria = session.createCriteria(Product.class);
criteria = criteria.setProjection(
  Projections.projectionList()
    .add(Projections.groupProperty("category"))
    .add(Projections.rowCount()));

CriteriaオブジェクトでGROUPBYを直接指定しなかったことに注意することが重要です。 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以降のCriteriaAPIを廃止したことに注意してください。 ただし、これは、Hibernateチームに2つの異なるAPIを維持する時間がないためです。これらのAPIは、ほぼ同じことを同期して実行します。

そしてもちろん、この記事で使用されているコードは、GitHubにあります。