JPAリポジトリのBootstrapMode
1. 序章
この簡単なチュートリアルでは、インスタンス化のオーケストレーションを変更するためにSpringが提供するJPAリポジトリーのさまざまなタイプのBootstrapModeに焦点を当てます。
起動時に、Spring Dataはリポジトリをスキャンし、それらのBean定義をシングルトンスコープのBeanとして登録します。 初期化中に、リポジトリはEntityManagerをすぐに取得します。 具体的には、JPAメタモデルを取得し、宣言されたクエリを検証します。
2. リポジトリをブートストラップするためのさまざまなオプション
spring-data-jpa依存関係を追加することから始めましょう。 Spring Bootを使用しているため、対応するspring-boot-starter-data-jpa依存関係を使用します。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
構成プロパティを介して、デフォルトのリポジトリブートストラップ動作を使用するようにSpringに指示できます。
spring.data.jpa.repositories.bootstrap-mode=default
注釈ベースの構成を使用して同じことを行うことができます。
@SpringBootApplication
@EnableJpaRepositories(bootstrapMode = BootstrapMode.DEFAULT)
public class Application {
// ...
}
単一のテストクラスに限定された3番目のアプローチは、@DataJpaTestアノテーションを使用することです。
@DataJpaTest(bootstrapMode = BootstrapMode.LAZY)
class BootstrapmodeLazyIntegrationTest {
// ...
}
次の例では、 @DataJpaTestアノテーションを使用して、さまざまなリポジトリブートストラップオプションを調べます。
2.1. デフォルト
ブートストラップモードのデフォルト値は、リポジトリを熱心にインスタンス化します。 したがって、他のSpring Beanと同様に、それらの初期化は注入されたときに発生します。
Todoエンティティを作成しましょう。
@Entity
public class Todo {
@Id
private Long id;
private String label;
// standard setters and getters
}
次に、関連するリポジトリが必要になります。 CrudRepositoryを拡張するものを作成しましょう。
public interface TodoRepository extends CrudRepository<Todo, Long> {
}
最後に、リポジトリを使用するテストを追加しましょう。
@DataJpaTest
class BootstrapmodeDefaultIntegrationTest {
@Autowired
private TodoRepository todoRepository;
@Test
void givenBootstrapmodeValueIsDefault_whenCreatingTodo_shouldSuccess() {
Todo todo = new Todo("Something to be done");
assertThat(todoRepository.save(todo)).hasNoNullFieldsOrProperties();
}
}
テストを開始した後、SpringがTodoRepositoryをどのようにブートストラップしたかを確認するログを確認しましょう。
[2022-03-22 14:46:47,597]-[main] INFO org.springframework.data.repository.config.RepositoryConfigurationDelegate - Bootstrapping Spring Data JPA repositories in DEFAULT mode.
[2022-03-22 14:46:47,737]-[main] TRACE org.springframework.data.repository.config.RepositoryConfigurationDelegate - Spring Data JPA - Registering repository: todoRepository - Interface: com.baeldung.boot.bootstrapmode.repository.TodoRepository - Factory: org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean
[2022-03-22 14:46:49,718]-[main] DEBUG org.springframework.data.repository.core.support.RepositoryFactorySupport - Initializing repository instance for com.baeldung.boot.bootstrapmode.repository.TodoRepository…
[2022-03-22 14:46:49,792]-[main] DEBUG org.springframework.data.repository.core.support.RepositoryFactorySupport - Finished creation of repository instance for com.baeldung.boot.bootstrapmode.repository.TodoRepository.
[2022-03-22 14:46:49,858]-[main] INFO com.baeldung.boot.bootstrapmode.BootstrapmodeDefaultIntegrationTest - Started BootstrapmodeDefaultIntegrationTest in 3.547 seconds (JVM running for 4.877)
この例では、リポジトリを早期に初期化し、アプリケーションの起動後にそれらを利用できるようにします。
2.2. 怠惰
JPAリポジトリにレイジーBootstrapModeを使用することにより、Springはリポジトリのbean定義を登録しますが、すぐにはインスタンス化しません。 したがって、レイジーオプションを使用すると、最初の使用で初期化がトリガーされます。
テストを変更して、レイジーオプションをbootstrapModeに適用しましょう。
@DataJpaTest(bootstrapMode = BootstrapMode.LAZY)
次に、新しい構成でテストを開始し、対応するログを確認しましょう。
[2022-03-22 15:09:01,360]-[main] INFO org.springframework.data.repository.config.RepositoryConfigurationDelegate - Bootstrapping Spring Data JPA repositories in LAZY mode.
[2022-03-22 15:09:01,398]-[main] TRACE org.springframework.data.repository.config.RepositoryConfigurationDelegate - Spring Data JPA - Registering repository: todoRepository - Interface: com.baeldung.boot.bootstrapmode.repository.TodoRepository - Factory: org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean
[2022-03-22 15:09:01,971]-[main] INFO com.baeldung.boot.bootstrapmode.BootstrapmodeLazyIntegrationTest - Started BootstrapmodeLazyIntegrationTest in 1.299 seconds (JVM running for 2.148)
[2022-03-22 15:09:01,976]-[main] DEBUG org.springframework.data.repository.config.RepositoryConfigurationDelegate$LazyRepositoryInjectionPointResolver - Creating lazy injection proxy for com.baeldung.boot.bootstrapmode.repository.TodoRepository…
[2022-03-22 15:09:02,588]-[main] DEBUG org.springframework.data.repository.core.support.RepositoryFactorySupport - Initializing repository instance for com.baeldung.boot.bootstrapmode.repository.TodoRepository…
ここでは、いくつかの欠点に注意を払う必要があります。
- Springは、リポジトリを初期化せずにリクエストの受け入れを開始する可能性があるため、最初のリポジトリが処理されるときのレイテンシが増加します。
- BootstrapMode をグローバルにレイジーに設定すると、エラーが発生しやすくなります。 Springは、テストに含まれていないリポジトリに含まれているクエリとメタデータを検証しません。
初期化エラーの可能性があるアプリケーションを本番環境にデプロイしないように、開発中のみレイジーブートストラップを使用する必要があります。 この目的のために、 SpringProfilesをエレガントに使用できます。
2.3. 延期
Deferredは、JPAを非同期でブートストラップするときに使用する適切なオプションです。 その結果、リポジトリはEntityManagerFactoryの初期化を待機しません。
ThreadPoolTaskExecutor (Spring実装の1つ)を使用して構成クラスで AsyncTaskExecutor を宣言し、submitメソッドをオーバーライドしてを返します。 Future :
@Bean
AsyncTaskExecutor delayedTaskExecutor() {
return new ThreadPoolTaskExecutor() {
@Override
public <T> Future<T> submit(Callable<T> task) {
return super.submit(() -> {
Thread.sleep(5000);
return task.call();
});
}
};
}
次に、 Spring を使用したJPAガイドに示すように、 EntityManagerFactory beanを構成に追加し、バックグラウンドブートストラップに非同期エグゼキューターを使用することを示します。
@Bean
LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource, AsyncTaskExecutor delayedTaskExecutor) {
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setPackagesToScan("com.baeldung.boot.bootstrapmode");
factory.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
factory.setDataSource(dataSource);
factory.setBootstrapExecutor(delayedTaskExecutor);
Map<String, Object> properties = new HashMap<>();
properties.put("hibernate.hbm2ddl.auto", "create-drop");
factory.setJpaPropertyMap(properties);
return factory;
}
最後に、遅延ブートストラップモードを有効にするようにテストを変更しましょう。
@DataJpaTest(bootstrapMode = BootstrapMode.DEFERRED)
テストをもう一度起動して、ログを確認しましょう。
[2022-03-23 10:31:16,513]-[main] INFO org.springframework.data.repository.config.RepositoryConfigurationDelegate - Bootstrapping Spring Data JPA repositories in DEFERRED mode.
[2022-03-23 10:31:16,543]-[main] TRACE org.springframework.data.repository.config.RepositoryConfigurationDelegate - Spring Data JPA - Registering repository: todoRepository - Interface: com.baeldung.boot.bootstrapmode.repository.TodoRepository - Factory: org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean
[2022-03-23 10:31:16,545]-[main] DEBUG org.springframework.data.repository.config.RepositoryConfigurationDelegate - Registering deferred repository initialization listener.
[2022-03-23 10:31:17,108]-[main] INFO org.springframework.data.repository.config.DeferredRepositoryInitializationListener - Triggering deferred initialization of Spring Data repositories…
[2022-03-23 10:31:22,538]-[main] DEBUG org.springframework.data.repository.core.support.RepositoryFactorySupport - Initializing repository instance for com.baeldung.boot.bootstrapmode.repository.TodoRepository…
[2022-03-23 10:31:22,572]-[main] INFO com.baeldung.boot.bootstrapmode.BootstrapmodeDeferredIntegrationTest - Started BootstrapmodeDeferredIntegrationTest in 6.769 seconds (JVM running for 7.519)
この例では、アプリケーションコンテキストのブートストラップが完了すると、リポジトリの初期化がトリガーされます。 つまり、Springはリポジトリをレイジーとしてマークし、DeferredRepositoryInitializationListenerBeanを登録します。 ApplicationContextがContextRefreshedEventを起動すると、すべてのリポジトリが初期化されます。
したがって、Spring Dataは、アプリケーションの起動前にリポジトリを初期化し、含まれているクエリとメタデータを検証します。
3. 結論
この記事では、 JPAリポジトリーを初期化するさまざまな方法と、それらを使用する場合について説明しました。 いつものように、この記事で使用されているすべてのコードサンプルは、GitHubのにあります。