データ統合
Spring for GraphQL を使用すると、既存の Spring テクノロジを活用し、一般的なプログラミングモデルに従って、GraphQL を介して基になるデータソースを公開できます。
このセクションでは、@GraphQlRepository でマークされたリポジトリの自動検出および GraphQL クエリ登録のオプションを含む、Querydsl または Query by Example リポジトリを DataFetcher に適応させる簡単な方法を提供する Spring Data の統合レイヤーについて説明します。
Querydsl
Spring for GraphQL は、Querydsl (英語) を使用して Spring Data QueryDSL 拡張機能を介してデータをフェッチすることをサポートしています。Querydsl は、アノテーションプロセッサーを使用してメタモデルを生成することにより、クエリ述語を表現するための柔軟で型安全なアプローチを提供します。
例: リポジトリを QuerydslPredicateExecutor として宣言します。
public interface AccountRepository extends Repository<Account, Long>,
QuerydslPredicateExecutor<Account> {
} 次に、それを使用して DataFetcher を作成します。
// For single result queries
DataFetcher<Account> dataFetcher =
QuerydslDataFetcher.builder(repository).single();
// For multi-result queries
DataFetcher<Iterable<Account>> dataFetcher =
QuerydslDataFetcher.builder(repository).many();
// For paginated queries
DataFetcher<Iterable<Account>> dataFetcher =
QuerydslDataFetcher.builder(repository).scrollable(); 上記の DataFetcher を RuntimeWiringConfigurer で登録できるようになりました。
DataFetcher は、GraphQL 引数から Querydsl Predicate を構築し、それを使用してデータをフェッチします。Spring Data は、JPA、MongoDB、Neo4j、LDAP の QuerydslPredicateExecutor をサポートします。
GraphQL 入力型である単一の引数の場合、QuerydslDataFetcher は 1 レベル下にネストし、引数のサブマップの値を使用します。 |
リポジトリが ReactiveQuerydslPredicateExecutor の場合、ビルダーは DataFetcher<Mono<Account>> または DataFetcher<Flux<Account>> を返します。Spring Data は、MongoDB および Neo4j のこのバリアントをサポートしています。
ビルドのセットアップ
ビルドで Querydsl を構成するには、公式のリファレンスドキュメント (英語) に従ってください。
例:
dependencies {
//...
annotationProcessor "com.querydsl:querydsl-apt:$querydslVersion:jakarta",
'jakarta.persistence:jakarta.persistence-api'
}
compileJava {
options.annotationProcessorPath = configurations.annotationProcessor
}
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<!-- Explicit opt-in required via annotationProcessors or
annotationProcessorPaths on Java 22+, see https://bugs.openjdk.org/browse/JDK-8306819 -->
<annotationProcessorPath>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>${querydsl.version}</version>
<classifier>jakarta</classifier>
</annotationProcessorPath>
<annotationProcessorPath>
<groupId>jakarta.persistence</groupId>
<artifactId>jakarta.persistence-api</artifactId>
</annotationProcessorPath>
</annotationProcessorPaths>
<!-- Recommended: Some IDE's might require this configuration to include generated sources for IDE usage -->
<generatedTestSourcesDirectory>target/generated-test-sources</generatedTestSourcesDirectory>
<generatedSourcesDirectory>target/generated-sources</generatedSourcesDirectory>
</configuration>
</plugin>
</plugins>
</build>
カスタム
QuerydslDataFetcher は、GraphQL 引数をプロパティにバインドして Querydsl Predicate を作成する方法のカスタマイズをサポートしています。デフォルトでは、引数は使用可能な各プロパティに対して「等しい」としてバインドされます。これをカスタマイズするには、QuerydslDataFetcher ビルダーメソッドを使用して QuerydslBinderCustomizer を提供します。
リポジトリ自体が QuerydslBinderCustomizer のインスタンスである場合があります。これは自動検出され、自動登録中に透過的に適用されます。ただし、手動で QuerydslDataFetcher をビルドする場合は、ビルダーメソッドを使用して適用する必要があります。
QuerydslDataFetcher はインターフェースと DTO 射影をサポートしてクエリ結果を変換してから、これらをさらに GraphQL 処理のために返します。
| 射影とは何かについては、Spring Data ドキュメントを参照してください。GraphQL で射影を使用する方法を理解するには、選択セットと射影を参照してください。 |
Querydsl リポジトリで Spring Data 射影を使用するには、射影 インターフェースまたはターゲット DTO クラスのいずれかを作成し、projectAs メソッドを介して構成して、ターゲット型を生成する DataFetcher を取得します。
class Account {
String name, identifier, description;
Person owner;
}
interface AccountProjection {
String getName();
String getIdentifier();
}
// For single result queries
DataFetcher<AccountProjection> dataFetcher =
QuerydslDataFetcher.builder(repository).projectAs(AccountProjection.class).single();
// For multi-result queries
DataFetcher<Iterable<AccountProjection>> dataFetcher =
QuerydslDataFetcher.builder(repository).projectAs(AccountProjection.class).many();自動登録
リポジトリに @GraphQlRepository のアノテーションが付けられている場合、DataFetcher がまだ登録されておらず、戻り値の型がリポジトリのドメイン型と一致するクエリに対して自動的に登録されます。これには、単一値クエリ、複数値クエリ、ページ分割されたクエリが含まれます。
デフォルトでは、クエリによって返される GraphQL 型の名前は、リポジトリドメイン型の単純な名前と一致する必要があります。必要に応じて、@GraphQlRepository の typeName 属性を使用して、ターゲットの GraphQL 型名を指定できます。
ページ分割されたクエリの場合、リポジトリドメイン型の単純名は、末尾 Connection を除いた Connection 型名と一致する必要があります (例: Book は BooksConnection と一致します)。自動登録の場合、ページ分割はオフセットベースで、1 ページあたり 20 項目になります。
自動登録は、特定のリポジトリが QuerydslBinderCustomizer を実装しているかどうかを検出し、QuerydslDataFetcher ビルダーメソッドを介して透過的に適用します。
自動登録は、QuerydslDataFetcher から取得できる組み込みの RuntimeWiringConfigurer を介して実行されます。Boot スターターは @GraphQlRepository Bean を自動的に検出し、使用して RuntimeWiringConfigurer を初期化します。
リポジトリがそれぞれ QuerydslBuilderCustomizer または ReactiveQuerydslBuilderCustomizer を実装している場合、自動登録はリポジトリインスタンスで customize(Builder) を呼び出すことによってカスタマイズを適用します。
例示による問い合わせ
Spring Data は、例示による問い合わせを使用したデータのフェッチをサポートしています。例示による問い合わせ (QBE) は、ストア固有のクエリ言語を使用してクエリを記述する必要のない単純なクエリ手法です。
QueryByExampleExecutor であるリポジトリを宣言することから始めます。
public interface AccountRepository extends Repository<Account, Long>,
QueryByExampleExecutor<Account> {
}QueryByExampleDataFetcher を使用して、リポジトリを DataFetcher に変換します。
// For single result queries
DataFetcher<Account> dataFetcher =
QueryByExampleDataFetcher.builder(repository).single();
// For multi-result queries
DataFetcher<Iterable<Account>> dataFetcher =
QueryByExampleDataFetcher.builder(repository).many();
// For paginated queries
DataFetcher<Iterable<Account>> dataFetcher =
QueryByExampleDataFetcher.builder(repository).scrollable(); 上記の DataFetcher を RuntimeWiringConfigurer で登録できるようになりました。
DataFetcher は、GraphQL 引数マップを使用してリポジトリのドメイン型を作成し、それをサンプルオブジェクトとして使用してデータをフェッチします。Spring Data は、JPA、MongoDB、Neo4j、Redis の QueryByExampleDataFetcher をサポートします。
GraphQL 入力型である単一の引数の場合、QueryByExampleDataFetcher は 1 レベル下にネストし、引数のサブマップの値とバインドします。 |
リポジトリが ReactiveQueryByExampleExecutor の場合、ビルダーは DataFetcher<Mono<Account>> または DataFetcher<Flux<Account>> を返します。Spring Data は、MongoDB、Neo4j、Redis、R2dbc のこのバリアントをサポートしています。
カスタム
QueryByExampleDataFetcher はインターフェースと DTO 射影をサポートしてクエリ結果を変換してから、これらをさらに GraphQL 処理のために返します。
| 射影とは何かについては、Spring Data ドキュメントを参照してください。GraphQL での射影のロールを理解するには、選択セットと射影を参照してください。 |
例示による問い合わせ リポジトリで Spring Data 射影を使用するには、射影 インターフェースまたはターゲット DTO クラスのいずれかを作成し、projectAs メソッドを介して構成して、ターゲット型を生成する DataFetcher を取得します。
class Account {
String name, identifier, description;
Person owner;
}
interface AccountProjection {
String getName();
String getIdentifier();
}
// For single result queries
DataFetcher<AccountProjection> dataFetcher =
QueryByExampleDataFetcher.builder(repository).projectAs(AccountProjection.class).single();
// For multi-result queries
DataFetcher<Iterable<AccountProjection>> dataFetcher =
QueryByExampleDataFetcher.builder(repository).projectAs(AccountProjection.class).many();自動登録
リポジトリに @GraphQlRepository のアノテーションが付けられている場合、DataFetcher がまだ登録されておらず、戻り値の型がリポジトリのドメイン型と一致するクエリに対して自動的に登録されます。これには、単一値クエリ、複数値クエリ、ページ分割されたクエリが含まれます。
デフォルトでは、クエリによって返される GraphQL 型の名前は、リポジトリドメイン型の単純な名前と一致する必要があります。必要に応じて、@GraphQlRepository の typeName 属性を使用して、ターゲットの GraphQL 型名を指定できます。
ページ分割されたクエリの場合、リポジトリドメイン型の単純名は、末尾 Connection を除いた Connection 型名と一致する必要があります (例: Book は BooksConnection と一致します)。自動登録の場合、ページ分割はオフセットベースで、1 ページあたり 20 項目になります。
自動登録は、QueryByExampleDataFetcher から取得できる組み込みの RuntimeWiringConfigurer を介して実行されます。Boot スターターは @GraphQlRepository Bean を自動的に検出し、使用して RuntimeWiringConfigurer を初期化します。
リポジトリがそれぞれ QueryByExampleBuilderCustomizer または ReactiveQueryByExampleBuilderCustomizer を実装している場合、自動登録はリポジトリインスタンスで customize(Builder) を呼び出すことによってカスタマイズを適用します。
選択セットと射影
発生する一般的な質問は、GraphQL 選択セットを Spring Data Projection と比較して、それぞれがどのようなロールを果たしているのかということです。
簡単に言えば、Spring for GraphQL は、GraphQL クエリを直接 SQL または JSON クエリに変換するデータゲートウェイではないということです。代わりに、既存の Spring テクノロジを活用でき、GraphQL スキーマと基礎となるデータモデル間の 1 対 1 のマッピングを想定していません。そのため、データモデルのクライアント主導の選択とサーバー側の変換が補完的なロールを果たすことができます。
理解を深めるために、データ層の複雑さを管理するための推奨されるアプローチとして、Spring Data がドメイン駆動 (DDD) 設計を推進していることを考慮してください。DDD では、集約の制約に従うことが重要です。部分的にロードされた集約は集約機能に制限を課す可能性があるため、定義上、集約は完全にロードされた場合にのみ有効です。
Spring Data では、集約をそのまま公開するか、GraphQL の結果として返す前にデータモデルに変換を適用するかを選択できます。前者を実行するだけで十分な場合もあります。デフォルトでは、Querydsl と例示による問い合わせの統合により、GraphQL の選択セットが、基になる Spring Data モジュールが選択を制限するために使用するプロパティパスのヒントに変わります。
それ以外の場合は、GraphQL スキーマに適応するために、基礎となるデータモデルを縮小または変換することが役立ちます。Spring Data は、インターフェースと DTO 射影を通じてこれをサポートします。
インターフェース射影は、データストアクエリの結果に応じて、プロパティが null である場合とそうでない場合がある場所を公開する固定のプロパティセットを定義します。インターフェース射影には 2 種類あり、どちらも基になるデータソースからどのプロパティを読み込むかを決定します。
閉じたインターフェースの射影は、集約オブジェクトを部分的に実体化できないが、プロパティのサブセットを公開したい場合に役立ちます。
オープンインターフェース射影は、Spring の
@Valueアノテーションと SpEL 式を活用して、連結、計算、静的関数のプロパティへの適用などの軽量データ変換を適用します。
DTO 射影は、コンストラクターまたは getter メソッドのいずれかに変換コードを配置できるため、より高いレベルのカスタマイズを提供します。
DTO 射影は、個々のプロパティが射影自体によって決定されるクエリから具体化されます。DTO 射影は、通常、完全な引数のコンストラクター (Java レコードなど) で使用されるため、必要なすべてのフィールド (または列) がデータベースクエリ結果の一部である場合にのみ構築できます。
スクロール
ページネーションで説明したように、GraphQL カーソル接続仕様は Connection、Edge、PageInfo スキーマ型によるページネーションのメカニズムを定義しますが、GraphQL Java は同等の Java 型表現を提供します。
Spring for GraphQL は、Spring Data ページネーション型 Window および Slice を透過的に適応させるための組み込み ConnectionAdapter 実装を提供します。次のように設定できます。
CursorStrategy<ScrollPosition> strategy = CursorStrategy.withEncoder(
new ScrollPositionCursorStrategy(),
CursorEncoder.base64()); (1)
GraphQLTypeVisitor visitor = ConnectionFieldTypeVisitor.create(List.of(
new WindowConnectionAdapter(strategy),
new SliceConnectionAdapter(strategy))); (2)
GraphQlSource.schemaResourceBuilder()
.schemaResources(..)
.typeDefinitionConfigurer(..)
.typeVisitors(List.of(visitor)); (3)| 1 | ScrollPosition を Base64 エンコードされたカーソルに変換する戦略を作成します。 |
| 2 | DataFetcher から返される Window および Slice を適応させるための型 ビジターを作成します。 |
| 3 | 型の訪問者を登録します。 |
リクエスト側では、コントローラーメソッドで ScrollSubrange メソッド引数を宣言して、前方または後方にページネーションできます。これが機能するには、CursorStrategy が ScrollPosition を Bean としてサポートすることを宣言する必要があります。
Boot スターターは CursorStrategy<ScrollPosition> Bean を宣言し、Spring Data がクラスパス上にある場合は上記のように ConnectionFieldTypeVisitor を登録します。
キーセットの位置
KeysetScrollPosition の場合、カーソルはキーセットから作成する必要があります。これは基本的にキーと値のペアからなる Map です。キーセットからカーソルを作成する方法を決定するには、ScrollPositionCursorStrategy を CursorStrategy<Map<String, Object>> と組み合わせて設定します。デフォルトでは、JsonKeysetCursorStrategy はキーセット Map を JSON に書き込みます。これは String、Boolean、Integer、Double などの単純な型には有効ですが、それ以外の型はターゲットの型情報がないと元の型に戻すことができません。Jackson ライブラリには、JSON に型情報を含めることができるデフォルトの型指定機能があります。これを安全に使用するには、許可される型のリストを指定する必要があります。
デフォルトでは、JsonKeysetCursorStrategy が CodecConfigurer なしで作成され、Jackson ライブラリがクラスパス上にある場合、JSON キーセットは Date、Calendar、UUID、Java Enums、Number、java.time の任意の型をサポートします。
アプリケーションは、カスタム Jackson エンコーダ / デコーダーペアを使用して独自の JsonKeysetCursorStrategy をインスタンス化することで、キーセットの JSON 直列化をさらに洗練させることができます。Spring Boot では、次のような EncodingCursorStrategy を提供するだけで十分です。
import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import java.util.UUID;
import tools.jackson.databind.DefaultTyping;
import tools.jackson.databind.cfg.DateTimeFeature;
import tools.jackson.databind.json.JsonMapper;
import tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
import tools.jackson.databind.jsontype.PolymorphicTypeValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.domain.ScrollPosition;
import org.springframework.graphql.data.pagination.CursorEncoder;
import org.springframework.graphql.data.pagination.CursorStrategy;
import org.springframework.graphql.data.pagination.EncodingCursorStrategy;
import org.springframework.graphql.data.query.JsonKeysetCursorStrategy;
import org.springframework.graphql.data.query.ScrollPositionCursorStrategy;
import org.springframework.http.codec.CodecConfigurer;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.http.codec.json.JacksonJsonDecoder;
import org.springframework.http.codec.json.JacksonJsonEncoder;
@Configuration
public class KeysetCursorConfiguration {
@Bean
// override the EncodingCursorStrategy bean in Spring Boot
public EncodingCursorStrategy<ScrollPosition> cursorStrategy() {
JsonKeysetCursorStrategy keysetCursorStrategy = keysetCursorStrategy();
ScrollPositionCursorStrategy cursorStrategy = new ScrollPositionCursorStrategy(keysetCursorStrategy);
return CursorStrategy.withEncoder(cursorStrategy, CursorEncoder.base64());
}
// create a cursor strategy with a custom CodecConfigurer
private JsonKeysetCursorStrategy keysetCursorStrategy() {
JsonMapper mapper = keysetJsonMapper();
CodecConfigurer codecConfigurer = keysetCodecConfigurer(mapper);
return new JsonKeysetCursorStrategy(codecConfigurer);
}
// use a custom JsonMapper for encoding/decoding JSON
private CodecConfigurer keysetCodecConfigurer(JsonMapper jsonMapper) {
CodecConfigurer configurer = ServerCodecConfigurer.create();
configurer.defaultCodecs().jacksonJsonDecoder(new JacksonJsonDecoder(jsonMapper));
configurer.defaultCodecs().jacksonJsonEncoder(new JacksonJsonEncoder(jsonMapper));
return configurer;
}
// create a custom JsonMapper
private JsonMapper keysetJsonMapper() {
// Configure which types should be allowed for serialization
// those should include all fields included in the keyset
PolymorphicTypeValidator validator = BasicPolymorphicTypeValidator.builder()
.allowIfBaseType(Map.class)
.allowIfSubType(Calendar.class)
.allowIfSubType(Date.class)
.allowIfSubType(UUID.class)
.allowIfSubType(Number.class)
.allowIfSubType(Enum.class)
.allowIfSubType("java.time.")
.build();
return JsonMapper.builder()
.activateDefaultTyping(validator, DefaultTyping.NON_FINAL)
// as of Jackson 3.0, dates are not written as timestamps by default
.enable(DateTimeFeature.WRITE_DATES_AS_TIMESTAMPS)
.build();
}
}ソート
Spring for GraphQL は、GraphQL 引数から Sort を作成するための SortStrategy を定義します。AbstractSortStrategy は、ソート方向とプロパティを抽出するための抽象メソッドを使用して契約を実装します。コントローラーメソッドの引数として Sort のサポートを有効にするには、SortStrategy Bean を宣言する必要があります。
トランザクション管理
データを扱う際には、ある時点で操作のアトミック性と分離性が重要になります。これらはどちらもトランザクションの特性です。GraphQL 自体はトランザクションのセマンティクスを定義していないため、トランザクションの処理方法はサーバーとアプリケーションが決定します。
GraphQL、特に GraphQL Java は、データの取得方法について特定の見解を持たないように設計されています。GraphQL の核となる特性は、クライアントがリクエストを主導することです。フィールドは元のソースとは独立して解決できるため、コンポジションが可能になります。フィールドセットを縮小することで、取得するデータ量が少なくなり、パフォーマンスが向上します。
トランザクション内で分散フィールド解決の概念を適用するのは適切ではありません。
トランザクションは作業単位をまとめるため、通常は単一のトランザクション内でオブジェクトグラフ全体(典型的なオブジェクトリレーショナルマッパーの動作)を取得します。これは、クライアントがクエリを駆動できるようにするという GraphQL のコア設計とは矛盾しています。
複数のデータフェッチャー間でトランザクションをオープンにしたままにして、各データフェッチャーがフラットオブジェクトのみをフェッチするようにすると、パフォーマンス面が緩和され、分離されたフィールド解決と一致しますが、必要以上に長い時間リソースを保持するトランザクションの実行時間が長くなる可能性があります。
一般的に、トランザクションは状態を変更するミューテーションに適用するのが最適であり、必ずしもデータを読み取るだけのクエリには適用できません。ただし、トランザクションによる読み取りが必要なユースケースもあります。
GraphQL は、単一のリクエスト内で複数のミューテーションをサポートするように設計されています。ユースケースに応じて、次のような対応が必要になる場合があります。
各ミューテーションを独自のトランザクション内で実行します。
一貫した状態を確保するために、いくつかの変更を単一のトランザクション内に保持します。
関係するすべてのミューテーションにわたって単一のトランザクションを実行します。
それぞれのアプローチには、若干異なるトランザクション管理戦略が必要です。
Spring Framework(JDBC など)または Spring Data を使用する場合、テンプレート API とリポジトリはデフォルトで(追加のインストルメンテーションなしで)、個々の操作に対して暗黙的なトランザクションを使用するため、リポジトリメソッドの呼び出しごとにトランザクションが開始され、コミットされます。これは、ほとんどのデータベースの通常の動作モードです。
次のセクションでは、GraphQL サーバーでトランザクションを管理するための 2 つの異なる戦略について説明します。
トランザクションコントローラーメソッド
トランザクションを管理する最も簡単な方法は、Spring のトランザクション管理を @MutationMapping コントローラーメソッド (またはその他の @SchemaMapping メソッド) と組み合わせて使用することです。例:
宣言的
プログラマティック
@Controller
public class AccountController {
@MutationMapping
@Transactional
public Account addAccount(@Argument AccountInput input) {
// ...
}
}
@Controller
public class AccountController {
private final TransactionOperations transactionOperations;
@MutationMapping
public Account addAccount(@Argument AccountInput input) {
return transactionOperations.execute(status -> {
// ...
});
}
}
トランザクションは、addAccount メソッドへの入力から戻りまで網羅します。トランザクションリソースへのすべての呼び出しは同一のトランザクションの一部であるため、アトミック性とミューテーションの独立性が確保されます。
これは推奨されるアプローチです。GraphQL サーバーインフラストラクチャをインストルメント化することなく、明確に定義されたエントリポイントを使用してトランザクション境界を完全に制御できます。
メソッド呼び出し後にトランザクションをクリーンアップすると、後続のデータ取得 (ネストされたフィールドなど) は、以下に概説されているように、トランザクションメソッド addAccount の一部ではなくなります。
@Controller
public class AccountController {
@MutationMapping
@Transactional
public Account addAccount(@Argument AccountInput input) { (1)
// ...
}
@SchemaMapping
@Transactional
public Person person(Account account) { (2)
... // fetching the person within a separate transaction
}
}| 1 | addAccount メソッドの呼び出しは、独自のトランザクション内で実行されます。 |
| 2 | person メソッドの呼び出しは、addAccount メソッドと関連付けられない独自の独立したトランザクションを作成します。これは、両方のメソッドが同じ GraphQL リクエストの一部として呼び出された場合に発生します。独立したトランザクションには、繰り返し不可能な読み取りや、addAcount メソッドの呼び出しと person メソッドの呼び出しの間でデータが変更された場合の不整合など、同じトランザクションの一部ではないことによるあらゆる欠点が伴います。 |
シンプルな設定を維持しながら、単一のトランザクションで複数のミューテーションを実行するには、必要なすべての入力を受け入れるミューテーションメソッドを設計することをお勧めします。このメソッドは複数のサービスメソッドを呼び出すことができ、それらすべてが同じトランザクションに参加することを保証します。
トランザクションインストルメンテーション
トランザクションインストルメンテーションの適用は、GraphQL リクエストの実行全体にわたってトランザクションを拡張するための、より高度なアプローチです。最初のデータフェッチャーが呼び出される前にトランザクションを開始することで、アプリケーションはすべてのデータフェッチャーが同じトランザクションに参加できることを保証できます。
サーバーをインストルメントする際は、ExecutionStrategy が DataFetcher 呼び出しをシリアルに実行し、すべての呼び出しが同じ Thread で実行されるようにする必要があります。これは必須です。同期トランザクション管理では、トランザクションへの参加を許可するために ThreadLocal の状態を使用します。データフェッチャーをシリアルに実行する AsyncSerialExecutionStrategy を出発点として検討することは、良い選択です。
トランザクションインストルメンテーションを実装するには、次の 2 つの一般的なオプションがあります。
GraphQL Java の
Instrumentation契約は、実行ライフサイクルの様々な段階でフックすることを可能にします。Instrumentation SPI は可観測性を念頭に置いて設計されていますが、同期リアクティブ、あるいはその他の非同期形式のデータフェッチャー呼び出しのいずれを使用していても、実行に依存しない拡張ポイントとして機能し、この点においてはあまり偏りがありません。ExecutionStrategyは実行を完全に制御し、失敗したトランザクションやトランザクションのクリーンアップ中に発生したエラーをクライアントに通知する様々な方法を提供します。また、カスタムディレクティブを実装するための優れたエントリポイントとしても機能します。カスタムディレクティブを使用すると、クライアントはディレクティブを通じてトランザクション属性を指定したり、スキーマ内のディレクティブを使用して特定のクエリやミューテーションのトランザクション境界を定義したりできます。
トランザクションを手動で管理する場合は、作業単位の完了後に、コミットまたはロールバック中のトランザクションを必ずクリーンアップしてください。ExceptionWhileDataFetching は、基盤となる Exception を取得するための便利な GraphQLError です。このエラーは、SimpleDataFetcherExceptionHandler の使用時に発生します。デフォルトでは、Spring GraphQL は、元の例外を公開しない内部の GraphQLError にフォールバックします。
トランザクションインストルメンテーションを適用すると、トランザクションへの参加を再考する機会が生まれます。すべての @SchemaMapping コントローラーメソッドは、ルート、ネストされたフィールド、ミューテーションの一部として呼び出されたかどうかに関係なく、トランザクションに参加します。トランザクションコントローラーメソッド(または呼び出しチェーン内のサービスメソッド)は、必要に応じて新しいトランザクションを開始するための伝播動作 REQUIRES_NEW などのトランザクション属性を宣言できます。