© 2008-2019 The original authors.

このドキュメントのコピーは、あなた自身の使用および他者への配布のために作成することができますが、そのようなコピーに料金を請求しないこと、さらに、印刷物または電子的に配布されるかどうかにかかわらず、各コピーにこの著作権表示が含まれていることを条件とします。

序文

Spring Data JPA は、Java Persistence API(JPA)のリポジトリサポートを提供します。JPA データソースにアクセスする必要があるアプリケーションの開発を容易にします。

1. プロジェクトメタデータ

2. 注目の新機能

2.1. Spring Data JPA 1.11 の新機能

Spring Data JPA 1.11 は以下の機能を追加しました:

  • Hibernate 5.2. との互換性の向上

  • 例によるクエリの任意一致モードをサポートします。

  • ページ化されたクエリ実行の最適化。

  • リポジトリクエリ派生での exists 射影のサポート。

2.2. Spring Data JPA 1.10 の新機能

Spring Data JPA 1.10 は以下の機能を追加しました:

  • リポジトリクエリメソッドでの射影のサポート。

  • 例によるクエリのサポート。

  • 次のアノテーションは、合成されたアノテーションに基づいて構築できるようになりました: @EntityGraph@Lock@Modifying@Query@QueryHints および @Procedure

  • コレクション式での Contains キーワードのサポート。

  • JSR-310 および ThreeTenBP の ZoneId の AttributeConverter 実装。

  • Querydsl 4、Hibernate 5, OpenJPA 2.4、および EclipseLink 2.6.1 にアップグレードします。

3. 依存関係

個々の Spring Data モジュールの開始日が異なるため、それらのほとんどには異なるメジャーバージョン番号とマイナーバージョン番号が付いています。互換性のあるものを見つける最も簡単な方法は、定義された互換性のあるバージョンとともに提供される Spring Data Release Train BOM に依存することです。Maven プロジェクトでは、次のように、POM の <dependencyManagement /> セクションでこの依存関係を宣言します。

例 1: Spring Data リリーストレイン BOM の使用
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-bom</artifactId>
      <version>2020.0.0-RC2</version>
      <scope>import</scope>
      <type>pom</type>
    </dependency>
  </dependencies>
</dependencyManagement>

現在のリリーストレインバージョンは 2020.0.0-RC2 です。トレインバージョンでは、パターン YYYY.MINOR.MICROcalver (英語) を使用しています。バージョン名は、GA リリースとサービスリリースでは ${calver} に従い、他のすべてのバージョンでは次のパターンに従います。${calver}-${modifier}modifier は次のいずれかになります。

  • SNAPSHOT: 現在のスナップショット

  • M1M2 など:マイルストーン

  • RC1RC2 など:リリース候補

BOM の使用例は、Spring Data サンプルリポジトリ (GitHub) にあります。これを配置すると、次のように、<dependencies /> ブロックにバージョンなしで使用する Spring Data モジュールを宣言できます。

例 2: Spring Data モジュールへの依存関係の宣言
<dependencies>
  <dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-jpa</artifactId>
  </dependency>
<dependencies>

3.1. Spring Boot を使用した依存関係管理

Spring Boot は、Spring Data モジュールの最新バージョンを選択します。それでも新しいバージョンにアップグレードする場合は、プロパティ spring-data-releasetrain.version を、使用するトレインバージョンとイテレーションに構成します。

3.2. Spring Framework

Spring Data モジュールの現在のバージョンには、バージョン 5.3.0-RC2 以上の Spring Framework が必要です。モジュールは、そのマイナーバージョンの古いバグ修正バージョンでも動作する場合があります。ただし、その世代内の最新バージョンを使用することを強くお勧めします。

4. Spring Data リポジトリの操作

Spring Data リポジトリの抽象化のゴールは、さまざまな永続ストアのデータアクセスレイヤーを実装するために必要な定型コードの量を大幅に削減することです。

Spring Data リポジトリのドキュメントとモジュール

この章では、Spring Data リポジトリのコア概念とインターフェースについて説明します。この章の情報は、Spring Data Commons モジュールから引用されています。Java Persistence API(JPA)モジュールの構成とコードサンプルを使用します。XML 名前空間宣言と型を、使用する特定のモジュールに相当するものに拡張する必要があります。「名前空間リファレンス」は、リポジトリ API をサポートするすべての Spring Data モジュールでサポートされる XML 構成をカバーしています。「リポジトリクエリキーワード」は、一般的なリポジトリ抽象化でサポートされるクエリメソッドキーワードをカバーしています。モジュールの特定の機能の詳細については、このドキュメントのそのモジュールに関する章を参照してください。

4.1. コアコンセプト

Spring Data リポジトリ抽象化の中心的なインターフェースは Repository です。ドメインクラスと、ドメインクラスの ID タイプを型引数として管理します。このインターフェースは、主にマーカーインターフェースとして機能し、使用するタイプをキャプチャーし、このインターフェースを継承するインターフェースを発見できます。CrudRepository は、管理されているエンティティクラスに洗練された CRUD 機能を提供します。

例 3: CrudRepository インターフェース
public interface CrudRepository<T, ID> extends Repository<T, ID> {

  <S extends T> S save(S entity);      (1)

  Optional<T> findById(ID primaryKey); (2)

  Iterable<T> findAll();               (3)

  long count();                        (4)

  void delete(T entity);               (5)

  boolean existsById(ID primaryKey);   (6)

  //  …  more functionality omitted.
}
1 指定されたエンティティを保存します。
2 指定された ID で識別されるエンティティを返します。
3 すべてのエンティティを返します。
4 エンティティの数を返します。
5 指定されたエンティティを削除します。
6 指定された ID のエンティティが存在するかどうかを示します。
また、JpaRepository や MongoRepository などの永続化技術固有の抽象化も提供します。これらのインターフェースは CrudRepository を継承し、CrudRepository などのかなり汎用的な永続化テクノロジーにとらわれないインターフェースに加えて、基礎となる永続化テクノロジーの機能を公開します。

CrudRepository の上部には、エンティティへのページ分割されたアクセスを容易にするための追加のメソッドを追加する PagingAndSortingRepository 抽象化があります。

例 4: PagingAndSortingRepository インターフェース
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {

  Iterable<T> findAll(Sort sort);

  Page<T> findAll(Pageable pageable);
}

20 のページサイズで User の 2 番目のページにアクセスするには、次のようなことができます。

PagingAndSortingRepository<User, Long> repository = //  …  get access to a bean
Page<User> users = repository.findAll(PageRequest.of(1, 20));

クエリメソッドに加えて、カウントクエリと削除クエリの両方のクエリ派生を使用できます。次のリストは、派生カウントクエリのインターフェース定義を示しています。

例 5: 派生カウントクエリ
interface UserRepository extends CrudRepository<User, Long> {

  long countByLastname(String lastname);
}

次のリストは、派生削除クエリのインターフェース定義を示しています。

例 6: 派生削除クエリ
interface UserRepository extends CrudRepository<User, Long> {

  long deleteByLastname(String lastname);

  List<User> removeByLastname(String lastname);
}

4.2. クエリメソッド

通常、標準の CRUD 機能リポジトリには、基になるデータストアに対するクエリがあります。Spring Data では、これらのクエリを宣言することは 4 ステップのプロセスになります。

  1. 次の例に示すように、リポジトリまたはそのサブインターフェースの 1 つを継承するインターフェースを宣言し、処理するドメインクラスと ID タイプに入力します。

    interface PersonRepository extends Repository<Person, Long> {  …  }
  2. インターフェースでクエリメソッドを宣言します。

    interface PersonRepository extends Repository<Person, Long> {
      List<Person> findByLastname(String lastname);
    }
  3. JavaConfig または XML 構成を使用して、Spring をセットアップして、これらのインターフェースのプロキシインスタンスを作成します。

    1. Java 構成を使用するには、次のようなクラスを作成します。

      import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
      
      @EnableJpaRepositories
      class Config {  …  }
    2. XML 構成を使用するには、次のような Bean を定義します。

      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns:jpa="http://www.springframework.org/schema/data/jpa"
         xsi:schemaLocation="http://www.springframework.org/schema/beans
           https://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/data/jpa
           https://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
      
         <jpa:repositories base-package="com.acme.repositories"/>
      
      </beans>

    この例では、JPA 名前空間が使用されています。リポジトリの抽象化を他のストアに使用する場合、これをストアモジュールの適切なネームスペース宣言に変更する必要があります。つまり、たとえば mongodb を優先して jpa を交換する必要があります。

    + また、アノテーション付きクラスのパッケージがデフォルトで使用されるため、JavaConfig バリアントはパッケージを明示的に構成しないことに注意してください。スキャンするパッケージをカスタマイズするには、データストア固有のリポジトリの @Enable${store}Repositories -annotation の basePackage …  属性のいずれかを使用します。

  4. 次の例に示すように、リポジトリインスタンスを挿入して使用します。

    class SomeClient {
    
      private final PersonRepository repository;
    
      SomeClient(PersonRepository repository) {
        this.repository = repository;
      }
    
      void doSomething() {
        List<Person> persons = repository.findByLastname("Matthews");
      }
    }

以下のセクションでは、各ステップについて詳しく説明します。

4.3. リポジトリインターフェースの定義

最初に、ドメインクラス固有のリポジトリインターフェースを定義します。インターフェースは Repository を継承し、ドメインクラスと ID の型を指定する必要があります。そのドメインタイプの CRUD メソッドを公開する場合は、Repository ではなく CrudRepository を継承します。

4.3.1. リポジトリ定義の微調整

通常、リポジトリインターフェースは RepositoryCrudRepository または PagingAndSortingRepository を継承します。あるいは、Spring Data インターフェースを継承したくない場合は、@RepositoryDefinition を使用してリポジトリインターフェースにアノテーションを付けることもできます。CrudRepository を継承すると、エンティティを操作するためのメソッドの完全なセットが公開されます。公開するメソッドを選択する場合、CrudRepository から公開するメソッドをドメインリポジトリにコピーします。

そうすることで、提供された Spring Data リポジトリ機能の上に独自の抽象化を定義できます。

次の例は、CRUD メソッド(この場合は findById および save)を選択的に公開する方法を示しています。

例 7: CRUD メソッドを選択的に公開する
@NoRepositoryBean
interface MyBaseRepository<T, ID> extends Repository<T, ID> {

  Optional<T> findById(ID id);

  <S extends T> S save(S entity);
}

interface UserRepository extends MyBaseRepository<User, Long> {
  User findByEmailAddress(EmailAddress emailAddress);
}

前の例では、すべてのドメインリポジトリと公開された findById(…) および save(…) に共通のベースインターフェースを定義しました。これらのメソッドは、Spring Data が提供する選択のストアのベースリポジトリ実装にルーティングされます(たとえば、JPA を使用する場合、実装は SimpleJpaRepository です)。これは、それらが CrudRepository のメソッドシグネチャーと一致するためです。そのため、UserRepository はユーザーを保存し、ID で個々のユーザーを検索し、メールアドレスで Users を検索するクエリをトリガーできるようになりました。

中間リポジトリインターフェースには @NoRepositoryBean のアノテーションが付けられています。Spring Data が実行時にインスタンスを作成してはならないすべてのリポジトリインターフェースに、そのアノテーションを必ず追加してください。

4.3.2. 複数の Spring Data モジュールでリポジトリを使用する

定義済みスコープ内のすべてのリポジトリインターフェースが Spring Data モジュールにバインドされているため、アプリケーションで一意の Spring Data モジュールを使用すると、物事が簡単になります。アプリケーションによっては、複数の Spring Data モジュールを使用する必要がある場合があります。そのような場合、リポジトリ定義は永続化テクノロジーを区別する必要があります。クラスパスで複数のリポジトリファクトリを検出すると、Spring Data は厳密なリポジトリ構成モードに入ります。厳密な構成では、リポジトリまたはドメインクラスの詳細を使用して、リポジトリ定義の Spring Data モジュールバインディングについて決定します。

  1. リポジトリ定義がモジュール固有のリポジトリを継承する場合、特定の Spring Data モジュールの有効な候補です。

  2. ドメインクラスにモジュール固有のタイプアノテーションが付けられている場合、特定の Spring Data モジュールの有効な候補です。Spring Data モジュールは、サードパーティのアノテーション(JPA の @Entity など)を受け入れるか、独自のアノテーション(Spring Data MongoDB および Spring Data Elasticsearch の @Document など)を提供します。

次の例は、モジュール固有のインターフェース(この場合は JPA)を使用するリポジトリを示しています。

例 8: モジュール固有のインターフェースを使用したリポジトリ定義
interface MyRepository extends JpaRepository<User, Long> { }

@NoRepositoryBean
interface MyBaseRepository<T, ID> extends JpaRepository<T, ID> {  …  }

interface UserRepository extends MyBaseRepository<User, Long> {  …  }

MyRepository および UserRepository は、型階層で JpaRepository を継承します。それらは、Spring Data JPA モジュールの有効な候補です。

次の例は、汎用インターフェースを使用するリポジトリを示しています。

例 9: ジェネリックインターフェースを使用したリポジトリ定義
interface AmbiguousRepository extends Repository<User, Long> {  …  }

@NoRepositoryBean
interface MyBaseRepository<T, ID> extends CrudRepository<T, ID> {  …  }

interface AmbiguousUserRepository extends MyBaseRepository<User, Long> {  …  }

AmbiguousRepository および AmbiguousUserRepository は、それらの型階層で Repository および CrudRepository のみを継承します。一意の Spring Data モジュールを使用する場合、これはまったく問題ありませんが、複数のモジュールは、これらのリポジトリをバインドする特定の Spring Data を区別できません。

次の例は、アノテーション付きのドメインクラスを使用するリポジトリを示しています。

例 10: アノテーション付きのドメインクラスを使用したリポジトリ定義
interface PersonRepository extends Repository<Person, Long> {  …  }

@Entity
class Person {  …  }

interface UserRepository extends Repository<User, Long> {  …  }

@Document
class User {  …  }

PersonRepository は、JPA @Entity アノテーションが付けられた Person を参照するため、このリポジトリは明らかに Spring Data JPA に属します。UserRepository は、Spring Data MongoDB の @Document アノテーションが付けられた User を参照します。

次の悪い例は、アノテーションが混在するドメインクラスを使用するリポジトリを示しています。

例 11: アノテーションが混在するドメインクラスを使用したリポジトリ定義
interface JpaPersonRepository extends Repository<Person, Long> {  …  }

interface MongoDBPersonRepository extends Repository<Person, Long> {  …  }

@Entity
@Document
class Person {  …  }

この例は、JPA と Spring Data の両方の MongoDB アノテーションを使用したドメインクラスを示しています。JpaPersonRepository と MongoDBPersonRepository の 2 つのリポジトリを定義します。1 つは JPA 向けで、もう 1 つは MongoDB の使用向けです。Spring Data はリポジトリを区別することができなくなり、未定義の動作につながります。

リポジトリタイプの詳細および識別ドメインクラスアノテーションは、特定の Spring Data モジュールのリポジトリ候補を識別するための厳密なリポジトリ構成に使用されます。同じドメインタイプで複数の永続化テクノロジ固有のアノテーションを使用することが可能であり、複数の永続化テクノロジでドメインタイプを再利用できます。ただし、Spring Data は、リポジトリをバインドする一意のモジュールを決定できなくなります。

リポジトリを区別する最後の方法は、リポジトリベースパッケージをスコープすることです。ベースパッケージは、リポジトリインターフェース定義のスキャンの開始点を定義します。これは、適切なパッケージにリポジトリ定義があることを意味します。デフォルトでは、アノテーション駆動型の構成では、構成クラスのパッケージが使用されます。XML ベースの構成の基本パッケージは必須です。

次の例は、基本パッケージのアノテーション駆動型の構成を示しています。

例 12: 基本パッケージのアノテーション駆動型構成
@EnableJpaRepositories(basePackages = "com.acme.repositories.jpa")
@EnableMongoRepositories(basePackages = "com.acme.repositories.mongo")
class Configuration {  …  }

4.4. クエリメソッドの定義

リポジトリプロキシには、メソッド名からストア固有のクエリを派生させる 2 つの方法があります。

  • メソッド名から直接クエリを導出します。

  • 手動で定義されたクエリを使用します。

利用可能なオプションは、実際のストアによって異なります。ただし、作成する実際のクエリを決定する戦略が必要です。次のセクションでは、使用可能なオプションについて説明します。

4.4.1. クエリ検索戦略

クエリを解決するリポジトリインフラストラクチャでは、次の戦略を使用できます。XML 構成では、query-lookup-strategy 属性を使用して名前空間で戦略を構成できます。Java 構成の場合、Enable${store}Repositories アノテーションの queryLookupStrategy 属性を使用できます。特定のデータストアでは一部の戦略がサポートされていない場合があります。

  • CREATE は、クエリメソッド名からストア固有のクエリを作成しようとします。一般的なアプローチは、メソッド名から既知のプレフィックスの特定のセットを削除し、メソッドの残りを解析することです。クエリ構築の詳細については、「クエリ作成」を参照してください。

  • USE_DECLARED_QUERY は宣言されたクエリを見つけようとし、見つからない場合は例外をスローします。クエリは、アノテーションによってどこかで定義するか、他の手段で宣言できます。特定のストアのドキュメントを参照して、そのストアで利用可能なオプションを見つけてください。リポジトリインフラストラクチャが、ブートストラップ時にメソッドの宣言されたクエリを見つけられない場合、失敗します。

  • CREATE_IF_NOT_FOUND (デフォルト)は、CREATE と USE_DECLARED_QUERY を組み合わせたものです。最初に宣言されたクエリを検索し、宣言されたクエリが見つからない場合は、カスタムメソッド名ベースのクエリを作成します。これはデフォルトのルックアップ戦略であるため、明示的に何も設定しない場合に使用されます。メソッド名による迅速なクエリ定義だけでなく、必要に応じて宣言されたクエリを導入することにより、これらのクエリのカスタムチューニングも可能です。

4.4.2. クエリ作成

Spring Data リポジトリインフラストラクチャに組み込まれたクエリビルダメカニズムは、リポジトリのエンティティに対して制約クエリを構築できます。このメカニズムは、メソッドからプレフィックス find … Byread … Byquery … Bycount … By および get … By を取り除き、残りの解析を開始します。導入句には、作成するクエリに個別のフラグを設定する Distinct などの追加の式を含めることができます。ただし、最初の By は区切り文字として機能し、実際の条件の開始を示します。非常に基本的なレベルでは、エンティティプロパティの条件を定義し、And および Or で連結できます。次の例は、いくつかのクエリを作成する方法を示しています。

例 13: メソッド名からのクエリ作成
interface PersonRepository extends Repository<Person, Long> {

  List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);

  // Enables the distinct flag for the query
  List<Person> findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname);
  List<Person> findPeopleDistinctByLastnameOrFirstname(String lastname, String firstname);

  // Enabling ignoring case for an individual property
  List<Person> findByLastnameIgnoreCase(String lastname);
  // Enabling ignoring case for all suitable properties
  List<Person> findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname);

  // Enabling static ORDER BY for a query
  List<Person> findByLastnameOrderByFirstnameAsc(String lastname);
  List<Person> findByLastnameOrderByFirstnameDesc(String lastname);
}

メソッドの解析の実際の結果は、クエリを作成する永続ストアによって異なります。ただし、注意すべき一般的な事項がいくつかあります。

  • 式は通常、連結可能な演算子と組み合わせたプロパティトラバーサルです。プロパティ式を AND および OR と組み合わせることができます。また、プロパティ式の BetweenLessThanGreaterThan や Like などの演算子のサポートも取得できます。サポートされる演算子はデータストアによって異なる場合があるため、リファレンスドキュメントの適切な部分を参照してください。

  • メソッドパーサーは、個々のプロパティ(たとえば findByLastnameIgnoreCase(…))または大文字と小文字の区別をサポートするタイプのすべてのプロパティ(通常は String インスタンス - findByLastnameAndFirstnameAllIgnoreCase(…) など)の IgnoreCase フラグの設定をサポートします。ケースの無視がサポートされているかどうかはストアによって異なるため、ストア固有のクエリメソッドについては、リファレンスドキュメントの関連セクションを参照してください。

  • プロパティを参照するクエリメソッドに OrderBy 句を追加し、並べ替え方向(Asc または Desc)を提供することにより、静的な順序を適用できます。動的な並べ替えをサポートするクエリメソッドを作成するには、「特別なパラメーター処理」を参照してください。

4.4.3. プロパティ式

前の例に示すように、プロパティ式は管理対象エンティティの直接プロパティのみを参照できます。クエリの作成時に、解析されたプロパティが管理対象ドメインクラスのプロパティであることを既に確認しています。ただし、ネストされたプロパティを走査して制約を定義することもできます。次のメソッドシグネチャーを検討してください。

List<Person> findByAddressZipCode(ZipCode zipCode);

Person に Address があり、ZipCode があるとします。その場合、メソッドはプロパティトラバーサル x.address.zipCode を作成します。解決アルゴリズムは、パーツ全体(AddressZipCode)をプロパティとして解釈することから始まり、その名前(大文字ではない)のプロパティのドメインクラスをチェックします。アルゴリズムが成功した場合、そのプロパティを使用します。そうでない場合、アルゴリズムはキャメルケース部分のソースを右側から頭と尾に分割し、対応するプロパティ(この例では AddressZip と Code)を見つけようとします。アルゴリズムがそのヘッドを持つプロパティを見つけると、テールを取得し、そこからツリーを構築し続け、先ほど説明したメソッドでテールを分割します。最初の分割が一致しない場合、アルゴリズムは分割ポイントを左(AddressZipCode)に移動して続行します。

これはほとんどの場合に機能するはずですが、アルゴリズムが間違ったプロパティを選択する可能性があります。Person クラスにも addressZip プロパティがあるとします。アルゴリズムは最初の分割ラウンドですでに一致し、間違ったプロパティを選択して失敗します(addressZip のタイプにはおそらく code プロパティがないため)。

このあいまいさを解決するには、メソッド名内で _ を使用して、トラバーサルポイントを手動で定義します。メソッド名は次のようになります。

List<Person> findByAddress_ZipCode(ZipCode zipCode);

アンダースコア文字を予約文字として扱うため、標準の Java 命名規則に従うことを強くお勧めします(つまり、プロパティ名にアンダースコアを使用せず、代わりにキャメルケースを使用します)。

4.4.4. 特別なパラメーター処理

クエリでパラメーターを処理するには、前の例で既に見たようにメソッドパラメーターを定義します。それに加えて、インフラストラクチャは Pageable や Sort などの特定の特定のタイプを認識し、ページネーションとソートをクエリに動的に適用します。次の例は、これらの機能を示しています。

例 14: 照会メソッドで PageableSlice および Sort を使用する
Page<User> findByLastname(String lastname, Pageable pageable);

Slice<User> findByLastname(String lastname, Pageable pageable);

List<User> findByLastname(String lastname, Sort sort);

List<User> findByLastname(String lastname, Pageable pageable);
Sort および Pageable を使用する API は、null 以外の値がメソッドに渡されることを想定しています。並べ替えやページネーションを適用したくない場合は、Sort.unsorted() と Pageable.unpaged() を使用してください。

最初の方法では、org.springframework.data.domain.Pageable インスタンスをクエリメソッドに渡して、静的に定義されたクエリにページングを動的に追加できます。Page は、利用可能な要素とページの総数を知っています。これは、インフラストラクチャがカウントクエリをトリガーして、全体の数を計算することによって行われます。これは(使用するストアによっては)高価になる可能性があるため、代わりに Slice を返すことができます。Slice は、次の Slice が使用可能かどうかのみを認識します。これは、より大きな結果セットをウォークスルーするときに十分な場合があります。

ソートオプションも Pageable インスタンスを介して処理されます。ソートのみが必要な場合は、org.springframework.data.domain.Sort パラメーターをメソッドに追加します。ご覧のとおり、List を返すことも可能です。この場合、実際の Page インスタンスを構築するために必要な追加のメタデータは作成されません(つまり、必要な追加のカウントクエリが発行されないことを意味します)。むしろ、特定の範囲のエンティティのみを検索するようにクエリを制限します。

クエリ全体で取得するページ数を調べるには、追加のカウントクエリをトリガーする必要があります。デフォルトでは、このクエリは実際にトリガーするクエリから派生します。
ページングとソート

単純なソート式は、プロパティ名を使用して定義できます。式を連結して、複数の条件を 1 つの式にまとめることができます。

例 15: ソート式の定義
Sort sort = Sort.by("firstname").ascending()
  .and(Sort.by("lastname").descending());

タイプセーフなソート式の定義方法については、タイプからソート式を定義し、メソッド参照を使用してソートするプロパティを定義します。

例 16: タイプセーフ API を使用してソート式を定義する
TypedSort<Person> person = Sort.sort(Person.class);

Sort sort = person.by(Person::getFirstname).ascending()
  .and(person.by(Person::getLastname).descending());
TypedSort.by(…) はランタイムプロキシを利用します。通常は CGlib を使用します。これにより、Gral VM Native などのツールを使用すると、ネイティブイメージのコンパイルが妨げられる可能性があります。

ストアの実装が Querydsl をサポートしている場合、生成されたメタモデルタイプを使用してソート式を定義することもできます。

例 17: Querydsl API を使用してソート式を定義する
QSort sort = QSort.by(QPerson.firstname.asc())
  .and(QSort.by(QPerson.lastname.desc()));

4.4.5. クエリ結果の制限

first または top キーワードを使用することで、クエリメソッドの結果を制限できます。これらのキーワードは互換的に使用できます。オプションの数値を top または first に追加して、返される結果の最大サイズを指定できます。数を省略すると、結果サイズ 1 が想定されます。次の例は、クエリサイズを制限する方法を示しています。

例 18: Top および First を使用したクエリの結果サイズの制限
User findFirstByOrderByLastnameAsc();

User findTopByOrderByAgeDesc();

Page<User> queryFirst10ByLastname(String lastname, Pageable pageable);

Slice<User> findTop3ByLastname(String lastname, Pageable pageable);

List<User> findFirst10ByLastname(String lastname, Sort sort);

List<User> findTop10ByLastname(String lastname, Pageable pageable);

制限式は、Distinct キーワードもサポートしています。また、結果セットを 1 つのインスタンスに制限するクエリでは、Optional キーワードで結果をラップすることがサポートされています。

ページネーションまたはスライシングがクエリのページネーションの制限(および使用可能なページ数の計算)に適用される場合、制限された結果内で適用されます。

Sort パラメーターを使用して動的ソートと組み合わせて結果を制限すると、「K」最小エレメントと「K」最大エレメントの照会メソッドを表現できます。

4.4.6. コレクションまたはイテラブルを返すリポジトリメソッド

複数の結果を返すクエリメソッドは、標準の Java IterableListSet を使用できます。さらに、Spring Data の StreamableIterable のカスタム拡張、および Vavr (英語) が提供するコレクションタイプの返送をサポートしています。

Streamable をクエリメソッドの戻り値の型として使用する

Streamable は、Iterable または任意のコレクションタイプの代替として使用できます。非並列 Stream (Iterable にない)にアクセスするための便利なメソッド、直接  … .filter(…) および  … .map(…) を要素に渡して Streamable を他の連結する機能を提供します。

例 19: Streamable を使用してクエリメソッドの結果を結合する
interface PersonRepository extends Repository<Person, Long> {
  Streamable<Person> findByFirstnameContaining(String firstname);
  Streamable<Person> findByLastnameContaining(String lastname);
}

Streamable<Person> result = repository.findByFirstnameContaining("av")
  .and(repository.findByLastnameContaining("ea"));
カスタムのストリーミング可能なラッパータイプを返す

コレクションに専用のラッパータイプを提供することは、複数の要素を返すクエリ実行結果に API を提供するために一般的に使用されるパターンです。通常、これらの型は、コレクションのような型を返すリポジトリメソッドを呼び出し、ラッパー型のインスタンスを手動で作成することで使用されます。Spring Data では、これらのラッパータイプが以下の条件を満たす場合、クエリメソッドの戻り値のタイプとしてこれらのラッパータイプを使用できるため、この追加ステップを回避できます。

  1. タイプは Streamable を実装します。

  2. この型は、Streamable を引数として取得する、of(…) または valueOf(…) という名前のコンストラクターまたは静的ファクトリメソッドを公開します。

サンプルの使用例は次のようになります。

class Product { (1)
  MonetaryAmount getPrice() {  …  }
}

@RequiredArgConstructor(staticName = "of")
class Products implements Streamable<Product> { (2)

  private Streamable<Product> streamable;

  public MonetaryAmount getTotal() { (3)
    return streamable.stream() //
      .map(Priced::getPrice)
      .reduce(Money.of(0), MonetaryAmount::add);
  }
}

interface ProductRepository implements Repository<Product, Long> {
  Products findAllByDescriptionContaining(String text); (4)
}
1 製品の価格にアクセスするための API を公開する Product エンティティ。
2Products.of(…) (Lombok アノテーションを介して作成されたファクトリメソッド)を介して構築できる Streamable<Product> のラッパータイプ。
3 ラッパータイプは、Streamable<Product> の新しい値を計算する追加の API を公開します。
4 そのラッパー型は、クエリメソッドの戻り型として直接使用できます。Stremable<Product> を返し、リポジトリクライアントで手動でラップする必要はありません。
Vavr コレクションのサポート

Vavr (英語) は、Java で関数型プログラミングの概念を取り入れるライブラリです。クエリメソッドの戻り値の型として使用できるコレクション型のカスタムセットが付属しています。

Vavr コレクションタイプ 使用される Vavr 実装タイプ 有効な Java ソースタイプ

io.vavr.collection.Seq

io.vavr.collection.List

java.util.Iterable

io.vavr.collection.Set

io.vavr.collection.LinkedHashSet

java.util.Iterable

io.vavr.collection.Map

io.vavr.collection.LinkedHashMap

java.util.Map

最初の列の型(またはそのサブタイプ)は quer メソッドの戻り型として使用でき、実際のクエリ結果(3 番目の列)の Java 型に応じて実装型として使用される 2 番目の列の型を取得します。または、Traversable (Vavr Iterable 相当)を宣言して、実際の戻り値から実装クラスを導出できます。つまり、java.util.List は Vavr List/Seq に、java.util.Set は Vavr LinkedHashSet/Set などになります。

4.4.7. リポジトリメソッドの null 処理

Spring Data 2.0, リポジトリの時点で、個々の集約インスタンスを返す CRUD メソッドは、Java 8 の Optional を使用して、値が存在しない可能性があることを示します。さらに、Spring Data はクエリメソッドで次のラッパータイプを返すことをサポートしています。

  • com.google.common.base.Optional

  • scala.Option

  • io.vavr.control.Option

または、クエリメソッドは、ラッパータイプをまったく使用しないことを選択できます。null を返すことにより、クエリ結果がないことが示されます。コレクション、コレクションの代替、ラッパー、およびストリームを返すリポジトリメソッドは、null ではなく、対応する空の表現を返すことが保証されています。詳細については、「リポジトリクエリの戻り値のタイプ」を参照してください。

Nullability アノテーション

Spring Framework の nullability アノテーションを使用して、リポジトリメソッドの nullability 制約を表現できます。これらは、次のように、実行時にツールに優しいアプローチとオプトイン null チェックを提供します。

  • @NonNullApi(Javadoc) : パッケージレベルで使用され、パラメーターと戻り値のデフォルトの動作が null 値を受け入れない、または生成しないことを宣言します。

  • @NonNull(Javadoc) null であってはならないパラメーターまたは戻り値で使用されます(@NonNullApi が適用されるパラメーターおよび戻り値では不要です)。

  • @Nullable(Javadoc) null の可能性があるパラメーターまたは戻り値で使用されます。

Spring アノテーションには、JSR 305 (英語) アノテーション(休止状態ですが、広く普及している JSR)でメタアノテーションが付けられています。JSR 305 メタアノテーションにより、IDEAEclipse (英語) 、および Kotlin (英語) などのツールベンダーは、Spring アノテーションのサポートをハードコードすることなく、一般的な方法で null 安全サポートを提供できます。クエリメソッドの nullability 制約の実行時チェックを有効にするには、次の例に示すように、package-info.java で Spring の @NonNullApi を使用して、パッケージレベルで null 禁止を有効にする必要があります。

例 20: package-info.java で非 null 可能性を宣言する
@org.springframework.lang.NonNullApi
package com.acme;

null 以外のデフォルトが設定されると、リポジトリクエリメソッドの呼び出しは、null 許容性の制約について実行時に検証されます。クエリの実行結果が定義された制約に違反すると、例外がスローされます。これは、メソッドが null を返すが、null 不可として宣言されている場合に発生します(リポジトリが存在するパッケージで定義されたアノテーションのデフォルト)。null 可能結果に再度オプトインする場合は、個々のメソッドで @Nullable を選択的に使用します。このセクションの冒頭で説明した結果ラッパータイプの使用は、引き続き期待どおりに機能します。空の結果は、不在を表す値に変換されます。

次の例は、今説明したいくつかの手法を示しています。

例 21: さまざまな null 値制約を使用する
package com.acme;                                                       (1)

import org.springframework.lang.Nullable;

interface UserRepository extends Repository<User, Long> {

  User getByEmailAddress(EmailAddress emailAddress);                    (2)

  @Nullable
  User findByEmailAddress(@Nullable EmailAddress emailAdress);          (3)

  Optional<User> findOptionalByEmailAddress(EmailAddress emailAddress); (4)
}
1 リポジトリは、null 以外の動作を定義したパッケージ(またはサブパッケージ)にあります。
2 実行されたクエリが結果を生成しない場合、EmptyResultDataAccessException をスローします。メソッドに渡された emailAddress が null の場合、IllegalArgumentException をスローします。
3 実行されたクエリが結果を生成しない場合、null を返します。emailAddress の値として null も受け入れます。
4 実行されたクエリが結果を生成しない場合、Optional.empty() を返します。メソッドに渡された emailAddress が null の場合、IllegalArgumentException をスローします。
Kotlin ベースのリポジトリの Nullability

Kotlin には、言語に組み込まれた null 可能性制約 (英語) の定義があります。Kotlin コードはバイトコードにコンパイルされます。これは、メソッドシグネチャーではなく、コンパイルされたメタデータを通じて nullability 制約を表現しません。kotlin-reflect JAR をプロジェクトに含めて、Kotlin の nullability 制約のイントロスペクションを有効にしてください。Spring Data リポジトリは、言語メカニズムを使用してこれらの制約を定義し、次のように同じランタイムチェックを適用します。

例 22: Kotlin リポジトリでの null 可能性制約の使用
interface UserRepository : Repository<User, String> {

  fun findByUsername(username: String): User     (1)

  fun findByFirstname(firstname: String?): User? (2)
}
1 このメソッドは、パラメーターと結果の両方を null 不可(Kotlin のデフォルト)として定義します。Kotlin コンパイラーは、null をメソッドに渡すメソッド呼び出しを拒否します。クエリの実行結果が空の場合、EmptyResultDataAccessException がスローされます。
2 このメソッドは、firstname パラメーターの null を受け入れ、クエリの実行で結果が生成されない場合は null を返します。

4.4.8. クエリ結果のストリーミング

Java 8 Stream<T> を戻り型として使用することにより、クエリメソッドの結果をインクリメンタルに処理できます。次の例に示すように、クエリ結果を Stream データストアにラップする代わりに、ストリーミングを実行するために特定のメソッドが使用されます。

例 23: Java 8 Stream<T> を使用したクエリの結果のストリーミング
@Query("select u from User u")
Stream<User> findAllByCustomQueryAndStream();

Stream<User> readAllByFirstnameNotNull();

@Query("select u from User u")
Stream<User> streamAllPaged(Pageable pageable);
Stream は潜在的に基礎となるデータストア固有のリソースをラップするため、使用後に閉じる必要があります。次の例に示すように、close() メソッドを使用するか、Java 7 try-with-resources ブロックを使用して、Stream を手動で閉じることができます。
例 24: Stream<T> を使用すると、try-with-resources ブロックが生成されます
try (Stream<User> stream = repository.findAllByCustomQueryAndStream()) {
  stream.forEach(…);
}
現在、すべての Spring Data モジュールが戻り型として Stream<T> をサポートしているわけではありません。

4.4.9. 非同期クエリの結果

Spring の非同期メソッド実行機能を使用して、リポジトリクエリを非同期に実行できます。これは、Spring TaskExecutor に送信されたタスクで実際のクエリ実行が行われている間に、メソッドが呼び出し時にすぐに戻ることを意味します。非同期クエリの実行は、リアクティブクエリの実行とは異なるため、混在させないでください。リアクティブサポートの詳細については、ストア固有のドキュメントを参照してください。次の例は、いくつかの非同期クエリを示しています。

@Async
Future<User> findByFirstname(String firstname);               (1)

@Async
CompletableFuture<User> findOneByFirstname(String firstname); (2)

@Async
ListenableFuture<User> findOneByLastname(String lastname);    (3)
1 戻り型として java.util.concurrent.Future を使用します。
2 戻り型として Java 8 java.util.concurrent.CompletableFuture を使用します。
3 戻り型として org.springframework.util.concurrent.ListenableFuture を使用します。

4.5. リポジトリインスタンスの作成

このセクションでは、定義されたリポジトリインターフェースのインスタンスと Bean 定義を作成します。そのための 1 つの方法は、リポジトリメカニズムをサポートする各 Spring Data モジュールに同梱されている Spring 名前空間を使用することです。ただし、通常は Java 構成を使用することをお勧めします。

4.5.1. XML 構成

次の例に示すように、各 Spring Data モジュールには、Spring がスキャンする基本パッケージを定義できる repositories 要素が含まれています。

例 25: XML を介した Spring Data リポジトリの有効化
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="http://www.springframework.org/schema/data/jpa"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/data/jpa
    https://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

  <repositories base-package="com.acme.repositories" />

</beans:beans>

上記の例では、Spring は com.acme.repositories とそのすべてのサブパッケージをスキャンして、Repository を継承するインターフェースまたはそのサブインターフェースの 1 つを探すように指示されています。見つかったインターフェースごとに、インフラストラクチャは永続化テクノロジー固有の FactoryBean を登録して、クエリメソッドの呼び出しを処理する適切なプロキシを作成します。各 Bean は、インターフェース名から派生した Bean 名で登録されるため、UserRepository のインターフェースは userRepository で登録されます。ネストされたリポジトリインターフェースの Bean 名には、囲む型名がプレフィックスとして付加されます。base-package 属性ではワイルドカードを使用できるため、スキャンしたパッケージのパターンを定義できます。

フィルターを使う

デフォルトでは、インフラストラクチャは、設定されたベースパッケージにある永続化技術固有の Repository サブインターフェースを継承するすべてのインターフェースをピックアップし、Bean インスタンスを作成します。ただし、Bean インスタンスを作成するインターフェースをより詳細に制御したい場合があります。そのためには、<repositories /> 要素内で <include-filter /> および <exclude-filter /> 要素を使用します。セマンティクスは、Spring のコンテキスト名前空間の要素とまったく同じです。詳細については、これらの要素の Spring リファレンスドキュメントを参照してください。

例:特定のインターフェースをリポジトリ Bean としてインスタンス化から除外するには、次の構成を使用できます。

例 26: exclude-filter 要素を使用する
<repositories base-package="com.acme.repositories">
  <context:exclude-filter type="regex" expression=".*SomeRepository" />
</repositories>

上記の例は、SomeRepository で終わるすべてのインターフェースをインスタンス化から除外します。

4.5.2. JavaConfig

リポジトリインフラストラクチャは、JavaConfig クラスでストア固有の @Enable${store}Repositories アノテーションを使用してトリガーすることもできます。Spring コンテナーの Java ベースの構成の概要については、Spring リファレンスドキュメントの JavaConfig を参照してください。

Spring Data リポジトリを有効にするサンプル構成は次のようになります。

例 27: アノテーションベースのリポジトリ構成のサンプル
@Configuration
@EnableJpaRepositories("com.acme.repositories")
class ApplicationConfiguration {

  @Bean
  EntityManagerFactory entityManagerFactory() {
    //  …
  }
}
上記の例では、JPA 固有のアノテーションを使用しています。これは、実際に使用するストアモジュールに応じて変更します。同じことが EntityManagerFactory Bean の定義にも当てはまります。ストア固有の構成について説明しているセクションを参照してください。

4.5.3. スタンドアロン使用

Spring コンテナーの外部(たとえば、CDI 環境)でリポジトリインフラストラクチャを使用することもできます。クラスパスにはまだ Spring ライブラリがいくつか必要ですが、通常、プログラムでリポジトリを設定することもできます。リポジトリサポートを提供する Spring Data モジュールには、次のように使用できる永続化技術固有の RepositoryFactory が付属しています。

例 28: リポジトリファクトリのスタンドアロン使用
RepositoryFactorySupport factory =  …  // Instantiate factory here
UserRepository repository = factory.getRepository(UserRepository.class);

4.6. Spring Data リポジトリのカスタム実装

このセクションでは、リポジトリのカスタマイズと、フラグメントが複合リポジトリを形成する方法について説明します。

クエリメソッドが別の動作を必要とする場合、またはクエリ派生によって実装できない場合は、カスタム実装を提供する必要があります。Spring Data リポジトリを使用すると、カスタムリポジトリコードを提供し、一般的な CRUD 抽象化およびクエリメソッド機能と統合できます。

4.6.1. 個々のリポジトリのカスタマイズ

リポジトリにカスタム機能を追加するには、次の例に示すように、最初にフラグメントインターフェースとカスタム機能の実装を定義する必要があります。

例 29: カスタムリポジトリ機能のインターフェース
interface CustomizedUserRepository {
  void someCustomMethod(User user);
}
例 30: カスタムリポジトリ機能の実装
class CustomizedUserRepositoryImpl implements CustomizedUserRepository {

  public void someCustomMethod(User user) {
    // Your custom implementation
  }
}
フラグメントインターフェースに対応するクラス名の最も重要な部分は、Impl 後置です。

実装自体は Spring Data に依存せず、通常の Spring Bean にすることができます。そのため、標準の依存性注入動作を使用して、他の Bean(JdbcTemplate など)への参照を注入したり、アスペクトに参加したりすることができます。

次に、次の例に示すように、リポジトリインターフェースでフラグメントインターフェースを継承できます。

例 31: リポジトリインターフェースの変更
interface UserRepository extends CrudRepository<User, Long>, CustomizedUserRepository {

  // Declare query methods here
}

リポジトリインターフェースでフラグメントインターフェースを拡張すると、CRUD とカスタム機能が組み合わされ、クライアントで使用できるようになります。

Spring Data リポジトリは、リポジトリ構成を形成するフラグメントを使用して実装されます。フラグメントは、ベースリポジトリ、関数側面(QueryDsl など)、およびカスタムインターフェースとその実装です。リポジトリインターフェースにインターフェースを追加するたびに、フラグメントを追加して構成を強化します。ベースリポジトリおよびリポジトリアスペクトの実装は、各 Spring Data モジュールによって提供されます。

次の例は、カスタムインターフェースとその実装を示しています。

例 32: 実装のフラグメント
interface HumanRepository {
  void someHumanMethod(User user);
}

class HumanRepositoryImpl implements HumanRepository {

  public void someHumanMethod(User user) {
    // Your custom implementation
  }
}

interface ContactRepository {

  void someContactMethod(User user);

  User anotherContactMethod(User user);
}

class ContactRepositoryImpl implements ContactRepository {

  public void someContactMethod(User user) {
    // Your custom implementation
  }

  public User anotherContactMethod(User user) {
    // Your custom implementation
  }
}

次の例は、CrudRepository を継承するカスタムリポジトリのインターフェースを示しています。

例 33: リポジトリインターフェースの変更
interface UserRepository extends CrudRepository<User, Long>, HumanRepository, ContactRepository {

  // Declare query methods here
}

リポジトリは、宣言の順序でインポートされる複数のカスタム実装で構成されます。カスタム実装は、基本実装およびリポジトリの側面よりも優先度が高くなります。この順序付けにより、ベースリポジトリおよびアスペクトメソッドをオーバーライドし、2 つのフラグメントが同じメソッドシグネチャーを提供する場合のあいまいさを解決できます。リポジトリフラグメントは、単一のリポジトリインターフェースでの使用に限定されません。複数のリポジトリがフラグメントインターフェースを使用し、異なるリポジトリでカスタマイズを再利用できる場合があります。

次の例は、リポジトリフラグメントとその実装を示しています。

例 34: save(…) をオーバーライドするフラグメント
interface CustomizedSave<T> {
  <S extends T> S save(S entity);
}

class CustomizedSaveImpl<T> implements CustomizedSave<T> {

  public <S extends T> S save(S entity) {
    // Your custom implementation
  }
}

次の例は、前述のリポジトリフラグメントを使用するリポジトリを示しています。

例 35: カスタマイズされたリポジトリインターフェース
interface UserRepository extends CrudRepository<User, Long>, CustomizedSave<User> {
}

interface PersonRepository extends CrudRepository<Person, Long>, CustomizedSave<Person> {
}
構成

名前空間構成を使用する場合、リポジトリインフラストラクチャは、リポジトリが見つかったパッケージのクラスをスキャンすることにより、カスタム実装フラグメントを自動検出しようとします。これらのクラスは、名前空間要素の repository-impl-postfix 属性をフラグメントインターフェース名に追加する命名規則に従う必要があります。この接尾辞のデフォルトは Impl です。次の例は、デフォルトの接尾辞を使用するリポジトリと、接尾辞のカスタム値を設定するリポジトリを示しています。

例 36: 構成例
<repositories base-package="com.acme.repository" />

<repositories base-package="com.acme.repository" repository-impl-postfix="MyPostfix" />

前の例の最初の構成は、com.acme.repository.CustomizedUserRepositoryImpl と呼ばれるクラスを検索して、カスタムリポジトリ実装として機能しようとします。2 番目の例では、com.acme.repository.CustomizedUserRepositoryMyPostfix を検索しようとします。

あいまいさの解決

一致するクラス名を持つ複数の実装が異なるパッケージで見つかった場合、Spring Data は Bean 名を使用して、使用する実装を識別します。

前に示した CustomizedUserRepository の次の 2 つのカスタム実装を考えると、最初の実装が使用されます。その Bean 名は customizedUserRepositoryImpl であり、これはフラグメントインターフェース(CustomizedUserRepository)の名前と接尾辞 Impl に一致します。

例 37: あいまいな実装の解決
package com.acme.impl.one;

class CustomizedUserRepositoryImpl implements CustomizedUserRepository {

  // Your custom implementation
}
package com.acme.impl.two;

@Component("specialCustomImpl")
class CustomizedUserRepositoryImpl implements CustomizedUserRepository {

  // Your custom implementation
}

UserRepository インターフェースに @Component("specialCustom") でアノテーションを付けると、Bean 名に Impl を加えたものが、com.acme.impl.two のリポジトリ実装用に定義されたものと一致し、最初のものの代わりに使用されます。

手動接続

カスタム実装でアノテーションベースの構成とオートワイヤーのみを使用する場合、上記のアプローチは他の Spring Bean と同様に処理されるため、上手く機能します。実装フラグメント Bean に特別な接続が必要な場合、Bean を宣言し、前のセクションで説明した規則に従って名前を付けることができます。インフラストラクチャは、Bean 定義を手動で作成する代わりに、名前で手動で定義したものを参照します。次の例は、カスタム実装を手動で接続する方法を示しています。

例 38: カスタム実装の手動接続
<repositories base-package="com.acme.repository" />

<beans:bean id="userRepositoryImpl" class=" … ">
  <!-- further configuration -->
</beans:bean>

4.6.2. ベースリポジトリをカスタマイズする

前のセクションで説明したアプローチでは、ベースリポジトリの動作をカスタマイズしてすべてのリポジトリが影響を受けるようにする場合、各リポジトリインターフェースをカスタマイズする必要があります。代わりに、すべてのリポジトリの動作を変更するために、永続化テクノロジ固有のリポジトリベースクラスを継承する実装を作成できます。このクラスは、次の例に示すように、リポジトリプロキシのカスタムベースクラスとして機能します。

例 39: カスタムリポジトリベースクラス
class MyRepositoryImpl<T, ID>
  extends SimpleJpaRepository<T, ID> {

  private final EntityManager entityManager;

  MyRepositoryImpl(JpaEntityInformation entityInformation,
                          EntityManager entityManager) {
    super(entityInformation, entityManager);

    // Keep the EntityManager around to used from the newly introduced methods.
    this.entityManager = entityManager;
  }

  @Transactional
  public <S extends T> S save(S entity) {
    // implementation goes here
  }
}
このクラスには、ストア固有のリポジトリファクトリ実装が使用するスーパークラスのコンストラクターが必要です。リポジトリの基本クラスに複数のコンストラクターがある場合は、EntityInformation とストア固有のインフラストラクチャオブジェクト(EntityManager またはテンプレートクラスなど)を取得するコンストラクターをオーバーライドします。

最後のステップは、Spring Data インフラストラクチャーにカスタマイズされたリポジトリ基本クラスを認識させることです。Java 構成では、次の例に示すように、@Enable${store}Repositories アノテーションの repositoryBaseClass 属性を使用してこれを行うことができます。

例 40: JavaConfig を使用したカスタムリポジトリベースクラスの構成
@Configuration
@EnableJpaRepositories(repositoryBaseClass = MyRepositoryImpl.class)
class ApplicationConfiguration {  …  }

次の例に示すように、XML 名前空間で対応する属性を使用できます。

例 41: XML を使用したカスタムリポジトリベースクラスの構成
<repositories base-package="com.acme.repository"
     base-class=" … .MyRepositoryImpl" />

4.7. 集約ルートからのイベントの公開

リポジトリによって管理されるエンティティは、集約ルートです。ドメイン駆動設計アプリケーションでは、これらの集約ルートは通常、ドメインイベントを発行します。Spring Data は、@DomainEvents と呼ばれるアノテーションを提供します。これは、次の例に示すように、集約パブリケーションのメソッドで使用して、その公開をできるだけ簡単にすることができます。

例 42: 集約ルートからのドメインイベントの公開
class AnAggregateRoot {

    @DomainEvents (1)
    Collection<Object> domainEvents() {
        //  …  return events you want to get published here
    }

    @AfterDomainEventPublication (2)
    void callbackMethod() {
       //  …  potentially clean up domain events list
    }
}
1@DomainEvents を使用するメソッドは、単一のイベントインスタンスまたはイベントのコレクションを返すことができます。引数を取ることはできません。
2 すべてのイベントが公開された後、@AfterDomainEventPublication アノテーションが付けられたメソッドがあります。(他の用途の中でも)公開されるイベントのリストを潜在的に消去するために使用できます。

メソッドは、Spring Data リポジトリの save(…)saveAll(…)delete(…) または deleteAll(…) メソッドのいずれかが呼び出されるたびに呼び出されます。

4.8. Spring Data 拡張

このセクションでは、さまざまなコンテキストで Spring Data を使用できるようにする一連の Spring Data 拡張について説明します。現在、統合のほとんどは Spring MVC を対象としています。

4.8.1. Querydsl 拡張

Querydsl (英語) は、流れるような API を使用して、静的に型指定された SQL のようなクエリの構築を可能にするフレームワークです。

次の例に示すように、いくつかの Spring Data モジュールは QuerydslPredicateExecutor を介して Querydsl との統合を提供します。

例 43: QuerydslPredicateExecutor インターフェース
public interface QuerydslPredicateExecutor<T> {

  Optional<T> findById(Predicate predicate);  (1)

  Iterable<T> findAll(Predicate predicate);   (2)

  long count(Predicate predicate);            (3)

  boolean exists(Predicate predicate);        (4)

  //  …  more functionality omitted.
}
1Predicate に一致する単一のエンティティを検索して返します。
2Predicate に一致するすべてのエンティティを検索して返します。
3Predicate に一致するエンティティの数を返します。
4Predicate に一致するエンティティが存在するかどうかを返します。

Querydsl サポートを利用するには、次の例に示すように、リポジトリインターフェースで QuerydslPredicateExecutor を継承します。

例 44: リポジトリでの Querydsl 統合
interface UserRepository extends CrudRepository<User, Long>, QuerydslPredicateExecutor<User> {
}

上記の例では、次の例に示すように、Querydsl Predicate インスタンスを使用してタイプセーフクエリを記述できます。

Predicate predicate = user.firstname.equalsIgnoreCase("dave")
	.and(user.lastname.startsWithIgnoreCase("mathews"));

userRepository.findAll(predicate);

4.8.2. Web サポート

このセクションには、Spring Data Commons の現在(およびそれ以降)のバージョンに実装されている Spring Data Web サポートのドキュメントが含まれています。新しく導入されたサポートは多くの点を変更するため、[web.legacy] で以前の動作のドキュメントを保持しました。

リポジトリプログラミングモデルをサポートする Spring Data モジュールには、さまざまな Web サポートが付属しています。Web 関連のコンポーネントでは、Spring MVC JAR がクラスパス上にある必要があります。それらのいくつかは Spring HATEOAS (GitHub) との統合さえ提供します。一般に、次の例に示すように、JavaConfig 構成クラスで @EnableSpringDataWebSupport アノテーションを使用することにより、統合サポートが有効になります。

例 45: Spring Data Web サポートの有効化
@Configuration
@EnableWebMvc
@EnableSpringDataWebSupport
class WebConfiguration {}

@EnableSpringDataWebSupport アノテーションは、少し説明するいくつかのコンポーネントを登録します。また、クラスパスで Spring HATEOAS を検出し、存在する場合は同様に統合コンポーネントを登録します。

あるいは、XML 構成を使用する場合、次の例(SpringDataWebConfiguration の場合)に示すように、SpringDataWebConfiguration または HateoasAwareSpringDataWebConfiguration を Spring Bean として登録します。

例 46: XML で Spring Data Web サポートを有効にする
<bean class="org.springframework.data.web.config.SpringDataWebConfiguration" />

<!-- If you use Spring HATEOAS, register this one *instead* of the former -->
<bean class="org.springframework.data.web.config.HateoasAwareSpringDataWebConfiguration" />
基本的な Web サポート

前のセクションで示した構成は、いくつかの基本的なコンポーネントを登録します。

  • Spring MVC がリクエストパラメーターまたはパス変数からリポジトリ管理ドメインクラスのインスタンスを解決できるようにする DomainClassConverter

  • Spring MVC がリクエストパラメーターから Pageable および Sort インスタンスを解決できるようにする HandlerMethodArgumentResolver 実装。

DomainClassConverter

DomainClassConverter では、Spring MVC コントローラーメソッドシグネチャーでドメインタイプを直接使用できるため、次の例に示すように、リポジトリを介してインスタンスを手動で検索する必要はありません。

例 47: メソッドシグネチャーでドメインタイプを使用する Spring MVC コントローラー
@Controller
@RequestMapping("/users")
class UserController {

  @RequestMapping("/{id}")
  String showUserForm(@PathVariable("id") User user, Model model) {

    model.addAttribute("user", user);
    return "userForm";
  }
}

ご覧のように、このメソッドは User インスタンスを直接受け取り、それ以上のルックアップは必要ありません。インスタンスは、Spring MVC が最初にパス変数をドメインクラスの id タイプに変換し、最終的にドメインタイプに登録されたリポジトリインスタンスで findById(…) を呼び出してインスタンスにアクセスすることで解決できます。

現在、リポジトリは変換のために発見される資格があるために CrudRepository を実装しなければなりません。
ページング可能およびソート用の HandlerMethodArgumentResolvers

前のセクションで示した構成スニペットは、PageableHandlerMethodArgumentResolver と SortHandlerMethodArgumentResolver のインスタンスも登録します。次の例に示すように、登録により、Pageable および Sort が有効なコントローラーメソッド引数として有効になります。

例 48: コントローラーメソッドの引数として Pageable を使用する
@Controller
@RequestMapping("/users")
class UserController {

  private final UserRepository repository;

  UserController(UserRepository repository) {
    this.repository = repository;
  }

  @RequestMapping
  String showUsers(Model model, Pageable pageable) {

    model.addAttribute("users", repository.findAll(pageable));
    return "users";
  }
}

上記のメソッドシグネチャーにより、Spring MVC は、次のデフォルト構成を使用して、リクエストパラメーターから Pageable インスタンスを取得しようとします。

表 1: Pageable インスタンスについて評価されたリクエストパラメーター

page

取得するページ。0 からインデックス付けされ、デフォルトは 0 です。

size

取得するページのサイズ。デフォルトは 20 です。

sort

property,property(,ASC|DESC)(,IgnoreCase) 形式でソートする必要があるプロパティ。デフォルトのソート方向は、大文字と小文字が区別される昇順です。?sort=firstname&sort=lastname,asc&sort=city,ignorecase など、方向や大文字と小文字を区別する場合は、複数の sort パラメーターを使用します。

この動作をカスタマイズするには、PageableHandlerMethodArgumentResolverCustomizer インターフェースまたは SortHandlerMethodArgumentResolverCustomizer インターフェースをそれぞれ実装する Bean を登録します。次の例に示すように、customize() メソッドが呼び出され、設定を変更できます。

@Bean SortHandlerMethodArgumentResolverCustomizer sortCustomizer() {
    return s -> s.setPropertyDelimiter("<-->");
}

既存の MethodArgumentResolver のプロパティを設定するだけでは目的に合わない場合は、SpringDataWebConfiguration または HATEOAS 対応の拡張機能を継承し、pageableResolver() または sortResolver() メソッドをオーバーライドし、@Enable アノテーションを使用する代わりにカスタマイズした構成ファイルをインポートします。

リクエストから複数の Pageable または Sort インスタンスを解決する必要がある場合(たとえば、複数のテーブルの場合)、Spring の @Qualifier アノテーションを使用して互いに区別できます。次に、リクエストパラメーターに ${qualifier}_ をプレフィックスとして付加する必要があります。次の例は、結果のメソッドシグネチャーを示しています。

String showUsers(Model model,
      @Qualifier("thing1") Pageable first,
      @Qualifier("thing2") Pageable second) {  …  }

thing1_page と thing2_page などを入力する必要があります。

メソッドに渡されるデフォルト Pageable は PageRequest.of(0, 20) と同等ですが、Pageable パラメーターで @PageableDefault アノテーションを使用してカスタマイズできます。

Pageable のハイパーメディアサポート

Spring HATEOAS には、クライアントがページを簡単にナビゲートできるようにするためのリンクだけでなく、必要な Page メタデータと Page インスタンスのコンテンツを充実させる表現モデルクラス(PagedResources)が付属しています。Page から PagedResources への変換は、PagedResourcesAssembler と呼ばれる Spring HATEOAS ResourceAssembler インターフェースの実装によって行われます。次の例は、PagedResourcesAssembler をコントローラーメソッドの引数として使用する方法を示しています。

例 49: コントローラーメソッドの引数として PagedResourcesAssembler を使用する
@Controller
class PersonController {

  @Autowired PersonRepository repository;

  @RequestMapping(value = "/persons", method = RequestMethod.GET)
  HttpEntity<PagedResources<Person>> persons(Pageable pageable,
    PagedResourcesAssembler assembler) {

    Page<Person> persons = repository.findAll(pageable);
    return new ResponseEntity<>(assembler.toResources(persons), HttpStatus.OK);
  }
}

前の例に示すように構成を有効にすると、PagedResourcesAssembler をコントローラーメソッドの引数として使用できます。toResources(…) を呼び出すと、次の効果があります。

  • Page のコンテンツは、PagedResources インスタンスのコンテンツになります。

  • PagedResources オブジェクトは PageMetadata インスタンスをアタッチし、Page および基礎となる PageRequest からの情報が取り込まれます。

  • PagedResources には、ページの状態に応じて、prev および next リンクが添付される場合があります。リンクは、メソッドがマップする URI を指します。メソッドに追加されたページネーションパラメーターは、PageableHandlerMethodArgumentResolver の設定と一致して、リンクを後で解決できるようにします。

データベースに 30 個の Person インスタンスがあるとします。これで、リクエスト(GET http://localhost:8080/persons)をトリガーして、次のような出力を表示できます。

{ "links" : [ { "rel" : "next",
                "href" : "http://localhost:8080/persons?page=1&size=20 }
  ],
  "content" : [
      …  // 20 Person instances rendered here
  ],
  "pageMetadata" : {
    "size" : 20,
    "totalElements" : 30,
    "totalPages" : 2,
    "number" : 0
  }
}

アセンブラーが正しい URI を生成し、デフォルトの構成を選択して、今後のリクエストのためにパラメーターを Pageable に解決することがわかります。つまり、その構成を変更すると、リンクは自動的にその変更を順守します。デフォルトでは、アセンブラはそれが呼び出されたコントローラーメソッドを指しますが、PagedResourcesAssembler.toResource(…) メソッドをオーバーロードするページネーションリンクを構築するためのベースとして使用されるカスタム Link を渡すことでカスタマイズできます。

Web データバインディングのサポート

Spring Data 射影(射影で説明)は、次の例に示すように、JSONPath (英語) 式(Jayway JsonPath (GitHub) または XPath (英語) 式が必要(XmlBeam (英語) が必要))を使用して、受信リクエストペイロードをバインドするために使用できます。

例 50: JSONPath または XPath 式を使用した HTTP ペイロードバインディング
@ProjectedPayload
public interface UserPayload {

  @XBRead("//firstname")
  @JsonPath("$..firstname")
  String getFirstname();

  @XBRead("/lastname")
  @JsonPath({ "$.lastname", "$.user.lastname" })
  String getLastname();
}

前の例に示した型は、Spring MVC ハンドラーメソッドの引数として、または RestTemplate のメソッドの 1 つで ParameterizedTypeReference を使用して使用できます。上記のメソッド宣言は、指定されたドキュメント内の任意の場所で firstname を見つけようとします。lastname XML ルックアップは、受信ドキュメントの最上位で実行されます。その JSON バリアントは、最初に最上位の lastname を試行しますが、前者が値を返さない場合は user サブドキュメントにネストされた lastname も試行します。これにより、クライアントが公開メソッドを呼び出すことなく、ソースドキュメントの構造の変更を簡単に軽減できます(通常、クラスベースのペイロードバインディングの欠点)。

ネストされた射影は、射影に従ってサポートされます。メソッドがインターフェース以外の複雑なタイプを返す場合、Jackson ObjectMapper が最閉じのマッピングに使用されます。

Spring MVC の場合、@EnableSpringDataWebSupport がアクティブになり、必要な依存関係がクラスパスで利用可能になるとすぐに、必要なコンバーターが自動的に登録されます。RestTemplate で使用するには、ProjectingJackson2HttpMessageConverter (JSON)または XmlBeamHttpMessageConverter を手動で登録します。

詳細については、標準の Spring Data サンプルリポジトリ (GitHub) Web 射影の例 (GitHub) を参照してください。

Querydsl Web サポート

QueryDSL (英語) が統合されているストアの場合、Request クエリ文字列に含まれる属性からクエリを派生させることができます。

次のクエリ文字列を検討してください。

?firstname=Dave&lastname=Matthews

前の例の User オブジェクトを考えると、QuerydslPredicateArgumentResolver を使用してクエリ文字列を次の値に解決できます。

QUser.user.firstname.eq("Dave").and(QUser.user.lastname.eq("Matthews"))
Querydsl がクラスパスで見つかると、この機能は @EnableSpringDataWebSupport とともに自動的に有効になります。

@QuerydslPredicate をメソッドシグネチャーに追加すると、すぐに使用できる Predicate が提供されます。これは、QuerydslPredicateExecutor を使用して実行できます。

型情報は通常、メソッドの戻り値型から解決されます。その情報は必ずしもドメインタイプと一致しないため、QuerydslPredicate の root 属性を使用することをお勧めします。

次の例は、メソッドシグネチャーで @QuerydslPredicate を使用する方法を示しています。

@Controller
class UserController {

  @Autowired UserRepository repository;

  @RequestMapping(value = "/", method = RequestMethod.GET)
  String index(Model model, @QuerydslPredicate(root = User.class) Predicate predicate,    (1)
          Pageable pageable, @RequestParam MultiValueMap<String, String> parameters) {

    model.addAttribute("users", repository.findAll(predicate, pageable));

    return "index";
  }
}
1User の一致する Predicate にクエリ文字列引数を解決します。

デフォルトのバインディングは次のとおりです。

  • eq としての単純なプロパティの Object

  • contains のようなプロパティのようなコレクションの Object

  • in としての単純なプロパティの Collection

これらのバインディングは、@QuerydslPredicate の bindings 属性を介して、または Java 8 default methods を使用して、QuerydslBinderCustomizer メソッドをリポジトリインターフェースに追加することによりカスタマイズできます。

interface UserRepository extends CrudRepository<User, String>,
                                 QuerydslPredicateExecutor<User>,                (1)
                                 QuerydslBinderCustomizer<QUser> {               (2)

  @Override
  default void customize(QuerydslBindings bindings, QUser user) {

    bindings.bind(user.username).first((path, value) -> path.contains(value))    (3)
    bindings.bind(String.class)
      .first((StringPath path, String value) -> path.containsIgnoreCase(value)); (4)
    bindings.excluding(user.password);                                           (5)
  }
}
1QuerydslPredicateExecutor は、Predicate の特定のファインダーメソッドへのアクセスを提供します。
2 リポジトリインターフェースで定義された QuerydslBinderCustomizer が自動的に選択され、ショートカット @QuerydslPredicate(bindings=…​) が選択されます。
3username プロパティのバインディングを単純な contains バインディングとして定義します。
4String プロパティのデフォルトのバインディングを、大文字と小文字を区別しない contains 一致になるように定義します。
5password プロパティを Predicate 解決から除外します。

4.8.3. リポジトリポピュレーター

Spring JDBC モジュールを使用している場合は、DataSource に SQL スクリプトを取り込むサポートに精通していると思われます。リポジトリレベルでも同様の抽象化を使用できますが、ストアに依存しないため、SQL をデータ定義言語として使用しません。ポピュレーターは、XML(Spring の OXM 抽象化による)および JSON(Jackson による)をサポートして、リポジトリに取り込むデータを定義します。

次の内容のファイル data.json があると仮定します。

例 51: JSON で定義されたデータ
[ { "_class" : "com.acme.Person",
 "firstname" : "Dave",
  "lastname" : "Matthews" },
  { "_class" : "com.acme.Person",
 "firstname" : "Carter",
  "lastname" : "Beauford" } ]

Spring Data Commons で提供されるリポジトリ名前空間の populator 要素を使用して、リポジトリにデータを追加できます。上記のデータを PersonRepository に取り込むには、次のようなポピュレーターを宣言します。

例 52: Jackson リポジトリポピュレーターの宣言
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:repository="http://www.springframework.org/schema/data/repository"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/data/repository
    https://www.springframework.org/schema/data/repository/spring-repository.xsd">

  <repository:jackson2-populator locations="classpath:data.json" />

</beans>

上記の宣言により、data.json ファイルは Jackson ObjectMapper によって読み取られ、逆直列化されます。

JSON オブジェクトが非整列化される型は、JSON ドキュメントの _class 属性を調べることで決定されます。インフラストラクチャは最終的に、適切なリポジトリを選択して、デシリアライズされたオブジェクトを処理します。

代わりに XML を使用してリポジトリにデータを取り込む必要があるデータを定義するには、unmarshaller-populator 要素を使用できます。Spring OXM で使用可能な XML マーシャラーオプションの 1 つを使用するように構成します。詳細については、Spring リファレンスドキュメントを参照してください。次の例は、JAXB でリポジトリポピュレーターを非整列化する方法を示しています。

例 53: 非整列化リポジトリポピュレーターの宣言 (JAXB を使用する)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:repository="http://www.springframework.org/schema/data/repository"
  xmlns:oxm="http://www.springframework.org/schema/oxm"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/data/repository
    https://www.springframework.org/schema/data/repository/spring-repository.xsd
    http://www.springframework.org/schema/oxm
    https://www.springframework.org/schema/oxm/spring-oxm.xsd">

  <repository:unmarshaller-populator locations="classpath:data.json"
    unmarshaller-ref="unmarshaller" />

  <oxm:jaxb2-marshaller contextPath="com.acme" />

</beans>

リファレンスドキュメント

5. JPA リポジトリ

この章では、JPA のリポジトリサポートの専門分野について説明します。これは、「Spring Data リポジトリの操作」で説明されているコアリポジトリサポートに基づいています。そこに説明されている基本概念をしっかり理解していることを確認してください。

5.1. 導入

このセクションでは、次のいずれかの方法で Spring Data JPA を構成する基本について説明します。

5.1.1. Spring ネームスペース

Spring Data の JPA モジュールには、リポジトリ Bean を定義できるカスタム名前空間が含まれています。また、JPA に特有の特定の機能と要素属性も含まれています。一般に、次の例に示すように、repositories 要素を使用して JPA リポジトリを設定できます。

例 54: 名前空間を使用して JPA リポジトリを設定する
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:jpa="http://www.springframework.org/schema/data/jpa"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/data/jpa
    https://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

  <jpa:repositories base-package="com.acme.repositories" />

</beans>

repositories エレメントを使用すると、「リポジトリインスタンスの作成」に従って Spring Data リポジトリが検索されます。さらに、@Repository アノテーションが付けられたすべての Bean の永続性例外変換をアクティブにして、JPA 永続性プロバイダーによってスローされる例外を Spring の DataAccessException 階層に変換できるようにします。

カスタム名前空間属性

repositories 要素のデフォルト属性以外に、JPA 名前空間には追加の属性があり、リポジトリのセットアップをより詳細に制御できます。

テーブル 2: repositories 要素のカスタム JPA 固有の属性

entity-manager-factory-ref

EntityManagerFactory を明示的に接続して、repositories 要素によって検出されるリポジトリで使用します。通常、アプリケーション内で複数の EntityManagerFactory Bean が使用される場合に使用されます。構成されていない場合、Spring Data は、ApplicationContext で entityManagerFactory という名前の EntityManagerFactory Bean を自動的に検索します。

transaction-manager-ref

PlatformTransactionManager を明示的に接続して、repositories 要素によって検出されるリポジトリで使用します。通常、複数のトランザクションマネージャーまたは EntityManagerFactory Bean が構成されている場合にのみ必要です。デフォルトは、現在の ApplicationContext 内の単一の定義済み PlatformTransactionManager です。

Spring Data JPA では、明示的な transaction-manager-ref が定義されていない場合、transactionManager という名前の PlatformTransactionManager Bean が存在する必要があります。

5.1.2. アノテーションベースの構成

Spring Data JPA リポジトリのサポートは、次の例に示すように、XML 名前空間だけでなく、JavaConfig を介したアノテーションを使用してアクティブにすることもできます。

例 55: JavaConfig を使用した Spring Data JPA リポジトリ
@Configuration
@EnableJpaRepositories
@EnableTransactionManagement
class ApplicationConfig {

  @Bean
  public DataSource dataSource() {

    EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
    return builder.setType(EmbeddedDatabaseType.HSQL).build();
  }

  @Bean
  public LocalContainerEntityManagerFactoryBean entityManagerFactory() {

    HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
    vendorAdapter.setGenerateDdl(true);

    LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
    factory.setJpaVendorAdapter(vendorAdapter);
    factory.setPackagesToScan("com.acme.domain");
    factory.setDataSource(dataSource());
    return factory;
  }

  @Bean
  public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {

    JpaTransactionManager txManager = new JpaTransactionManager();
    txManager.setEntityManagerFactory(entityManagerFactory);
    return txManager;
  }
}
EntityManagerFactory ではなく LocalContainerEntityManagerFactoryBean を直接作成する必要があります。前者は EntityManagerFactory の作成に加えて例外変換メカニズムにも参加しているためです。

上記の構成クラスは、spring-jdbc の EmbeddedDatabaseBuilder API を使用して、組み込み HSQL データベースをセットアップします。次に、Spring Data は EntityManagerFactory をセットアップし、Hibernate をサンプル永続プロバイダーとして使用します。ここで宣言された最後のインフラストラクチャコンポーネントは JpaTransactionManager です。最後に、この例では、@EnableJpaRepositories アノテーションを使用して Spring Data JPA リポジトリをアクティブ化します。@EnableJpaRepositories アノテーションは、本質的に XML 名前空間と同じ属性を保持します。基本パッケージが構成されていない場合、構成パッケージが存在するパッケージが使用されます。

5.1.3. ブートストラップモード

デフォルトでは、Spring Data JPA リポジトリはデフォルトの Spring Bean です。それらはシングルトンスコープであり、熱心に初期化されます。起動時に、検証およびメタデータ分析のために、すでに JPA EntityManager と対話します。Spring Framework は、バックグラウンドスレッドで JPA EntityManagerFactory の初期化をサポートします。これは、そのプロセスが Spring アプリケーションで通常かなりの量の起動時間を費やしているためです。バックグラウンドの初期化を効果的に使用するには、JPA リポジトリができるだけ遅く初期化されるようにする必要があります。

Spring Data JPA 2.1 以降、次の値を取る BootstrapMode を(@EnableJpaRepositories アノテーションまたは XML 名前空間を介して)構成できるようになりました。

  • DEFAULT (デフォルト) —  リポジトリは、明示的に @Lazy でアノテーションが付けられていない限り、熱心にインスタンス化されます。クライアント Bean がリポジトリ Bean の初期化を必要とするため、リポジトリのインスタンスを必要としない場合にのみ、lazification が有効になります。

  • LAZY —  暗黙的にすべてのリポジトリ Bean を遅延宣言し、また、遅延初期化プロキシを作成してクライアント Bean に注入します。つまり、クライアント Bean がインスタンスをフィールドに単に格納し、初期化中にリポジトリを使用しない場合、そのリポジトリはインスタンス化されません。リポジトリインスタンスは、リポジトリとの最初の対話時に初期化および検証されます。

  • DEFERRED —  基本的に LAZY と同じ動作モードですが、ContextRefreshedEvent にレスポンスしてリポジトリの初期化をトリガーし、アプリケーションが完全に起動する前にリポジトリが検証されるようにします。

推奨

デフォルトのブートストラップモードで非同期 JPA ブートストラップスティックを使用していない場合。

JPA を非同期にブートストラップする場合、DEFERRED は、Spring Data JPA ブートストラップ自体が他のすべてのアプリケーションコンポーネントの初期化よりも時間がかかる場合にのみ EntityManagerFactory セットアップを待機するようにするため、妥当なデフォルトです。それでも、アプリケーションが起動する前に、リポジトリが適切に初期化および検証されるようにします。

LAZY は、テストシナリオおよびローカル開発に適しています。リポジトリが適切にブートストラップされることを確認したら、またはアプリケーションの他の部分をテストする場合、すべてのリポジトリに対して検証を実行すると、起動時間が不必要に長くなる可能性があります。同じことは、単一のリポジトリを初期化するだけでよいアプリケーションの部分にのみアクセスするローカル開発にも当てはまります。

5.2. 永続化エンティティ

このセクションでは、Spring Data JPA でエンティティを永続化(保存)する方法について説明します。

5.2.1. エンティティの保存

エンティティの保存は、CrudRepository.save(…) メソッドを使用して実行できます。基礎となる JPA EntityManager を使用して、指定されたエンティティを永続化またはマージします。エンティティがまだ永続化されていない場合、Spring Data JPA は entityManager.persist(…) メソッドの呼び出しでエンティティを保存します。それ以外の場合は、entityManager.merge(…) メソッドを呼び出します。

エンティティ状態検出戦略

Spring Data JPA は、エンティティが新しいかどうかを検出するために次の戦略を提供します。

  1. バージョンプロパティと ID プロパティインスペクション(デフォルト):デフォルトでは、Spring Data JPA は、非プリミティブ型のバージョンプロパティがあるかどうかを最初にインスペクションします。エンティティが存在する場合、値が null である場合、新しいと見なされます。このようなバージョンプロパティがない場合、Spring Data JPA は指定されたエンティティの識別子プロパティをインスペクションします。識別子プロパティが null の場合、エンティティは新しいと見なされます。それ以外の場合、新規ではないと見なされます。

  2. Persistable の実装 : エンティティが Persistable を実装している場合、Spring Data JPA は新しい検出をエンティティの isNew(…) メソッドに委譲します。詳細については、JavaDoc(Javadoc) を参照してください。

  3. EntityInformation の実装 : JpaRepositoryFactory のサブクラスを作成し、それに応じて getEntityInformation(…) メソッドをオーバーライドすることにより、SimpleJpaRepository 実装で使用される EntityInformation 抽象化をカスタマイズできます。次に、JpaRepositoryFactory のカスタム実装を Spring Bean として登録する必要があります。これはほとんど必要ないことに注意してください。詳細については、JavaDoc (英語) を参照してください。

オプション 1 は、手動で割り当てられた識別子を使用するエンティティのオプションではありません。識別子は常に null でないためです。このシナリオの一般的なパターンは、新しいインスタンスを示すデフォルトの一時フラグを持つ共通の基本クラスを使用し、JPA ライフサイクルコールバックを使用して永続化操作でそのフラグを反転させることです。

例 56: 手動で割り当てられた識別子を持つエンティティの基本クラス
@MappedSuperclass
public abstract class AbstractEntity<ID> implements Persistable<ID> {

  @Transient
  private boolean isNew = true; (1)

  @Override
  public boolean isNew() {
    return isNew; (2)
  }

  @PrePersist (3)
  @PostLoad
  void markNotNew() {
    this.isNew = false;
  }

  // More code …
}
1 新しい状態を保持するフラグを宣言します。一時的であるため、データベースに保持されません。
2Persistable.isNew() の実装でフラグを返し、Spring Data リポジトリが EntityManager.persist() を呼び出すか  … .merge() を呼び出すかを認識できるようにします。
3JPA エンティティコールバックを使用してメソッドを宣言し、save(…) へのリポジトリ呼び出しまたは永続性プロバイダーによるインスタンス作成の後にフラグが既存のエンティティを示すように切り替えられるようにします。

5.3. クエリメソッド

このセクションでは、Spring Data JPA を使用してクエリを作成するさまざまな方法について説明します。

5.3.1. クエリ検索戦略

JPA モジュールは、クエリを文字列として手動で定義すること、またはメソッド名から派生させることをサポートしています。

述語 IsStartingWithStartingWithStartsWithIsEndingWithEndingWithEndsWithIsNotContainingNotContainingNotContainsIsContainingContainingContains を持つ派生クエリは、これらのクエリのそれぞれの引数がサニタイズされます。つまり、LIKE によってワイルドカードとして認識される文字が実際に引数に含まれる場合、これらはエスケープされるため、リテラルとしてのみ一致します。使用されるエスケープ文字は、@EnableJpaRepositories アノテーションの escapeCharacter を設定することにより構成できます。SpEL 式の使用と比較してください。

宣言されたクエリ

メソッド名から派生したクエリを取得することは非常に便利ですが、メソッド名パーサーが使用したいキーワードをサポートしていないか、メソッド名が不必要にくなる状況に直面するかもしれません。そのため、命名規則を使用して JPA 名前付きクエリを使用するか(詳細については JPA 名前付きクエリの使用を参照)、@Query を使用してクエリメソッドにアノテーションを付けることができます(詳細については @Query を使用するを参照)。

5.3.2. クエリ作成

一般に、JPA のクエリ作成メカニズムは、「クエリメソッド」に従って機能します。次の例は、JPA クエリメソッドが何に変換されるかを示しています。

例 57: メソッド名からのクエリ作成
public interface UserRepository extends Repository<User, Long> {

  List<User> findByEmailAddressAndLastname(String emailAddress, String lastname);
}

これから JPA 条件 API を使用してクエリを作成しますが、本質的に、これは次のクエリに変換されます: select u from User u where u.emailAddress = ?1 and u.lastname = ?2. Spring Data JPA は、「プロパティ式」に従って、プロパティチェックを実行し、ネストされたプロパティを走査します。

次の表は、JPA でサポートされているキーワードと、そのキーワードを含むメソッドが何に変換されるかを示しています。

表 3: メソッド名内でサポートされているキーワード
キーワード サンプル JPQL スニペット

And

findByLastnameAndFirstname

… where x.lastname = ?1 and x.firstname = ?2

Or

findByLastnameOrFirstname

… where x.lastname = ?1 or x.firstname = ?2

IsEquals

findByFirstnamefindByFirstnameIsfindByFirstnameEquals

… where x.firstname = ?1

Between

findByStartDateBetween

… where x.startDate between ?1 and ?2

LessThan

findByAgeLessThan

… where x.age < ?1

LessThanEqual

findByAgeLessThanEqual

… where x.age <= ?1

GreaterThan

findByAgeGreaterThan

… where x.age > ?1

GreaterThanEqual

findByAgeGreaterThanEqual

… where x.age >= ?1

After

findByStartDateAfter

… where x.startDate > ?1

Before

findByStartDateBefore

… where x.startDate < ?1

IsNullNull

findByAge(Is)Null

… where x.age is null

IsNotNullNotNull

findByAge(Is)NotNull

… where x.age not null

Like

findByFirstnameLike

… where x.firstname like ?1

NotLike

findByFirstnameNotLike

… where x.firstname not like ?1

StartingWith

findByFirstnameStartingWith

… where x.firstname like ?1 ( % が付加されたパラメーター)

EndingWith

findByFirstnameEndingWith

… where x.firstname like ?1 ( % が先頭に追加されたパラメーター)

Containing

findByFirstnameContaining

… where x.firstname like ?1 ( % にラップされたパラメーターバインド)

OrderBy

findByAgeOrderByLastnameDesc

… where x.age = ?1 order by x.lastname desc

Not

findByLastnameNot

… where x.lastname <> ?1

In

findByAgeIn(Collection<Age> ages)

… where x.age in ?1

NotIn

findByAgeNotIn(Collection<Age> ages)

… where x.age not in ?1

True

findByActiveTrue()

… where x.active = true

False

findByActiveFalse()

… where x.active = false

IgnoreCase

findByFirstnameIgnoreCase

… where UPPER(x.firstame) = UPPER(?1)

In および NotIn は、Collection のサブクラスをパラメーターおよび配列または可変引数としても使用します。同じ論理演算子の他の構文バージョンについては、「リポジトリクエリキーワード」を確認してください。

5.3.3. JPA 名前付きクエリの使用

例では、<named-query /> 要素と @NamedQuery アノテーションを使用しています。これらの構成要素のクエリは、JPA クエリ言語で定義する必要があります。もちろん、<named-native-query /> または @NamedNativeQuery も使用できます。これらの要素を使用すると、データベースプラットフォームの独立性が失われるため、ネイティブ SQL でクエリを定義できます。
XML 名前付きクエリ定義

XML 構成を使用するには、必要な <named-query /> 要素を、クラスパスの META-INF フォルダーにある orm.xml JPA 構成ファイルに追加します。名前付きクエリの自動呼び出しは、定義済みの命名規則を使用することで有効になります。詳細については、以下を参照してください。

例 58: XML 名前付きクエリの構成
<named-query name="User.findByLastname">
  <query>select u from User u where u.lastname = ?1</query>
</named-query>

クエリには、実行時に解決するために使用される特別な名前があります。

アノテーションベースの構成

アノテーションベースの構成には、別の構成ファイルを編集する必要がないという利点があり、メンテナンスの労力が軽減されます。新しいクエリの宣言ごとにドメインクラスを再コンパイルする必要があるため、そのメリットを享受できます。

例 59: アノテーションベースの名前付きクエリの構成
@Entity
@NamedQuery(name = "User.findByEmailAddress",
  query = "select u from User u where u.emailAddress = ?1")
public class User {

}
インターフェースの宣言

これらの名前付きクエリの実行を許可するには、UserRepository を次のように指定します。

例 60: UserRepository でのクエリメソッド宣言
public interface UserRepository extends JpaRepository<User, Long> {

  List<User> findByLastname(String lastname);

  User findByEmailAddress(String emailAddress);
}

Spring Data は、これらのメソッドの呼び出しを、構成済みドメインクラスの単純名で始まり、その後にドットで区切られたメソッド名が続く名前付きクエリへの呼び出しを解決しようとします。前述の例では、メソッド名からクエリを作成しようとする代わりに、examine で定義された名前付きクエリを使用します。

5.3.4. @Query を使用する

名前付きクエリを使用してエンティティのクエリを宣言することは有効なアプローチであり、少数のクエリでは正常に機能します。クエリ自体はそれらを実行する Java メソッドに関連付けられているため、ドメインクラスにアノテーションを付けるのではなく、Spring Data JPA @Query アノテーションを使用して実際にクエリを直接バインドできます。これにより、ドメインクラスが永続化固有の情報から解放され、クエリがリポジトリインターフェースに配置されます。

クエリメソッドにアノテーションが付けられたクエリは、@NamedQuery を使用して定義されたクエリまたは orm.xml で宣言された名前付きクエリよりも優先されます。

次の例は、@Query アノテーションを使用して作成されたクエリを示しています。

例 61: @Query を使用して、クエリメソッドでクエリを宣言する
public interface UserRepository extends JpaRepository<User, Long> {

  @Query("select u from User u where u.emailAddress = ?1")
  User findByEmailAddress(String emailAddress);
}
高度な LIKE 式の使用

@Query で作成された手動で定義されたクエリのクエリ実行メカニズムにより、次の例に示すように、クエリ定義内で高度な LIKE 式を定義できます。

例 62: @Query の高度な like 式
public interface UserRepository extends JpaRepository<User, Long> {

  @Query("select u from User u where u.firstname like %?1")
  List<User> findByFirstnameEndsWith(String firstname);
}

前の例では、LIKE 区切り文字(%)が認識され、クエリは有効な JPQL クエリに変換されます(% を削除します)。クエリの実行時に、メソッド呼び出しに渡されるパラメーターは、以前に認識された LIKE パターンで拡張されます。

ネイティブクエリ

@Query アノテーションでは、次の例に示すように、nativeQuery フラグを true に設定することにより、ネイティブクエリを実行できます。

例 63: @Query を使用して、クエリメソッドでネイティブクエリを宣言します。
public interface UserRepository extends JpaRepository<User, Long> {

  @Query(value = "SELECT * FROM USERS WHERE EMAIL_ADDRESS = ?1", nativeQuery = true)
  User findByEmailAddress(String emailAddress);
}
Spring Data JPA は、宣言された実際のクエリを操作する必要があるため、ネイティブクエリの動的ソートを現在サポートしていません。ただし、次の例に示すように、カウントクエリを自分で指定することにより、ページネーションにネイティブクエリを使用できます。
例 64: @Query を使用して、クエリメソッドでページネーションのネイティブカウントクエリを宣言します。
public interface UserRepository extends JpaRepository<User, Long> {

  @Query(value = "SELECT * FROM USERS WHERE LASTNAME = ?1",
    countQuery = "SELECT count(*) FROM USERS WHERE LASTNAME = ?1",
    nativeQuery = true)
  Page<User> findByLastname(String lastname, Pageable pageable);
}

同様のアプローチは、クエリのコピーに .count サフィックスを追加することにより、名前付きネイティブクエリでも機能します。ただし、カウントクエリの結果セットマッピングを登録する必要があります。

5.3.5. 並べ替えの使用

ソートは、PageRequest を提供するか、Sort を直接使用して行うことができます。Sort の Order インスタンス内で実際に使用されるプロパティは、ドメインモデルに一致する必要があります。つまり、クエリ内で使用されるプロパティまたはエイリアスに解決する必要があります。JPQL は、これを状態フィールドパス式として定義します。

参照不可能なパス式を使用すると、Exception になります。

ただし、Sort を @Query と組み合わせて使用すると、ORDER BY 句内の関数を含む、パスがチェックされていない Order インスタンスに忍び込むことができます。これは、Order が特定のクエリ文字列に追加されるため可能です。デフォルトでは、Spring Data JPA は関数呼び出しを含む Order インスタンスを拒否しますが、JpaSort.unsafe を使用して、潜在的に安全でない順序を追加できます。

次の例では、Sort と JpaSort を使用しています。JpaSort の安全でないオプションも含まれています。

例 65: Sort および JpaSort の使用
public interface UserRepository extends JpaRepository<User, Long> {

  @Query("select u from User u where u.lastname like ?1%")
  List<User> findByAndSort(String lastname, Sort sort);

  @Query("select u.id, LENGTH(u.firstname) as fn_len from User u where u.lastname like ?1%")
  List<Object[]> findByAsArrayAndSort(String lastname, Sort sort);
}

repo.findByAndSort("lannister", new Sort("firstname"));               (1)
repo.findByAndSort("stark", new Sort("LENGTH(firstname)"));           (2)
repo.findByAndSort("targaryen", JpaSort.unsafe("LENGTH(firstname)")); (3)
repo.findByAsArrayAndSort("bolton", new Sort("fn_len"));              (4)
1 ドメインモデルのプロパティを指す有効な Sort 式。
2 関数呼び出しを含む無効な Sort。ハウズ例外。
3 明示的に安全でない Order を含む有効Sort
4 エイリアス機能を指す有効な Sort 式。

5.3.6. 名前付きパラメーターの使用

上記のすべての例で説明したように、デフォルトでは、Spring Data JPA は位置ベースのパラメーターバインディングを使用します。これにより、パラメーターの位置に関するリファクタリング時に、クエリメソッドが少しエラーを起こしやすくなります。この課題を解決するには、次の例に示すように、@Param アノテーションを使用してメソッドパラメーターに具体的な名前を付け、クエリ内の名前をバインドします。

例 66: 名前付きパラメーターの使用
public interface UserRepository extends JpaRepository<User, Long> {

  @Query("select u from User u where u.firstname = :firstname or u.lastname = :lastname")
  User findByLastnameOrFirstname(@Param("lastname") String lastname,
                                 @Param("firstname") String firstname);
}
メソッドパラメーターは、定義されたクエリの順序に従って切り替えられます。
バージョン 4 の時点で、Spring は -parameters コンパイラフラグに基づいて Java 8 のパラメーター名の検出を完全にサポートしています。デバッグ情報の代替としてビルドでこのフラグを使用することにより、名前付きパラメーターの @Param アノテーションを省略できます。

5.3.7. SpEL 式の使用

Spring Data JPA リリース 1.4 の時点で、@Query で定義された手動で定義されたクエリでの制限された SpEL テンプレート式の使用をサポートしています。クエリの実行時に、これらの式は定義済みの変数セットに対して評価されます。Spring Data JPA は、entityName という変数をサポートしています。その使用箇所は select x from #{#entityName} x です。指定されたリポジトリに関連付けられたドメインタイプの entityName を挿入します。entityName は次のように解決されます。ドメインタイプが @Entity アノテーションの name プロパティを設定している場合、それが使用されます。それ以外の場合、ドメインタイプの単純なクラス名が使用されます。

次の例は、クエリ文字列内の #{#entityName} 式の 1 つのユースケースを示しています。ここでは、クエリメソッドと手動で定義されたクエリでリポジトリインターフェースを定義します。

例 67: リポジトリクエリメソッドでの SpEL 式の使用 -entityName
@Entity
public class User {

  @Id
  @GeneratedValue
  Long id;

  String lastname;
}

public interface UserRepository extends JpaRepository<User,Long> {

  @Query("select u from #{#entityName} u where u.lastname = ?1")
  List<User> findByLastname(String lastname);
}

@Query アノテーションのクエリ文字列に実際のエンティティ名が記述されないようにするには、#{#entityName} 変数を使用できます。

entityName は、@Entity アノテーションを使用してカスタマイズできます。orm.xml のカスタマイズは、SpEL 式ではサポートされていません。

もちろん、クエリ宣言で User を直接使用することもできますが、その場合もクエリを変更する必要があります。#entityName への参照は、User クラスの潜在的な将来の再マッピングを別のエンティティ名にピックアップします(たとえば、@Entity(name = "MyUser") を使用して)。

クエリ文字列の #{#entityName} 式のもう 1 つの使用例は、具体的なドメインタイプ用の特殊なリポジトリインターフェースで汎用リポジトリインターフェースを定義する場合です。具体的なインターフェースでカスタムクエリメソッドの定義を繰り返さないようにするには、次の例に示すように、汎用リポジトリインターフェースの @Query アノテーションのクエリ文字列でエンティティ名式を使用できます。

例 68: リポジトリクエリメソッドでの SpEL 式の使用 - 継承を伴う entityName
@MappedSuperclass
public abstract class AbstractMappedType {
   …
  String attribute
}

@Entity
public class ConcreteType extends AbstractMappedType {  …  }

@NoRepositoryBean
public interface MappedTypeRepository<T extends AbstractMappedType>
  extends Repository<T, Long> {

  @Query("select t from #{#entityName} t where t.attribute = ?1")
  List<T> findAllByAttribute(String attribute);
}

public interface ConcreteRepository
  extends MappedTypeRepository<ConcreteType> {  …  }

上記の例では、MappedTypeRepository インターフェースは、AbstractMappedType を継承するいくつかのドメインタイプの共通の親インターフェースです。また、汎用の findAllByAttribute(…) メソッドも定義します。これは、特殊なリポジトリインターフェースのインスタンスで使用できます。ConcreteRepository で findByAllAttribute(…) を呼び出すと、クエリは select t from ConcreteType t where t.attribute = ?1 になります。

引数を操作する SpEL 式は、メソッドの引数を操作するためにも使用できます。これらの SpEL 式では、エンティティ名は使用できませんが、引数は使用できます。次の例に示すように、名前またはインデックスでアクセスできます。

例 69: リポジトリクエリメソッドでの SpEL 式の使用 - 引数へのアクセス。
@Query("select u from User u where u.firstname = ?1 and u.firstname=?#{[0]} and u.emailAddress = ?#{principal.emailAddress}")
List<User> findByFirstnameAndCurrentUserWithCustomQuery(String firstname);

like -conditions の場合、多くの場合、% をストリング値パラメーターの先頭または末尾に追加します。これは、バインドパラメーターマーカーまたは SpEL 式に % を追加またはプレフィックスすることで実行できます。繰り返しますが、次の例はこれを示しています。

例 70: リポジトリクエリメソッドでの SpEL 式の使用 - ワイルドカードショートカット。
@Query("select u from User u where u.lastname like %:#{[0]}% and u.lastname like %:lastname%")
List<User> findByLastnameWithSpelExpression(@Param("lastname") String lastname);

安全ではないソースからの値で like -conditions を使用する場合、値をサニタイズしてワイルドカードを含めることができないようにし、攻撃者ができる以上のデータを選択できるようにする必要があります。この目的のために、escape(String) メソッドが SpEL コンテキストで利用可能になります。最初の引数の _ および % のすべてのインスタンスの前に、2 番目の引数の単一の文字が付加されます。これにより、JPQL および標準 SQL で使用可能な like 式の escape 句と組み合わせて、バインドパラメーターを簡単にクリーニングできます。

例 71: リポジトリクエリメソッドでの SpEL 式の使用 - 入力値のサニタイズ。
@Query("select u from User u where u.firstname like %?#{escape([0])}% escape ?#{escapeCharacter()}")
List<User> findContainingEscaped(String namePart);

リポジトリインターフェース findContainingEscaped("Peter_")" will find `Peter_Parker ではなく Peter Parker でこのメソッド宣言を指定します。使用されるエスケープ文字は、@EnableJpaRepositories アノテーションの escapeCharacter を設定することにより構成できます。SpEL コンテキストで使用可能なメソッド escape(String) は、SQL および JPQL の標準ワイルドカード _ および % のみをエスケープすることに注意してください。基礎となるデータベースまたは JPA 実装が追加のワイルドカードをサポートしている場合、これらはエスケープされません。

5.3.8. クエリの変更

前のすべてのセクションでは、特定のエンティティまたはエンティティのコレクションにアクセスするクエリを宣言する方法について説明しました。「Spring Data リポジトリのカスタム実装」で説明されている機能を使用して、カスタム変更動作を追加できます。このアプローチは包括的なカスタム機能に適しているため、次の例に示すように、@Modifying を使用してクエリメソッドにアノテーションを付けることで、パラメーターバインドのみが必要なクエリを変更できます。

例 72: 操作クエリの宣言
@Modifying
@Query("update User u set u.firstname = ?1 where u.lastname = ?2")
int setFixedFirstnameFor(String firstname, String lastname);

これを行うと、メソッドにアノテーションが付けられたクエリが、選択クエリではなく更新クエリとしてトリガーされます。EntityManager には変更クエリの実行後に古いエンティティが含まれる可能性があるため、自動的にクリアしません(詳細については EntityManager.clear() の JavaDoc(Javadoc) を参照)。これにより、EntityManager で保留中のすべての非フラッシュ変更が効果的にドロップされます。EntityManager を自動的にクリアする場合、@Modifying アノテーションの clearAutomatically 属性を true に設定できます。

@Modifying アノテーションは、@Query アノテーションと組み合わせた場合にのみ関連します。派生クエリメソッドまたはカスタムメソッドは、このアノテーションを必要としません。

派生削除クエリ

Spring Data JPA は、次の例に示すように、JPQL クエリを明示的に宣言する必要のない派生削除クエリもサポートしています。

例 73: 派生削除クエリを使用する
interface UserRepository extends Repository<User, Long> {

  void deleteByRoleId(long roleId);

  @Modifying
  @Query("delete from User u where u.role.id = ?1")
  void deleteInBulkByRoleId(long roleId);
}

deleteByRoleId(…) メソッドは基本的に deleteInBulkByRoleId(…) と同じ結果を生成するように見えますが、2 つのメソッド宣言には実行方法の点で重要な違いがあります。名前が示すように、後者のメソッドは、データベースに対して 1 つの JPQL クエリ(アノテーションで定義されたクエリ)を発行します。これは、現在読み込まれている User のインスタンスでさえ、ライフサイクルコールバックが呼び出されないことを意味します。

ライフサイクルクエリが実際に呼び出されることを確認するために、deleteByRoleId(…) の呼び出しはクエリを実行し、返されたインスタンスを 1 つずつ削除するため、永続性プロバイダーは実際にそれらのエンティティで @PreRemove コールバックを呼び出すことができます。

実際、派生削除クエリはクエリを実行し、結果に対して CrudRepository.delete(Iterable<User> users) を呼び出し、CrudRepository の他の delete(…) メソッドの実装と動作を同期させるためのショートカットです。

5.3.9. クエリヒントの適用

リポジトリインターフェースで宣言されたクエリに JPA クエリヒントを適用するには、@QueryHints アノテーションを使用できます。次の例に示すように、JPA @QueryHint アノテーションの配列とブールフラグを使用して、ページネーションを適用するときにトリガーされる追加カウントクエリに適用されるヒントを潜在的に無効にします。

例 74: QueryHints をリポジトリ方式で使用する
public interface UserRepository extends Repository<User, Long> {

  @QueryHints(value = { @QueryHint(name = "name", value = "value")},
              forCounting = false)
  Page<User> findByLastname(String lastname, Pageable pageable);
}

上記の宣言は、実際のクエリに構成済みの @QueryHint を適用しますが、ページ総数を計算するためにトリガーされるカウントクエリには適用しません。

5.3.10. Fetch- および LoadGraphs の構成

JPA 2.1 仕様では、@NamedEntityGraph 定義を参照できる @EntityGraph アノテーションでもサポートする Fetch- および LoadGraphs の指定のサポートが導入されました。エンティティでそのアノテーションを使用して、結果のクエリのフェッチプランを構成できます。フェッチのタイプ(Fetch または Load)は、@EntityGraph アノテーションの type 属性を使用して構成できます。詳細については、JPA 2.1 仕様 3.7.4 を参照してください。

次の例は、エンティティに名前付きエンティティグラフを定義する方法を示しています。

例 75: エンティティ上の名前付きエンティティグラフの定義。
@Entity
@NamedEntityGraph(name = "GroupInfo.detail",
  attributeNodes = @NamedAttributeNode("members"))
public class GroupInfo {

  // default fetch mode is lazy.
  @ManyToMany
  List<GroupMember> members = new ArrayList<GroupMember>();

   …
}

次の例は、リポジトリクエリメソッドで名前付きエンティティグラフを参照する方法を示しています。

例 76: リポジトリクエリメソッドで名前付きエンティティグラフ定義を参照します。
@Repository
public interface GroupRepository extends CrudRepository<GroupInfo, String> {

  @EntityGraph(value = "GroupInfo.detail", type = EntityGraphType.LOAD)
  GroupInfo getByGroupName(String name);

}

@EntityGraph を使用して、アドホックエンティティグラフを定義することもできます。提供された attributePaths は、次の例に示すように、@NamedEntityGraph をドメインタイプに明示的に追加する必要なく、対応する EntityGraph に変換されます。

例 77: リポジトリクエリメソッドでの AD-HOC エンティティグラフ定義の使用。
@Repository
public interface GroupRepository extends CrudRepository<GroupInfo, String> {

  @EntityGraph(attributePaths = { "members" })
  GroupInfo getByGroupName(String name);

}

5.3.11. 射影

Spring Data クエリメソッドは通常、リポジトリによって管理される集約ルートの 1 つまたは複数のインスタンスを返します。ただし、これらのタイプの特定の属性に基づいて射影を作成することが望ましい場合があります。Spring Data では、専用の戻り値型をモデル化して、管理対象集合体の部分ビューをより選択的に取得できます。

次の例のようなリポジトリおよび集約ルートタイプを想像してください。

例 78: サンプルの集約とリポジトリ
class Person {

  @Id UUID id;
  String firstname, lastname;
  Address address;

  static class Address {
    String zipCode, city, street;
  }
}

interface PersonRepository extends Repository<Person, UUID> {

  Collection<Person> findByLastname(String lastname);
}

ここで、人の名前属性のみを取得することを想像してください。Spring Data はこれを達成するためにどのような意味を持っていますか?この章の残りはその質問に回答します。

インターフェースベースの射影

クエリの結果を名前属性のみに制限する最も簡単な方法は、次の例に示すように、読み取るプロパティのアクセサーメソッドを公開するインターフェースを宣言することです。

例 79: 属性のサブセットを取得する射影インターフェース
interface NamesOnly {

  String getFirstname();
  String getLastname();
}

ここで重要なことは、ここで定義されたプロパティが集約ルートのプロパティと正確に一致することです。これにより、クエリメソッドを次のように追加できます。

例 80: クエリメソッドでインターフェースベースの射影を使用するリポジトリ
interface PersonRepository extends Repository<Person, UUID> {

  Collection<NamesOnly> findByLastname(String lastname);
}

クエリ実行エンジンは、返された各要素に対して実行時にそのインターフェースのプロキシインスタンスを作成し、公開されたメソッドへの呼び出しをターゲットオブジェクトに転送します。

射影は再帰的に使用できます。Address 情報の一部も含めたい場合は、次の例に示すように、そのための射影インターフェースを作成し、getAddress() の宣言からそのインターフェースを返します。

例 81: 属性のサブセットを取得する射影インターフェース
interface PersonSummary {

  String getFirstname();
  String getLastname();
  AddressSummary getAddress();

  interface AddressSummary {
    String getCity();
  }
}

メソッドの呼び出し時に、ターゲットインスタンスの address プロパティが取得され、順番に射影プロキシにラップされます。

閉じた射影

アクセサーメソッドがすべてターゲット集合体のプロパティに一致する射影インターフェースは、閉じた射影と見なされます。次の例(この章の前半でも使用しました)は、閉じた射影です。

例 82: 閉じた射影
interface NamesOnly {

  String getFirstname();
  String getLastname();
}

閉じた射影を使用する場合、Spring Data はクエリの実行を最適化できます。これは、射影プロキシのバックアップに必要なすべての属性がわかっているためです。詳細については、リファレンスドキュメントのモジュール固有の部分を参照してください。

開いた射影

次の例に示すように、@Value アノテーションを使用して、射影インターフェースのアクセサーメソッドを使用して新しい値を計算することもできます。

例 83: 開いた射影
interface NamesOnly {

  @Value("#{target.firstname + ' ' + target.lastname}")
  String getFullName();
   …
}

射影を支える集約ルートは、target 変数で利用可能です。@Value を使用した射影インターフェースは、オープン射影です。この場合、Spring Data はクエリ実行最適化を適用できません。これは、SpEL 式が集約ルートの任意の属性を使用できるためです。

@Value で使用される式は複雑すぎてはいけません — String 変数でのプログラミングは避けたいです。非常に単純な式の場合、次の例に示すように、1 つのオプションはデフォルトのメソッド(Java 8 で導入)に頼ることです。

例 84: カスタムロジックにデフォルトのメソッドを使用する射影インターフェース
interface NamesOnly {

  String getFirstname();
  String getLastname();

  default String getFullName() {
    return getFirstname().concat(" ").concat(getLastname());
  }
}

このアプローチでは、射影インターフェースで公開される他のアクセサーメソッドに純粋に基づいてロジックを実装できる必要があります。次の例に示すように、2 番目のより柔軟なオプションは、Spring Bean にカスタムロジックを実装し、SpEL 式からそれを呼び出すことです。

例 85: サンプル Person オブジェクト
@Component
class MyBean {

  String getFullName(Person person) {
     …
  }
}

interface NamesOnly {

  @Value("#{@myBean.getFullName(target)}")
  String getFullName();
   …
}

SpEL 式が myBean を参照し、getFullName(…) メソッドを呼び出し、射影ターゲットをメソッドパラメーターとして転送する方法に注目してください。SpEL 式の評価に裏付けられたメソッドは、メソッドパラメーターを使用することもできます。このパラメーターは、式から参照できます。メソッドのパラメーターは、args という名前の Object 配列を介して使用できます。次の例は、args 配列からメソッドパラメーターを取得する方法を示しています。

例 86: サンプル Person オブジェクト
interface NamesOnly {

  @Value("#{args[0] + ' ' + target.firstname + '!'}")
  String getSalutation(String prefix);
}

ここでも、より複雑な式のために、Spring Bean を使用する必要がありますし、説明するように、メソッド Invoke 表現を聞かせてそれ以前

null 可能ラッパー

射影インターフェースの Getter は、null 許容ラッパーを使用して null の安全性を向上させることができます。現在サポートされているラッパータイプは次のとおりです。

  • java.util.Optional

  • com.google.common.base.Optional

  • scala.Option

  • io.vavr.control.Option

例 87: null 許容ラッパーを使用した射影インターフェース
interface NamesOnly {

  Optional<String> getFirstname();
}

基になる射影値が null でない場合、値はラッパータイプの現在の表現を使用して返されます。バッキング値が null の場合、getter メソッドは使用されたラッパータイプの空の表現を返します。

クラスベースの射影 (DTO)

射影を定義するもう 1 つの方法は、取得することになっているフィールドのプロパティを保持する値型 DTO(データ転送オブジェクト)を使用することです。これらの DTO タイプは、プロキシが発生せず、ネストされた射影を適用できないことを除いて、射影インターフェースとまったく同じ方法で使用できます。

ストアがロードするフィールドを制限することでクエリの実行を最適化する場合、ロードされるフィールドは公開されているコンストラクターのパラメーター名から決定されます。

次の例は、射影 DTO を示しています。

例 88: 射影 DTO
class NamesOnly {

  private final String firstname, lastname;

  NamesOnly(String firstname, String lastname) {

    this.firstname = firstname;
    this.lastname = lastname;
  }

  String getFirstname() {
    return this.firstname;
  }

  String getLastname() {
    return this.lastname;
  }

  // equals(…) and hashCode() implementations
}
射影 DTO の定型コードを避ける

@Value アノテーションを提供するプロジェクト Lombok (英語) を使用すると、DTO のコードを劇的に簡素化できます(前のインターフェース例で示した Spring の @Value アノテーションと混同しないでください)。Project Lombok の @Value アノテーションを使用する場合、前述のサンプル DTO は次のようになります。

@Value
class NamesOnly {
	String firstname, lastname;
}

フィールドはデフォルトで private final であり、クラスはすべてのフィールドを取得し、equals(…) および hashCode() メソッドを自動的に実装するコンストラクターを公開します。

動的射影

これまで、コレクションの戻り値型または要素型として射影型を使用しました。ただし、呼び出し時に使用するタイプを選択することもできます(これにより、動的になります)。動的射影を適用するには、次の例に示すようなクエリメソッドを使用します。

例 89: 動的射影パラメーターを使用するリポジトリ
interface PersonRepository extends Repository<Person, UUID> {

  <T> Collection<T> findByLastname(String lastname, Class<T> type);
}

この方法では、次の例に示すように、メソッドを使用して、そのままで、または射影を適用して集計を取得できます。

例 90: 動的射影でリポジトリを使用する
void someMethod(PersonRepository people) {

  Collection<Person> aggregates =
    people.findByLastname("Matthews", Person.class);

  Collection<NamesOnly> aggregates =
    people.findByLastname("Matthews", NamesOnly.class);
}

5.4. ストアドプロシージャー

JPA 2.1 仕様では、JPA 条件クエリ API を使用してストアドプロシージャを呼び出すサポートが導入されました。リポジトリメソッドでストアドプロシージャメタデータを宣言するための @Procedure アノテーションを導入しました。

以下の例では、次のストアドプロシージャを使用します。

例 91: HSQL DB の plus1inout プロシージャの定義。
/;
DROP procedure IF EXISTS plus1inout
/;
CREATE procedure plus1inout (IN arg int, OUT res int)
BEGIN ATOMIC
 set res = arg + 1;
END
/;

ストアドプロシージャのメタデータは、エンティティタイプで NamedStoredProcedureQuery アノテーションを使用して構成できます。

例 92: エンティティの StoredProcedure メタデータ定義。
@Entity
@NamedStoredProcedureQuery(name = "User.plus1", procedureName = "plus1inout", parameters = {
  @StoredProcedureParameter(mode = ParameterMode.IN, name = "arg", type = Integer.class),
  @StoredProcedureParameter(mode = ParameterMode.OUT, name = "res", type = Integer.class) })
public class User {}

@NamedStoredProcedureQuery には、ストアドプロシージャの 2 つの異なる名前があることに注意してください。name は、JPA が使用する名前です。procedureName は、データベース内のストアドプロシージャの名前です。

リポジトリメソッドからストアドプロシージャを複数の方法で参照できます。呼び出されるストアドプロシージャは、@Procedure アノテーションの value または procedureName 属性を使用して直接定義できます。これは、データベース内のストアドプロシージャを直接参照し、@NamedStoredProcedureQuery を介した設定を無視します。

または、@NamedStoredProcedureQuery.name 属性を @Procedure.name 属性として指定できます。valueprocedureName、および name のいずれも構成されていない場合、リポジトリ方式の名前が name 属性として使用されます。

次の例は、明示的にマップされたプロシージャを参照する方法を示しています。

例 93: データベースで「plus1inout」という名前の明示的にマップされたプロシージャを参照しています。
@Procedure("plus1inout")
Integer explicitlyNamedPlus1inout(Integer arg);

次の例は前の例と同等ですが、procedureName エイリアスを使用しています。

例 94: procedureName エイリアスを介してデータベース内の名前「plus1inout」を持つ暗黙的にマップされたプロシージャを参照します。
@Procedure(procedureName = "plus1inout")
Integer callPlus1InOut(Integer arg);

次も前の 2 つと同じですが、明示的なアノテーション属性の代わりにメソッド名を使用しています。

例 95: メソッド名を使用して、EntityManager で暗黙的にマップされた名前付きストアドプロシージャ「User.plus1」を参照します。
@Procedure
Integer plus1inout(@Param("arg") Integer arg);

次の例は、@NamedStoredProcedureQuery.name 属性を参照してストアドプロシージャを参照する方法を示しています。

例 96: EntityManager で明示的にマップされた名前付きストアドプロシージャ「User.plus1IO」の参照。
@Procedure(name = "User.plus1IO")
Integer entityAnnotatedCustomNamedProcedurePlus1IO(@Param("arg") Integer arg);

呼び出されるストアドプロシージャに単一の出力パラメーターがある場合、そのパラメーターはメソッドの戻り値として返されます。@NamedStoredProcedureQuery アノテーションで複数の出力パラメーターが指定されている場合、@NamedStoredProcedureQuery アノテーションで指定されたパラメーター名をキーとして Map として返すことができます。

5.5. 仕様

JPA 2 は、クエリをプログラムで作成するために使用できる条件 API を導入しています。criteria を記述することにより、ドメインクラスのクエリの where 句を定義します。別のステップに戻ると、これらの条件は、JPA 条件 API 制約によって記述されたエンティティに関する述語と見なすことができます。

Spring Data JPA は、同じセマンティクスに従い、JPA 条件 API を使用してそのような仕様を定義する API を提供する、エリックエバンスの著書「Domain Driven Design」から仕様の概念を取り入れています。次のように、仕様をサポートするために、JpaSpecificationExecutor インターフェースを使用してリポジトリインターフェースを継承できます。

public interface CustomerRepository extends CrudRepository<Customer, Long>, JpaSpecificationExecutor {
  …
}

追加のインターフェースには、さまざまな方法で仕様を実行できるメソッドがあります。例: findAll メソッドは、次の例に示すように、仕様に一致するすべてのエンティティを返します。

List<T> findAll(Specification<T> spec);

Specification インターフェースは次のように定義されます。

public interface Specification<T> {
  Predicate toPredicate(Root<T> root, CriteriaQuery<?> query,
            CriteriaBuilder builder);
}

次の例に示すように、仕様を簡単に使用して、エンティティの上に拡張可能な一連の述語を構築し、JpaRepository と組み合わせて使用し、必要な組み合わせごとにクエリ(メソッド)を宣言する必要なしに使用できます。

例 97: 顧客の仕様
public class CustomerSpecs {

  public static Specification<Customer> isLongTermCustomer() {
    return new Specification<Customer>() {
      public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query,
            CriteriaBuilder builder) {

         LocalDate date = new LocalDate().minusYears(2);
         return builder.lessThan(root.get(Customer_.createdAt), date);
      }
    };
  }

  public static Specification<Customer> hasSalesOfMoreThan(MonetaryAmount value) {
    return new Specification<Customer>() {
      public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query,
            CriteriaBuilder builder) {

         // build query here
      }
    };
  }
}

確かに、ボイラープレートの量には改善の余地がありますが(最終的には Java 8 の閉鎖によって削減される可能性があります)、このセクションの後半で説明するように、クライアント側はより良くなります。Customer_ タイプは、JPA メタモデルジェネレーターを使用して生成されるメタモデルタイプです(Hibernate 実装のドキュメントの例 (英語) を参照)。式 Customer_.createdAt は、Customer がタイプ Date の createdAt 属性を持っていると想定しています。それに加えて、ビジネス要件の抽象化レベルに関するいくつかの条件を表明し、実行可能な Specifications を作成しました。クライアントは次のように Specification を使用する場合があります。

例 98: 簡単な仕様を使用する
List<Customer> customers = customerRepository.findAll(isLongTermCustomer());

この種のデータアクセスのクエリを作成してみませんか?単一の Specification を使用しても、単純なクエリ宣言よりも大きな利点は得られません。仕様を組み合わせて新しい Specification オブジェクトを作成すると、仕様の威力が本当に発揮されます。これは、次のような式を作成するために提供されている Specification のデフォルトメソッドを使用して実現できます。

例 99: 組み合わせ仕様
MonetaryAmount amount = new MonetaryAmount(200.0, Currencies.DOLLAR);
List<Customer> customers = customerRepository.findAll(
  isLongTermCustomer().or(hasSalesOfMoreThan(amount)));

Specification は、チェーンにいくつかの「グルーコード」デフォルトメソッドを提供し、Specification インスタンスを結合します。これらのメソッドを使用すると、新しい Specification 実装を作成し、既存の実装と組み合わせることにより、データアクセスレイヤーを継承できます。

5.6. 例によるクエリ

5.6.1. 導入

この章では、Query by Example の概要とその使用方法について説明します。

Query by Example(QBE)は、シンプルなインターフェースを備えた使いやすいクエリ手法です。動的なクエリの作成が可能になり、フィールド名を含むクエリを作成する必要がなくなります。実際、Query by Example では、ストア固有のクエリ言語を使用してクエリを記述する必要はまったくありません。

5.6.2. 使用方法

サンプル API によるクエリは、3 つの部分で構成されています。

  • プローブ : フィールドが設定されたドメインオブジェクトの実際の例。

  • ExampleMatcherExampleMatcher には、特定のフィールドの照合方法に関する詳細が記載されています。複数の例で再利用できます。

  • ExampleExample は、プローブと ExampleMatcher で構成されています。クエリの作成に使用されます。

例によるクエリは、いくつかのユースケースに適しています。

  • 静的または動的な制約のセットを使用してデータストアをクエリします。

  • 既存のクエリを壊すことを心配せずにドメインオブジェクトを頻繁にリファクタリングします。

  • 基礎となるデータストア API から独立して動作します。

例によるクエリには、いくつかの制限もあります。

  • firstname = ?0 or (firstname = ?1 and lastname = ?2) など、ネストまたはグループ化されたプロパティ制約はサポートされていません。

  • 文字列の starts/contains/ends/regex マッチングと他のプロパティタイプの完全一致のみをサポートします。

Query by Example を開始する前に、ドメインオブジェクトが必要です。開始するには、次の例に示すように、リポジトリのインターフェースを作成します。

例 100: サンプル Person オブジェクト
public class Person {

  @Id
  private String id;
  private String firstname;
  private String lastname;
  private Address address;

  //  …  getters and setters omitted
}

上記の例は、単純なドメインオブジェクトを示しています。これを使用して Example を作成できます。デフォルトでは、null 値を持つフィールドは無視され、ストア固有のデフォルトを使用して文字列が一致します。例は、of ファクトリメソッドを使用するか、ExampleMatcher を使用して作成できます。Example は不変です。次のリストは、簡単な例を示しています。

例 101: 簡単な例
Person person = new Person();                         (1)
person.setFirstname("Dave");                          (2)

Example<Person> example = Example.of(person);         (3)
1 ドメインオブジェクトの新しいインスタンスを作成します。
2 クエリにプロパティを設定します。
3Example を作成します。

リポジトリを使用して、クエリの例を実行できます。これを行うには、リポジトリインターフェースで QueryByExampleExecutor<T> を継承します。次のリストは、QueryByExampleExecutor インターフェースからの抜粋を示しています。

例 102: QueryByExampleExecutor
public interface QueryByExampleExecutor<T> {

  <S extends T> S findOne(Example<S> example);

  <S extends T> Iterable<S> findAll(Example<S> example);

  //  …  more functionality omitted.
}

5.6.3. マッチャーの例

例はデフォルト設定に限定されません。次の例に示すように、ExampleMatcher を使用して、文字列照合、null 処理、およびプロパティ固有の設定に独自のデフォルトを指定できます。

例 103: カスタマイズされたマッチングを使用したマッチャーの例
Person person = new Person();                          (1)
person.setFirstname("Dave");                           (2)

ExampleMatcher matcher = ExampleMatcher.matching()     (3)
  .withIgnorePaths("lastname")                         (4)
  .withIncludeNullValues()                             (5)
  .withStringMatcherEnding();                          (6)

Example<Person> example = Example.of(person, matcher); (7)
1 ドメインオブジェクトの新しいインスタンスを作成します。
2 セットのプロパティ。
3ExampleMatcher を作成して、すべての値が一致することを期待します。この段階では、さらに構成しなくても使用できます。
4lastname プロパティパスを無視する新しい ExampleMatcher を構築します。
5 新しい ExampleMatcher を作成して、lastname プロパティパスを無視し、null 値を含めます。
6 新しい ExampleMatcher を作成して、lastname プロパティパスを無視し、null 値を含め、サフィックス文字列の照合を実行します。
7 ドメインオブジェクトと設定された ExampleMatcher に基づいて新しい Example を作成します。

デフォルトでは、ExampleMatcher はプローブに設定されたすべての値が一致することを期待しています。暗黙的に定義された述語のいずれかに一致する結果を取得する場合は、ExampleMatcher.matchingAny() を使用します。

個々のプロパティ(「firstname」や「lastname」、ネストされたプロパティの場合は「address.city」など)の動作を指定できます。次の例に示すように、一致するオプションと大文字と小文字を区別して調整できます。

例 104: マッチャーオプションの構成
ExampleMatcher matcher = ExampleMatcher.matching()
  .withMatcher("firstname", endsWith())
  .withMatcher("lastname", startsWith().ignoreCase());
}

マッチャーオプションを構成する別の方法は、ラムダ(Java 8 で導入)を使用することです。このアプローチは、実装者にマッチャーの変更を要求するコールバックを作成します。設定オプションはマッチャーインスタンス内に保持されているため、マッチャーを返す必要はありません。次の例は、ラムダを使用するマッチャーを示しています。

例 105: ラムダを使用したマッチャーオプションの構成
ExampleMatcher matcher = ExampleMatcher.matching()
  .withMatcher("firstname", match -> match.endsWith())
  .withMatcher("firstname", match -> match.startsWith());
}

Example によって作成されたクエリは、構成の統合ビューを使用します。デフォルトのマッチング設定は ExampleMatcher レベルで設定できますが、個々の設定は特定のプロパティパスに適用できます。ExampleMatcher で設定された設定は、明示的に定義されていない限り、プロパティパス設定に継承されます。プロパティパッチの設定は、デフォルト設定よりも優先されます。次の表は、さまざまな ExampleMatcher 設定の範囲を説明しています。

表 4: ExampleMatcher 設定の範囲
設定 スコープ

null ハンドリング

ExampleMatcher

文字列マッチング

ExampleMatcher とプロパティパス

プロパティを無視する

プロパティパス

大文字と小文字の区別

ExampleMatcher とプロパティパス

価値変革

プロパティパス

5.6.4. 例を実行する

Spring Data JPA では、次の例に示すように、リポジトリで例によるクエリを使用できます。

例 106: リポジトリを使用した例によるクエリ
public interface PersonRepository extends JpaRepository<Person, String> {  …  }

public class PersonService {

  @Autowired PersonRepository personRepository;

  public List<Person> findPeople(Person probe) {
    return personRepository.findAll(Example.of(probe));
  }
}
現在、プロパティの一致に使用できるのは SingularAttribute プロパティのみです。

プロパティ指定子は、プロパティ名(firstname や lastname など)を受け入れます。プロパティをドット(address.city)と連結してナビゲートできます。また、一致するオプションと大文字と小文字を区別して調整することもできます。

次の表は、使用できるさまざまな StringMatcher オプションと、firstname という名前のフィールドで使用した結果を示しています。

表 5: StringMatcher オプション
マッチング 論理的な結果

DEFAULT (case-sensitive)

firstname = ?0

DEFAULT (case-insensitive)

LOWER(firstname) = LOWER(?0)

EXACT (case-sensitive)

firstname = ?0

EXACT (case-insensitive)

LOWER(firstname) = LOWER(?0)

STARTING (case-sensitive)

firstname like ?0 + '%'

STARTING (case-insensitive)

LOWER(firstname) like LOWER(?0) + '%'

ENDING (case-sensitive)

firstname like '%' + ?0

ENDING (case-insensitive)

LOWER(firstname) like '%' + LOWER(?0)

CONTAINING (case-sensitive)

firstname like '%' + ?0 + '%'

CONTAINING (case-insensitive)

LOWER(firstname) like '%' + LOWER(?0) + '%'

5.7. トランザクション性

デフォルトでは、リポジトリインスタンスの CRUD メソッドはトランザクション型です。読み取り操作の場合、トランザクション構成 readOnly フラグは true に設定されます。他のすべてはプレーンな @Transactional で構成されているため、デフォルトのトランザクション構成が適用されます。詳細については、SimpleJpaRepository (英語) の JavaDoc を参照してください。リポジトリで宣言されたメソッドのいずれかのトランザクション構成を微調整する必要がある場合は、次のようにリポジトリインターフェースでメソッドを再宣言します。

例 107: CRUD のカスタムトランザクション設定
public interface UserRepository extends CrudRepository<User, Long> {

  @Override
  @Transactional(timeout = 10)
  public List<User> findAll();

  // Further query method declarations
}

これを行うと、findAll() メソッドは 10 秒のタイムアウトで、readOnly フラグなしで実行されます。

トランザクションの動作を変更するもう 1 つの方法は、(通常)複数のリポジトリをカバーするファサードまたはサービス実装を使用することです。その目的は、非 CRUD 操作のトランザクション境界を定義することです。次の例は、そのようなファサードを複数のリポジトリに使用する方法を示しています。

例 108: ファサードを使用して複数のリポジトリ呼び出しのトランザクションを定義する
@Service
class UserManagementImpl implements UserManagement {

  private final UserRepository userRepository;
  private final RoleRepository roleRepository;

  @Autowired
  public UserManagementImpl(UserRepository userRepository,
    RoleRepository roleRepository) {
    this.userRepository = userRepository;
    this.roleRepository = roleRepository;
  }

  @Transactional
  public void addRoleToAllUsers(String roleName) {

    Role role = roleRepository.findByName(roleName);

    for (User user : userRepository.findAll()) {
      user.addRole(role);
      userRepository.save(user);
    }
}

この例では、addRoleToAllUsers(…) の呼び出しをトランザクション内で実行します(既存のトランザクションに参加するか、まだ実行されていない場合は新しいトランザクションを作成します)。リポジトリでのトランザクション構成は無視されます。外部のトランザクション構成が実際に使用される構成を決定するためです。ファサードのアノテーションベースの構成を機能させるには、<tx:annotation-driven /> をアクティブにするか、@EnableTransactionManagement を明示的に使用する必要があることに注意してください。この例では、コンポーネントスキャンを使用することを前提としています。

JPA の観点からは save の呼び出しは厳密には必要ありませんが、Spring Data によって提供されるリポジトリの抽象化との整合性を保つために必要です。

5.7.1. トランザクションクエリメソッド

クエリメソッドをトランザクション対応にするには、次の例に示すように、定義するリポジトリインターフェースで @Transactional を使用します。

例 109: クエリメソッドで @Transactional を使用する
@Transactional(readOnly = true)
public interface UserRepository extends JpaRepository<User, Long> {

  List<User> findByLastname(String lastname);

  @Modifying
  @Transactional
  @Query("delete from User u where u.active = false")
  void deleteInactiveUsers();
}

通常、ほとんどのクエリメソッドはデータの読み取りのみを行うため、readOnly フラグを true に設定する必要があります。それとは対照的に、deleteInactiveUsers() は @Modifying アノテーションを使用して、トランザクション構成をオーバーライドします。メソッドは readOnly フラグを false に設定して実行されます。

readOnly フラグを設定することにより、読み取り専用クエリにトランザクションを使用し、そのようなものとしてマークできます。ただし、そうすることで、クエリの操作をトリガーしないことを確認することはできません(ただし、一部のデータベースは、読み取り専用トランザクション内で INSERT および UPDATE ステートメントを拒否します)。readOnly フラグは、代わりに、パフォーマンスの最適化のための基礎となる JDBC ドライバーへのヒントとして伝搬されます。さらに、Spring は、基礎となる JPA プロバイダーでいくつかの最適化を実行します。例:Hibernate で使用する場合、トランザクションを readOnly として設定すると、フラッシュモードは NEVER に設定されます。これにより、Hibernate はダーティチェックをスキップします(大きなオブジェクトツリーの顕著な改善)。

5.8. ロッキング

使用するロックモードを指定するには、次の例に示すように、クエリメソッドで @Lock アノテーションを使用できます。

例 110: クエリメソッドのロックメタデータの定義
interface UserRepository extends Repository<User, Long> {

  // Plain query method
  @Lock(LockModeType.READ)
  List<User> findByLastname(String lastname);
}

このメソッド宣言により、トリガーされるクエリに READ の LockModeType が装備されます。次の例に示すように、リポジトリインターフェースでメソッドを再宣言し、@Lock アノテーションを追加することにより、CRUD メソッドのロックを定義することもできます。

例 111: CRUD メソッドのロックメタデータの定義
interface UserRepository extends Repository<User, Long> {

  // Redeclaration of a CRUD method
  @Lock(LockModeType.READ);
  List<User> findAll();
}

5.9. 監査

5.9.1. 基本

Spring Data は、エンティティの作成者または変更者と変更がいつ発生したかを透過的に追跡する高度なサポートを提供します。その機能を活用するには、アノテーションを使用するか、インターフェースを実装することで定義できる監査メタデータをエンティティクラスに装備する必要があります。

アノテーションベースの監査メタデータ

エンティティを作成または変更したユーザーをキャプチャーする @CreatedBy と @LastModifiedBy、および変更が発生したときにキャプチャーする @CreatedDate と @LastModifiedDate を提供します。

例 112: 監査対象エンティティ
class Customer {

  @CreatedBy
  private User user;

  @CreatedDate
  private DateTime createdDate;

  //  …  further properties omitted
}

ご覧のとおり、どの情報をキャプチャーするかに応じて、アノテーションを選択的に適用できます。変更が行われたときにキャプチャーするアノテーションは、タイプ Joda-Time、DateTime、レガシー Java Date および Calendar、JDK8 の日付と時刻タイプ、および long または Long のプロパティで使用できます。

インターフェースベースの監査メタデータ

アノテーションを使用して監査メタデータを定義したくない場合は、ドメインクラスに Auditable インターフェースを実装させることができます。すべての監査プロパティの setter メソッドを公開します。

また、便利な基本クラス AbstractAuditable もあります。これは、インターフェースメソッドを手動で実装する必要を回避するために拡張できます。これにより、ドメインクラスの Spring Data への結合が増加しますが、これは避けたい場合があります。通常、監査メタデータを定義するアノテーションベースのメソッドは、侵襲性が低く、柔軟性が高いため好まれます。

AuditorAware

@CreatedBy または @LastModifiedBy を使用する場合、監査インフラストラクチャは何らかの形で現在のプリンシパルを認識する必要があります。そのために、現在のユーザーまたはアプリケーションと対話するシステムが誰であるかをインフラストラクチャに伝えるために実装する必要がある AuditorAware<T> SPI インターフェースを提供します。ジェネリックタイプ T は、@CreatedBy または @LastModifiedBy アノテーションが付けられたプロパティのタイプを定義します。

次の例は、Spring Security の Authentication オブジェクトを使用するインターフェースの実装を示しています。

例 113: Spring Security に基づく AuditorAware の実装
class SpringSecurityAuditorAware implements AuditorAware<User> {

  @Override
  public Optional<User> getCurrentAuditor() {

    return Optional.ofNullable(SecurityContextHolder.getContext())
            .map(SecurityContext::getAuthentication)
            .filter(Authentication::isAuthenticated)
            .map(Authentication::getPrincipal)
            .map(User.class::cast);
  }
}

実装は、Spring Security が提供する Authentication オブジェクトにアクセスし、UserDetailsService 実装で作成したカスタム UserDetails インスタンスを検索します。ここでは、UserDetails 実装を介してドメインユーザーを公開しているが、見つかった Authentication に基づいて、どこからでも検索できると想定しています。

ReactiveAuditorAware

リアクティブインフラストラクチャを使用する場合、コンテキスト情報を利用して @CreatedBy または @LastModifiedBy 情報を提供することができます。アプリケーションと対話している現在のユーザーまたはシステムが誰であるかをインフラストラクチャに通知するために実装する必要がある ReactiveAuditorAware<T> SPI インターフェースを提供します。ジェネリック型 T は、@CreatedBy または @LastModifiedBy でアノテーションが付けられたプロパティがどの型でなければならないかを定義します。

次の例は、リアクティブ Spring Security の Authentication オブジェクトを使用するインターフェースの実装を示しています。

例 114: Spring Security に基づく ReactiveAuditorAware の実装
class SpringSecurityAuditorAware implements ReactiveAuditorAware<User> {

  @Override
  public Mono<User> getCurrentAuditor() {

    return ReactiveSecurityContextHolder.getContext()
                .map(SecurityContext::getAuthentication)
                .filter(Authentication::isAuthenticated)
                .map(Authentication::getPrincipal)
                .map(User.class::cast);
  }
}

実装は、Spring Security が提供する Authentication オブジェクトにアクセスし、UserDetailsService 実装で作成したカスタム UserDetails インスタンスを検索します。ここでは、UserDetails 実装を介してドメインユーザーを公開しているが、見つかった Authentication に基づいて、どこからでも検索できると想定しています。:leveloffset:-1

5.9.2. JPA 監査

一般的な監査構成

Spring Data JPA には、監査情報のキャプチャーをトリガーするために使用できるエンティティリスナーが付属しています。まず、次の例に示すように、orm.xml ファイル内の永続コンテキストのすべてのエンティティに使用される AuditingEntityListener を登録する必要があります。

例 115: 監査構成 orm.xml
<persistence-unit-metadata>
  <persistence-unit-defaults>
    <entity-listeners>
      <entity-listener class=" … .data.jpa.domain.support.AuditingEntityListener" />
    </entity-listeners>
  </persistence-unit-defaults>
</persistence-unit-metadata>

次のように、@EntityListeners アノテーションを使用して、エンティティごとに AuditingEntityListener を有効にすることもできます。

@Entity
@EntityListeners(AuditingEntityListener.class)
public class MyEntity {

}
監査機能では、spring-aspects.jar がクラスパス上にある必要があります。

orm.xml が適切に変更され、spring-aspects.jar がクラスパスにある場合、監査機能をアクティブ化するには、次のように Spring Data JPA auditing 名前空間要素を構成に追加します。

例 116: XML 構成を使用した監査のアクティブ化
<jpa:auditing auditor-aware-ref="yourAuditorAwareBean" />

Spring Data JPA 1.5, 以降、構成クラスに @EnableJpaAuditing アノテーションを付けることにより、監査を有効にすることができます。ただし、orm.xml ファイルを変更し、クラスパスに spring-aspects.jar を含める必要があります。次の例は、@EnableJpaAuditing アノテーションの使用方法を示しています。

例 117: Java 構成を使用した監査のアクティブ化
@Configuration
@EnableJpaAuditing
class Config {

  @Bean
  public AuditorAware<AuditableUser> auditorProvider() {
    return new AuditorAwareImpl();
  }
}

タイプ AuditorAware の Bean を ApplicationContext に公開すると、監査インフラストラクチャはそれを自動的に選択し、それを使用してドメインタイプに設定する現在のユーザーを決定します。ApplicationContext に複数の実装が登録されている場合、@EnableJpaAuditing の auditorAwareRef 属性を明示的に設定することにより、使用する実装を選択できます。

5.10. その他の考慮事項

5.10.1. カスタム実装での JpaContext の使用

複数の EntityManager インスタンスとカスタムリポジトリ実装を使用する場合、正しい EntityManager をリポジトリ実装クラスに接続する必要があります。そのためには、@PersistenceContext アノテーションで EntityManager を明示的に命名するか、EntityManager が @Autowired である場合は @Qualifier を使用します。

Spring Data JPA 1.9, 以降、Spring データ JPA には、JpaContext と呼ばれるクラスが含まれています。これにより、アプリケーションの EntityManager インスタンスの 1 つだけで管理されていると仮定して、管理対象ドメインクラスで EntityManager を取得できます。次の例は、カスタムリポジトリで JpaContext を使用する方法を示しています。

例 118: カスタムリポジトリ実装での JpaContext の使用
class UserRepositoryImpl implements UserRepositoryCustom {

  private final EntityManager em;

  @Autowired
  public UserRepositoryImpl(JpaContext context) {
    this.em = context.getEntityManagerByManagedType(User.class);
  }

   …
}

このアプローチの利点は、ドメインタイプが別の永続性ユニットに割り当てられた場合、永続性ユニットへの参照を変更するためにリポジトリに触れる必要がないことです。

5.10.2. 永続性ユニットのマージ

Spring は、複数の永続性ユニットを持つことをサポートしています。ただし、場合によっては、アプリケーションをモジュール化しながら、これらのすべてのモジュールが単一の永続性ユニット内で実行されるようにすることもできます。この動作を可能にするために、Spring Data JPA は、次の例に示すように、名前に基づいて永続性ユニットを自動的にマージする PersistenceUnitManager 実装を提供します。

例 119: MergingPersistenceUnitmanager を使用する
<bean class=" … .LocalContainerEntityManagerFactoryBean">
  <property name="persistenceUnitManager">
    <bean class=" … .MergingPersistenceUnitManager" />
  </property>
</bean>
@Entity クラスと JPA マッピングファイルのクラスパススキャン

プレーンな JPA セットアップでは、すべてのアノテーションがマッピングされたエンティティクラスが orm.xml にリストされている必要があります。同じことが XML マッピングファイルにも当てはまります。Spring Data JPA は、基本パッケージを構成し、オプションでマッピングファイル名パターンを取得する ClasspathScanningPersistenceUnitPostProcessor を提供します。次に、指定されたパッケージをスキャンして @Entity または @MappedSuperclass アノテーションが付けられたクラスを探し、ファイル名パターンに一致する構成ファイルをロードして、JPA 構成に渡します。ポストプロセッサは次のように構成する必要があります。

例 120: ClasspathScanningPersistenceUnitPostProcessor を使用する
<bean class=" … .LocalContainerEntityManagerFactoryBean">
  <property name="persistenceUnitPostProcessors">
    <list>
      <bean class="org.springframework.data.jpa.support.ClasspathScanningPersistenceUnitPostProcessor">
        <constructor-arg value="com.acme.domain" />
        <property name="mappingFileNamePattern" value="**/*Mapping.xml" />
      </bean>
    </list>
  </property>
</bean>
Spring 3.1, 以降、スキャンするパッケージを LocalContainerEntityManagerFactoryBean で直接設定して、エンティティクラスのクラスパススキャンを有効にできます。詳細については、JavaDoc(Javadoc) を参照してください。

5.10.3. CDI 統合

リポジトリインターフェースのインスタンスは通常、コンテナーによって作成されます。Spring Data を使用する場合は、Spring が最も自然な選択です。Spring は、リポジトリインスタンスの作成でドキュメント化されているように、Bean インスタンスを作成するための洗練されたサポートを提供します。バージョン 1.1.0 の時点で、Spring Data JPA には CDI 環境でリポジトリ抽象化を使用できるカスタム CDI 拡張が付属しています。拡張機能は JAR の一部です。有効にするには、クラスパスに Spring Data JPA JAR を含めます。

次の例に示すように、EntityManagerFactory および EntityManager の CDI プロデューサーを実装することにより、インフラストラクチャをセットアップできます。

class EntityManagerFactoryProducer {

  @Produces
  @ApplicationScoped
  public EntityManagerFactory createEntityManagerFactory() {
    return Persistence.createEntityManagerFactory("my-presistence-unit");
  }

  public void close(@Disposes EntityManagerFactory entityManagerFactory) {
    entityManagerFactory.close();
  }

  @Produces
  @RequestScoped
  public EntityManager createEntityManager(EntityManagerFactory entityManagerFactory) {
    return entityManagerFactory.createEntityManager();
  }

  public void close(@Disposes EntityManager entityManager) {
    entityManager.close();
  }
}

必要なセットアップは、JavaEE 環境によって異なります。次のように、EntityManager を CDI Bean として再宣言するだけでよい場合があります。

class CdiConfig {

  @Produces
  @RequestScoped
  @PersistenceContext
  public EntityManager entityManager;
}

上記の例では、コンテナーは JPA EntityManagers 自体を作成できる必要があります。すべての構成は、JPA EntityManager を CDI Bean として再エクスポートするだけです。

Spring Data JPA CDI 拡張機能は、使用可能なすべての EntityManager インスタンスを CDI Bean としてピックアップし、コンテナーによってリポジトリタイプの Bean がリクエストされるたびに、Spring Data リポジトリのプロキシを作成します。Spring Data リポジトリのインスタンスを取得するには、次の例に示すように、@Injected プロパティを宣言する必要があります。

class RepositoryClient {

  @Inject
  PersonRepository repository;

  public void businessMethod() {
    List<Person> people = repository.findAll();
  }
}

付録

付録 A: 名前空間リファレンス

<repositories /> 要素

<repositories /> 要素は、Spring Data リポジトリインフラストラクチャのセットアップをトリガーします。最も重要な属性は base-package です。これは、Spring Data リポジトリインターフェースをスキャンするパッケージを定義します。「XML 構成」を参照してください。次の表は、<repositories /> 要素の属性を説明しています。

表 6: 属性
名前 説明

base-package

自動検出モードで *Repository を継承するリポジトリインターフェース(実際のインターフェースは特定の Spring Data モジュールによって決定されます)をスキャンするパッケージを定義します。設定されたパッケージのすべてのパッケージもスキャンされます。ワイルドカードが許可されます。

repository-impl-postfix

カスタムリポジトリ実装を自動検出するための接尾辞を定義します。名前が構成された接尾辞で終わるクラスは、候補と見なされます。デフォルトは Impl です。

query-lookup-strategy

ファインダー照会の作成に使用される戦略を決定します。詳細については、「クエリ検索戦略」を参照してください。デフォルトは create-if-not-found です。

named-queries-location

外部で定義されたクエリを含むプロパティファイルを検索する場所を定義します。

consider-nested-repositories

ネストされたリポジトリインターフェース定義を考慮する必要があるかどうか。デフォルトは false です。

付録 B: Populators 名前空間リファレンス

<populator /> 要素

<populator /> 要素を使用すると、Spring Data リポジトリインフラストラクチャを介してデータストアにデータを入力できます。[1]

表 7: 属性
名前 説明

locations

リポジトリからオブジェクトを読み取るためのファイルの場所には、データが入力されます。

付録 C: リポジトリクエリキーワード

サポートされているクエリキーワード

次の表は、Spring Data リポジトリクエリ派生メカニズムで一般的にサポートされているキーワードの一覧です。ただし、ここにリストされている一部のキーワードは特定のストアでサポートされていない可能性があるため、サポートされているキーワードの正確なリストについては、ストア固有のドキュメントを参照してください。

表 8: クエリキーワード
論理キーワード キーワード表現

AND

And

OR

Or

AFTER

AfterIsAfter

BEFORE

BeforeIsBefore

CONTAINING

ContainingIsContainingContains

BETWEEN

BetweenIsBetween

ENDING_WITH

EndingWithIsEndingWithEndsWith

EXISTS

Exists

FALSE

FalseIsFalse

GREATER_THAN

GreaterThanIsGreaterThan

GREATER_THAN_EQUALS

GreaterThanEqualIsGreaterThanEqual

IN

InIsIn

IS

IsEquals, (またはキーワードなし)

IS_EMPTY

IsEmptyEmpty

IS_NOT_EMPTY

IsNotEmptyNotEmpty

IS_NOT_NULL

NotNullIsNotNull

IS_NULL

NullIsNull

LESS_THAN

LessThanIsLessThan

LESS_THAN_EQUAL

LessThanEqualIsLessThanEqual

LIKE

LikeIsLike

NEAR

NearIsNear

NOT

NotIsNot

NOT_IN

NotInIsNotIn

NOT_LIKE

NotLikeIsNotLike

REGEX

RegexMatchesRegexMatches

STARTING_WITH

StartingWithIsStartingWithStartsWith

TRUE

TrueIsTrue

WITHIN

WithinIsWithin

付録 D: リポジトリクエリの戻り値のタイプ

サポートされているクエリの戻り値のタイプ

次の表に、Spring Data リポジトリで一般的にサポートされる戻り値の型を示します。ただし、ここにリストされている一部のタイプは特定のストアでサポートされていない可能性があるため、サポートされる戻り型の正確なリストについてはストア固有のドキュメントを参照してください。

地理空間タイプ(GeoResultGeoResultsGeoPage など)は、地理空間クエリをサポートするデータストアでのみ使用できます。
表 9: クエリの戻り型
戻りの型 説明

void

戻り値がないことを示します。

プリミティブ

Java プリミティブ。

ラッパーの種類

Java ラッパータイプ。

T

一意のエンティティ。クエリメソッドが最大で 1 つの結果を返すことを期待します。結果が見つからない場合、null が返されます。複数の結果が IncorrectResultSizeDataAccessException をトリガーします。

Iterator<T>

Iterator

Collection<T>

Collection

List<T>

List

Optional<T>

Java 8 または Guava Optional クエリメソッドが最大で 1 つの結果を返すことを期待します。結果が見つからない場合、Optional.empty() または Optional.absent() が返されます。複数の結果が IncorrectResultSizeDataAccessException をトリガーします。

Option<T>

Scala または Vavr Option タイプのいずれか。前述の Java 8 の Optional と同じ意味の振る舞い。

Stream<T>

Java 8 Stream

Streamable<T>

Iterable の便利な拡張機能で、結果のストリーミング、マッピング、フィルタリング、連結などのメソッドを直接公開します。

Streamable を実装し、Streamable コンストラクターまたはファクトリメソッド引数を取るタイプ

Streamable を引数として取るコンストラクターまたは  … .of(…)/ … .valueOf(…) ファクトリメソッドを公開する型。詳細については、カスタムのストリーミング可能なラッパータイプを返すを参照してください。

Vavr SeqListMapSet

Vavr コレクションタイプ。詳細については、Vavr コレクションのサポートを参照してください。

Future<T>

Future メソッドに @Async のアノテーションが付けられることを期待し、Spring の非同期メソッド実行機能を有効にする必要があります。

CompletableFuture<T>

Java 8 CompletableFuture メソッドに @Async のアノテーションが付けられることを期待し、Spring の非同期メソッド実行機能を有効にする必要があります。

ListenableFuture

org.springframework.util.concurrent.ListenableFuture メソッドに @Async のアノテーションが付けられることを期待し、Spring の非同期メソッド実行機能を有効にする必要があります。

Slice

使用可能なデータがさらにあるかどうかを示すサイズのデータチャンク。Pageable メソッドパラメーターが必要です。

Page<T>

結果の総数などの追加情報を含む SlicePageable メソッドパラメーターが必要です。

GeoResult<T>

参照場所までの距離などの追加情報を含む結果エントリ。

GeoResults<T>

参照場所までの平均距離などの追加情報を含む GeoResult<T> のリスト。

GeoPage<T>

参照位置までの平均距離など、Page と GeoResult<T>

Mono<T>

リアクティブリポジトリを使用して 0 個または 1 個の要素を放出するプロジェクト Reactor Mono。クエリメソッドが最大で 1 つの結果を返すことを期待します。結果が見つからない場合、Mono.empty() が返されます。複数の結果が IncorrectResultSizeDataAccessException をトリガーします。

Flux<T>

プロジェクト Reactor Flux は、リアクティブリポジトリを使用してゼロ、1 つ、または多くの要素を放出します。Flux を返すクエリは、無限の数の要素も放出できます。

Single<T>

リアクティブリポジトリを使用して単一の要素を放出する RxJava Single。クエリメソッドが最大で 1 つの結果を返すことを期待します。結果が見つからない場合、Mono.empty() が返されます。複数の結果が IncorrectResultSizeDataAccessException をトリガーします。

Maybe<T>

リアクティブリポジトリを使用して 0 個または 1 個の要素を放出する RxJava Maybe。クエリメソッドが最大で 1 つの結果を返すことを期待します。結果が見つからない場合、Mono.empty() が返されます。複数の結果が IncorrectResultSizeDataAccessException をトリガーします。

Flowable<T>

リアクティブリポジトリを使用してゼロ、1 つ、または多くの要素を放出する RxJava FlowableFlowable を返すクエリは、無限の数の要素も放出できます。

付録 E: よくある質問

共通

  1. たとえば、JpaRepository 内で呼び出されるメソッドに関する詳細なログ情報を取得したいと思います。どうすれば獲得できますか?

    次の例に示すように、Spring が提供する CustomizableTraceInterceptor を使用できます。

    <bean id="customizableTraceInterceptor" class="
      org.springframework.aop.interceptor.CustomizableTraceInterceptor">
      <property name="enterMessage" value="Entering $[methodName]($[arguments])"/>
      <property name="exitMessage" value="Leaving $[methodName](): $[returnValue]"/>
    </bean>
    
    <aop:config>
      <aop:advisor advice-ref="customizableTraceInterceptor"
        pointcut="execution(public * org.springframework.data.jpa.repository.JpaRepository+.*(..))"/>
    </aop:config>

インフラ

  1. 現在、HibernateDaoSupport に基づくリポジトリ層を実装しています。Spring の AnnotationSessionFactoryBean を使用して SessionFactory を作成します。この環境で Spring Data リポジトリを機能させるにはどうすればよいですか?

    次のように、AnnotationSessionFactoryBean を HibernateJpaSessionFactoryBean に置き換える必要があります。

    例 121: HibernateEntityManagerFactory から SessionFactory を検索する
    <bean id="sessionFactory" class="org.springframework.orm.jpa.vendor.HibernateJpaSessionFactoryBean">
      <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>

監査

  1. Spring Data JPA 監査機能を使用したいのですが、エンティティの変更日と作成日を設定するようにデータベースが既に構成されています。Spring Data がプログラムで日付を設定しないようにするにはどうすればよいですか?

    auditing 名前空間要素の set-dates 属性を false に設定します。

付録 F: 用語集

AOP

アスペクト指向プログラミング

Commons DBCP

コモンズ DataBase 接続プール - DataSource インターフェースのプーリング実装を提供する Apache Foundation のライブラリ。

CRUD

作成、読み取り、更新、削除 - 基本的な永続化操作。

DAO

データアクセスオブジェクト - 永続化するオブジェクトから永続化ロジックを分離するパターン

依存性注入

外部からコンポーネントへのコンポーネントの依存関係を渡すパターン。コンポーネントを解放して、依存関係自体をルックアップします。詳細については、https://ja.wikipedia.org/wiki/ 依存性の注入 を参照してください。

EclipseLink

JPA を実装するオブジェクトリレーショナルマッパー -https://www.eclipse.org/eclipselink/ (英語)

Hibernate

JPA を実装するオブジェクトリレーショナルマッパー -https://hibernate.org/ (英語)

JPA

Java Persistence API

Spring

Java アプリケーションフレームワーク - /projects/spring-framework


1. XML 構成を参照