© 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の ZoneIdAttributeConverter 実装。

  • 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-releasetrain</artifactId>
      <version>Moore-SR4</version>
      <scope>import</scope>
      <type>pom</type>
    </dependency>
  </dependencies>
</dependencyManagement>

現在のリリーストレインバージョンは Moore-SR4です。列車名はアルファベット順に昇順になり、現在利用可能な列車がここにリストされます(GitHub) 。バージョン名は次のパターンに従います: ${name}-${release}、ここでreleaseは以下のいずれかです。

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

  • M1, M2など: マイルストーン

  • RC1, RC2など: リリース候補

  • RELEASE : GAリリース

  • SR1, SR2など: サービスリリース

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.2.3.RELEASE以上の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のエンティティが存在するかどうかを示します。
また、JpaRepositoryMongoRepositoryなどの永続化技術固有の抽象化も提供します。これらのインターフェースは 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. リポジトリ定義の微調整

通常、リポジトリインターフェースは Repository, CrudRepositoryまたは 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アノテーションを使用したドメインクラスを示しています。 JpaPersonRepositoryMongoDBPersonRepositoryの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 (デフォルト)は、CREATEUSE_DECLARED_QUERYを組み合わせたものです。最初に宣言されたクエリを検索し、宣言されたクエリが見つからない場合は、カスタムメソッド名ベースのクエリを作成します。これはデフォルトのルックアップ戦略であるため、明示的に何も設定しない場合に使用されます。メソッド名による迅速なクエリ定義だけでなく、必要に応じて宣言されたクエリを導入することにより、これらのクエリのカスタムチューニングも可能です。

4.4.2. クエリ作成

Spring Dataリポジトリインフラストラクチャに組み込まれたクエリビルダメカニズムは、リポジトリのエンティティに対して制約クエリを構築できます。このメカニズムは、メソッドからプレフィックス find…By, read…By, query…By, count…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と組み合わせることができます。また、プロパティ式の Between, LessThan, GreaterThanLike などの演算子のサポートも取得できます。サポートされる演算子はデータストアによって異なる場合があるため、リファレンスドキュメントの適切な部分を参照してください。

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

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

4.4.3. プロパティ式

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

List<Person> findByAddressZipCode(ZipCode zipCode);

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

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

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

List<Person> findByAddress_ZipCode(ZipCode zipCode);

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

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

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

例 14: 照会メソッドで Pageable, Sliceおよび 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);

TypedSort<Person> sort = person.by(Person::getFirstname).ascending()
  .and(person.by(Person::getLastname).descending());

ストアの実装が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 Iterable, List, Setを使用できます。さらに、Spring Dataの StreamableIterableのカスタム拡張、およびVavr(英語) が提供するコレクションタイプの返送をサポートしています。

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

Streamable は、Iterable または任意のコレクションタイプの代替として使用できます。非並列 StreamIterableにない)にアクセスするための便利なメソッド、直接 ….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 エンティティ。
2 Products.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 をスローします。メソッドに渡された emailAddressnullの場合、IllegalArgumentException をスローします。
3実行されたクエリが結果を生成しない場合、null を返します。 emailAddressの値として null も受け入れます。
4実行されたクエリが結果を生成しない場合、Optional.empty() を返します。メソッドに渡された emailAddressnullの場合、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で登録されます。 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(…) メソッドの1つが呼び出されるたびに呼び出されます。

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.
}
1 Predicateに一致する単一のエンティティを検索して返します。
2 Predicateに一致するすべてのエンティティを検索して返します。
3 Predicateに一致するエンティティの数を返します。
4 Predicate に一致するエンティティが存在するかどうかを返します。

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

前のセクションで示した構成スニペットは、PageableHandlerMethodArgumentResolverSortHandlerMethodArgumentResolverのインスタンスも登録します。次の例に示すように、登録により、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)でソートする必要があるプロパティ。デフォルトのソート方向は昇順です。方向を切り替える場合は、複数の sort パラメーターを使用します(例: ?sort=firstname&sort=lastname,asc)。

この動作をカスタマイズするには、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_pagething2_page などを入力する必要があります。

メソッドに渡されるデフォルト PageablePageRequest.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(英語) 式(ジェイウェイ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を使用して実行できます。

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

次の例は、メソッドシグネチャーで @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";
  }
}
1 Userの一致する Predicate にクエリ文字列引数を解決します。

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

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

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

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

これらのバインディングは、@QuerydslPredicatebindings 属性を介して、または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)
  }
}
1 QuerydslPredicateExecutor は、Predicateの特定のファインダーメソッドへのアクセスを提供します。
2 リポジトリインターフェースで定義されたQuerydslBinderCustomizer が自動的に選択され、ショートカット @QuerydslPredicate(bindings=…​)が選択されます。
3 username プロパティのバインディングを単純な contains バインディングとして定義します。
4 String プロパティのデフォルトのバインディングを、大文字と小文字を区別しない contains 一致になるように定義します。
5 password プロパティを 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は、ApplicationContextentityManagerFactory という名前の 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-jdbcEmbeddedDatabaseBuilder 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がインスタンスをフィールドに単に格納し、初期化中にリポジトリを使用しない場合、リポジトリはインスタンス化されません。リポジトリインスタンスは、リポジトリとの最初の対話時に初期化および検証されます。

  • DEFERREDLAZYと基本的に同じ動作モードですが、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(英語) を参照してください。

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

5.3. クエリメソッド

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

5.3.1. クエリ検索戦略

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

述語 IsStartingWith, StartingWith, StartsWith, IsEndingWith, EndingWith, EndsWith, IsNotContaining, NotContaining, NotContains, IsContaining, Containing, Contains を持つ派生クエリは、これらのクエリのそれぞれの引数がサニタイズされます。つまり、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

Is, Equals

findByFirstname, findByFirstnameIs, findByFirstnameEquals

… 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

IsNull, Null

findByAge(Is)Null

… where x.age is null

IsNotNull, NotNull

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 を直接使用して行うことができます。 SortOrder インスタンス内で実際に使用されるプロパティは、ドメインモデルに一致する必要があります。つまり、クエリ内で使用されるプロパティまたはエイリアスに解決する必要があります。JPQLは、これを状態フィールドパス式として定義します。

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

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

次の例では、SortJpaSortを使用しています。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(…) メソッドも定義します。これは、特殊なリポジトリインターフェースのインスタンスで使用できます。 ConcreteRepositoryfindByAllAttribute(…) を呼び出すと、クエリは 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 user.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表現を聞かせてそれ以前

クラスベースのプロジェクション (DTO)

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

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

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

例 87: 投影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() メソッドを自動的に実装するコンストラクターを公開します。

動的投影

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

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

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

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

例 89: 動的投影でリポジトリを使用する
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 アノテーションを導入しました。

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

例90: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 アノテーションを使用して構成できます。

例91:エンティティの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 属性として指定できます。 value, procedureNamename も設定されていない場合、リポジトリメソッドの名前が name 属性として使用されます。

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

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

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

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

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

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

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

例95: 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 と組み合わせて使用し、必要な組み合わせごとにクエリ(メソッド)を宣言する必要なしに使用できます。

例 96: 顧客の仕様
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 がタイプ DatecreatedAt 属性を持っていると想定しています。それに加えて、ビジネス要件の抽象化レベルに関するいくつかの基準を表明し、実行可能な Specificationsを作成しました。クライアントは次のように Specification を使用する場合があります。

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

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

例 98: 組み合わせ仕様
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つの部分で構成されています。

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

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

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

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

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

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

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

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

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

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

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

例 99: サンプル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 は不変です。次のリストは、簡単な例を示しています。

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

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

サンプルはリポジトリを使用して実行するのが理想的です。そのためには、リポジトリインターフェースで QueryByExampleExecutor<T>を継承します。次のリストは、QueryByExampleExecutor インターフェースからの抜粋です。

例 101: 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処理、およびプロパティ固有の設定に独自のデフォルトを指定できます。

例 102: カスタマイズされたマッチングを使用したマッチャーの例
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セットのプロパティ。
3 ExampleMatcher を作成して、すべての値が一致することを期待します。この段階では、さらに構成しなくても使用できます。
4 lastname プロパティパスを無視する新しい ExampleMatcher を構築します。
5新しい ExampleMatcher を作成して、lastname プロパティパスを無視し、null値を含めます。
6新しい ExampleMatcher を作成して、lastname プロパティパスを無視し、null値を含め、サフィックス文字列の照合を実行します。
7ドメインオブジェクトと設定された ExampleMatcherに基づいて新しい Example を作成します。

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

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

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

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

例 104: ラムダを使用したマッチャーオプションの構成
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では、次の例に示すように、リポジトリで例によるクエリを使用できます。

例 105: リポジトリを使用した例によるクエリ
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 プロパティのみです。

プロパティ指定子は、プロパティ名( firstnamelastnameなど)を受け入れます。プロパティをドット(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を参照してください。リポジトリで宣言されたメソッドのいずれかのトランザクション構成を微調整する必要がある場合は、次のようにリポジトリインターフェースでメソッドを再宣言します。

例 106: 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操作のトランザクション境界を定義することです。次の例は、そのようなファサードを複数のリポジトリに使用する方法を示しています。

例 107: ファサードを使用して複数のリポジトリ呼び出しのトランザクションを定義する
@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 を明示的に使用する必要があることに注意してください。この例では、コンポーネントスキャンを使用することを前提としています。

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

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

例 108: クエリメソッドで@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 アノテーションを使用できます。

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

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

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

例 110: 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 を提供します。

例 111: 監査対象エンティティ
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 オブジェクトを使用するインターフェースの実装を示しています。

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

  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 に基づいて、どこからでも検索できると想定しています。:leveloffset:-1

5.9.2. JPA監査

一般的な監査構成

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

例 113: 監査構成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 名前空間要素を構成に追加します。

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

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

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

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

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

5.10. その他の考慮事項

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

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

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

例 116: カスタムリポジトリ実装での 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 実装を提供します。

例 117: 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構成に渡します。ポストプロセッサは次のように構成する必要があります。

例 118: 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

After, IsAfter

BEFORE

Before, IsBefore

CONTAINING

Containing, IsContaining, Contains

BETWEEN

Between, IsBetween

ENDING_WITH

EndingWith, IsEndingWith, EndsWith

EXISTS

Exists

FALSE

False, IsFalse

GREATER_THAN

GreaterThan, IsGreaterThan

GREATER_THAN_EQUALS

GreaterThanEqual, IsGreaterThanEqual

IN

In, IsIn

IS

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

IS_EMPTY

IsEmpty, Empty

IS_NOT_EMPTY

IsNotEmpty, NotEmpty

IS_NOT_NULL

NotNull, IsNotNull

IS_NULL

Null, IsNull

LESS_THAN

LessThan, IsLessThan

LESS_THAN_EQUAL

LessThanEqual, IsLessThanEqual

LIKE

Like, IsLike

NEAR

Near, IsNear

NOT

Not, IsNot

NOT_IN

NotIn, IsNotIn

NOT_LIKE

NotLike, IsNotLike

REGEX

Regex, MatchesRegex, Matches

STARTING_WITH

StartingWith, IsStartingWith, StartsWith

TRUE

True, IsTrue

WITHIN

Within, IsWithin

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

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

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

地理空間タイプ( GeoResult, GeoResultsGeoPageなど)は、地理空間クエリをサポートするデータストアでのみ使用できます。
表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 Seq, List, Map, Set

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>

参照位置までの平均距離など、PageGeoResult<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リポジトリを機能させるにはどうすればよいですか?

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

    例 119: 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

Commons 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 構成を参照

Unofficial Translation by spring.pleiades.io. See the original content.