1. 概要

このチュートリアルでは、JPAクエリとHibernateクエリの使用方法、およびCriteria、JPQL、HQLクエリの違いについて説明します。 基準クエリを使用すると、ユーザーは生のSQLを使用せずにクエリを記述できます。 Criteriaクエリに加えて、Hibernate Named Queriesの記述と、SpringDataJPAで@Queryアノテーションを使用する方法について説明します。

飛び込む前に、HibernateCriteriaAPIはHibernate5.2以降非推奨になっていることに注意してください。 したがって、例ではJPA Criteria APIを使用します。これは、Criteriaクエリを作成するための新しく推奨されるツールです。 したがって、これ以降は、これを単にCriteriaAPIと呼びます。

2. 基準クエリ

Criteria APIは、さまざまなフィルターと論理条件をその上に適用することにより、Criteriaクエリオブジェクトの構築に役立ちます。これは、オブジェクトを操作し、RDBMSテーブルから目的のデータを返す別の方法です。

Hibernate SessioncreateCriteria()メソッドは、アプリケーションで基準クエリを実行するための永続オブジェクトインスタンスを返します。 簡単に言えば、Criteria APIは、さまざまなフィルターと論理条件を適用する基準クエリを構築します。

2.1. Mavenの依存関係

参照JPA依存関係の最新バージョン(HibernateでJPAを実装)を取得して、pom.xmlに追加しましょう。

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>5.3.2.Final</version>
</dependency>

2.2. 基準クエリと式の使用

ユーザーの条件に従って、CriteriaBuilderはクエリ結果を制御します。 CriteriaBuilder式を提供するCriteriaQuerywhere()メソッドを使用します。

この記事で使用するエンティティを見てみましょう。

public class Employee {

    private Integer id;
    private String name;
    private Long salary;

   // standard getters and setters
}

データベースから「Employee」のすべての行を取得する単純な条件クエリを見てみましょう。

Session session = HibernateUtil.getHibernateSession();
CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<Employee> cr = cb.createQuery(Employee.class);
Root<Employee> root = cr.from(Employee.class);
cr.select(root);

Query<Employee> query = session.createQuery(cr);
List<Employee> results = query.getResultList();
session.close();
return results;

上記のCriteriaクエリは、すべてのアイテムのセットを返します。 それがどのように起こるか見てみましょう:

  1. SessionFactory オブジェクトは、Sessionインスタンスを作成します
  2. Session は、 getCriteriaBuilder()メソッドを使用して、CriteriaBuilderのインスタンスを返します。
  3. CriteriaBuilder は、 createQuery()メソッドを使用します。 これにより、 CriteriaQuery()オブジェクトが作成され、さらにQueryインスタンスが返されます。
  4. 最後に、 getResult()メソッドを呼び出して、結果を保持するクエリオブジェクトを取得します

CriteriaQueryの別の式を見てみましょう。

cr.select(root).where(cb.gt(root.get("salary"), 50000));

その結果、上記のクエリは、給与が50000を超える従業員のセットを返します。

3. JPQL

JPQL は、Java PersistenceQueryLanguageの略です。 Spring Dataは、クエリを作成および実行するための複数の方法を提供します。JPQLはその1つです。 Springの@Queryアノテーションを使用してクエリを定義し、JPQLクエリとネイティブSQLクエリの両方を実行します。 クエリ定義はデフォルトでJPQLを使用します。

@Query アノテーションを使用して、SpringでSQLクエリを定義します。 @Queryアノテーションで定義されたクエリは、@NamedQueryでアノテーションが付けられた名前付きクエリよりも優先されます。

3.1. JPQLクエリの使用

JPQLを使用して動的クエリを作成しましょう。

@Query(value = "SELECT e FROM Employee e")
List<Employee> findAllEmployees(Sort sort);

引数パラメーターを持つJPQLクエリでは、SpringDataはメソッド宣言と同じ順序でメソッド引数をクエリに渡します。 メソッド引数をクエリに渡すいくつかの例を見てみましょう。

@Query("SELECT e FROM Employee e WHERE e.salary = ?1")
Employee findAllEmployeesWithSalary(Long salary);
@Query("SELECT e FROM Employee e WHERE e.name = ?1 and e.salary = ?2")
Employee findEmployeeByNameAndSalary(String name, Long salary);

上記の後者のクエリでは、 name メソッド引数がインデックス1に関するクエリパラメータとして渡され、salary引数がインデックス2クエリパラメータとして渡されます。

3.2. JPQLネイティブクエリの使用

これらのSQLクエリは、実際のデータベースとテーブルオブジェクトを参照するネイティブクエリを使用して、データベースで直接実行できます。 ネイティブSQLクエリを定義するには、nativeQuery属性の値をtrueに設定する必要があります。 ネイティブSQLクエリはアノテーションのvalue属性で定義されます。

クエリの引数として渡されるインデックス付きパラメータを示すネイティブクエリを見てみましょう。

@Query(
  value = "SELECT * FROM Employee e WHERE e.salary = ?1",
  nativeQuery = true)
Employee findEmployeeBySalaryNative(Long salary);

名前付きパラメーターを使用すると、クエリが読みやすくなり、リファクタリングの場合にエラーが発生しにくくなります。JPQLおよびネイティブ形式の単純な名前付きクエリの図を見てみましょう。

@Query("SELECT e FROM Employee e WHERE e.name = :name and e.salary = :salary")
Employee findEmployeeByNameAndSalaryNamedParameters(
  @Param("name") String name,
  @Param("salary") Long salary);

メソッドパラメータは、名前付きパラメータを使用してクエリに渡されます。 リポジトリメソッド宣言内の@Paramアノテーションを使用して、名前付きクエリを定義できます。 その結果、 @Paramアノテーションには、対応するJPQLまたはSQLクエリ名と一致する文字列値が必要です。

@Query(value = "SELECT * FROM Employee e WHERE e.name = :name and e.salary = :salary", 
  nativeQuery = true) 
Employee findUserByNameAndSalaryNamedParamsNative( 
  @Param("name") String name, 
  @Param("salary") Long salary);

4. HQL

HQLはHibernateQueryLanguageの略です。 これはSQLに似たオブジェクト指向言語であり、データベースのクエリに使用できます。 ただし、主な欠点はコードが読みにくいことです。 クエリを名前付きクエリとして定義して、データベースにアクセスする実際のコードに配置できます。

4.1. Hibernate名前付きクエリの使用

名前付きクエリは、事前定義された変更不可能なクエリ文字列を使用してクエリを定義します。 これらのクエリは、セッションファクトリの作成中に検証されるため、フェイルファストです。 org.hibernate.annotations.NamedQueryアノテーションを使用して名前付きクエリを定義しましょう。

@NamedQuery(name = "Employee_FindByEmployeeId",
 query = "from Employee where id = :id")

@NamedQueryアノテーションは、それ自体を1つのエンティティクラスにのみアタッチします。 @NamedQueries アノテーションを使用して、エンティティの複数の名前付きクエリをグループ化できます。

@NamedQueries({
    @NamedQuery(name = "Employee_findByEmployeeId", 
      query = "from Employee where id = :id"),
    @NamedQuery(name = "Employee_findAllByEmployeeSalary", 
      query = "from Employee where salary = :salary")
})

4.2. ストアドプロシージャと式

結論として、 @NamedNativeQuery アノテーションを使用して、プロシージャと関数を格納できます。

@NamedNativeQuery(
  name = "Employee_FindByEmployeeId", 
  query = "select * from employee emp where id=:id", 
  resultClass = Employee.class)

5. HQLおよびJPQLクエリに対する基準クエリの利点

HQLに対するCriteriaQueriesの主な利点は、すてきでクリーンなオブジェクト指向APIです。 その結果、コンパイル時にCriteriaAPIのエラーを検出できます。

さらに、JPQLクエリとCriteriaクエリのパフォーマンスと効率は同じです。

基準クエリは、HQLやJPQLと比較して、より柔軟性があり、動的クエリの記述をより適切にサポートします。

ただし、HQLとJPQLは、Criteriaクエリでは不可能なネイティブクエリサポートを提供します。 これは、Criteriaクエリの欠点の1つです。

JPQLネイティブクエリを使用して複雑な結合を簡単に記述できますが、CriteriaAPIを使用して同じものを適用すると管理が困難になります。

6. 結論

この記事では、主にCriteriaクエリ、JPQLクエリ、およびHibernateとJPAのHQLクエリの基本について説明しました。 さらに、これらのクエリの使用方法と、各アプローチの長所と短所を学びました。

いつものように、この記事で使用されている完全なコード例は、GitHubおよびここのにあります。