送信ゲートウェイ

JPA 受信チャネルアダプターを使用すると、データベースをポーリングして 1 つ以上の JPA エンティティを取得できます。その結果、取得したデータを使用して、取得したデータをメッセージペイロードとして使用する Spring Integration フローを開始します。

さらに、フローの最後で JPA 送信チャネルアダプターを使用してデータを永続化し、本質的に永続化操作の最後でフローを停止することができます。

ただし、フローの途中で JPA 永続化操作を実行するにはどうすればよいですか? 例: Spring Integration メッセージフローで処理しているビジネスデータがあり、それを保持したいが、さらに下流で他のコンポーネントを使用する必要がある場合があります。または、ポーラーを使用してデータベースをポーリングする代わりに、JPQL クエリを実行し、データをアクティブに取得する必要があります。その後、データはフロー内の後続のコンポーネントで処理されます。

これが、JPA 送信ゲートウェイの出番です。データを取得するだけでなく、データを永続化する機能を提供します。これらの使用を容易にするために、Spring Integration は 2 種類の JPA 送信ゲートウェイを提供します。

  • 送信ゲートウェイの更新

  • 送信ゲートウェイの取得

送信ゲートウェイを使用して、データベース内の一部のレコードを保存、更新、削除するアクションを実行する場合は常に、更新する送信ゲートウェイを使用する必要があります。たとえば、entity を使用して永続化する場合、結果としてマージおよび永続化されたエンティティが返されます。その他の場合、影響を受ける(更新または削除された)レコードの数が代わりに返されます。

データベースからデータを取得(選択)するときは、取得送信ゲートウェイを使用します。送信ゲートウェイを取得すると、JPQL、名前付きクエリ(ネイティブまたは JPQL ベース)、またはネイティブクエリ(SQL)を使用してデータを選択し、結果を取得できます。

更新送信ゲートウェイは、JPA 操作の実行後に結果をゲートウェイの応答チャネルに送信することを除いて、関数には送信チャネルアダプターに似ています。

送信ゲートウェイの取得は、受信チャネルアダプターに似ています。

一般的な概念のほとんどはそこで説明されているため、最初にこの章で前述した送信チャネルアダプターセクションと受信チャネルアダプターセクションを読むことをお勧めします。

この類似性は、共通の機能を可能な限り統合するために主要な JpaExecutor クラスを使用する主な要因でした。

すべての JPA 送信ゲートウェイに共通であり、outbound-channel-adapter と同様、さまざまな JPA 操作の実行に使用できます。

  • エンティティクラス

  • JPA クエリ言語 (JPQL)

  • ネイティブクエリ

  • 名前付きクエリ

構成例については、JPA 送信ゲートウェイのサンプルを参照してください。

共通の構成パラメーター

JPA 送信ゲートウェイは、常に Spring Integration Message に入力としてアクセスできます。その結果、次のパラメーターが使用可能になります。

parameter-source-factory

o.s.i.jpa.support.parametersource.ParameterSource のインスタンスを取得するために使用される o.s.i.jpa.support.parametersource.ParameterSourceFactory のインスタンス。ParameterSource は、クエリで提供されるパラメーターの値を解決するために使用されます。JPA エンティティを使用して操作を実行する場合、parameter-source-factory 属性は無視されます。parameter サブエレメントは parameter-source-factory と相互に排他的であり、提供された ParameterSourceFactory で構成する必要があります。オプション。

use-payload-as-parameter-source

true に設定すると、Message のペイロードがパラメーターのソースとして使用されます。false に設定されている場合、Message 全体がパラメーターのソースとして使用可能です。JPA パラメーターが渡されない場合、このプロパティはデフォルトで true になります。つまり、デフォルトの BeanPropertyParameterSourceFactory を使用すると、ペイロードの Bean プロパティが JPA クエリのパラメーター値のソースとして使用されます。ただし、JPA パラメーターが渡された場合、このプロパティはデフォルトで false に評価されます。その理由は、JPA パラメーターを使用すると SpEL 式を提供できるからです。ヘッダーを含む Message 全体にアクセスできることは非常に有益です。オプション。

送信ゲートウェイの更新

次のリストは、updateing-outbound-gateway に設定できるすべての属性を示し、主要な属性について説明しています。

<int-jpa:updating-outbound-gateway request-channel=""  (1)
    auto-startup="true"
    entity-class=""
    entity-manager=""
    entity-manager-factory=""
    id=""
    jpa-operations=""
    jpa-query=""
    named-query=""
    native-query=""
    order=""
    parameter-source-factory=""
    persist-mode="MERGE"
    reply-channel=""  (2)
    reply-timeout=""  (3)
    use-payload-as-parameter-source="true">

    <int:poller/>
    <int-jpa:transactional/>

    <int-jpa:parameter name="" type="" value=""/>
    <int-jpa:parameter name="" expression=""/>
</int-jpa:updating-outbound-gateway>
1 送信ゲートウェイが目的の操作を実行するためのメッセージを受信するチャネル。この属性は、outbound-channel-adapter の channel 属性に似ています。オプション。
2 ゲートウェイが必要な JPA 操作を実行した後にレスポンスを送信するチャネル。この属性が定義されていない場合、リクエストメッセージには replyChannel ヘッダーが必要です。オプション。
3 ゲートウェイが結果を応答チャネルに送信するまで待機する時間を指定します。応答チャネル自体が送信操作をブロックする可能性がある場合にのみ適用されます (たとえば、現在いっぱいである制限された QueueChannel )。値はミリ秒単位で指定します。オプション。

残りの属性については、この章で前述しています。構成パラメーターのリファレンスおよび構成パラメーターのリファレンスを参照してください。

Java 構成を使用した構成

次の Spring Boot アプリケーションは、Java で送信アダプターを構成する方法の例を示しています。

@SpringBootApplication
@EntityScan(basePackageClasses = StudentDomain.class)
@IntegrationComponentScan
public class JpaJavaApplication {

    public static void main(String[] args) {
        new SpringApplicationBuilder(JpaJavaApplication.class)
            .web(false)
            .run(args);
    }

    @Autowired
    private EntityManagerFactory entityManagerFactory;

    @MessagingGateway
    interface JpaGateway {

       @Gateway(requestChannel = "jpaUpdateChannel")
       @Transactional
       void updateStudent(StudentDomain payload);

    }

    @Bean
    @ServiceActivator(channel = "jpaUpdateChannel")
    public MessageHandler jpaOutbound() {
        JpaOutboundGateway adapter =
               new JpaOutboundGateway(new JpaExecutor(this.entityManagerFactory));
        adapter.setOutputChannelName("updateResults");
        return adapter;
    }

}

Java DSL を使用した構成

次の Spring Boot アプリケーションは、Java DSL を使用して送信アダプターを構成する方法の例を示しています。

@SpringBootApplication
@EntityScan(basePackageClasses = StudentDomain.class)
public class JpaJavaApplication {

    public static void main(String[] args) {
        new SpringApplicationBuilder(JpaJavaApplication.class)
            .web(false)
            .run(args);
    }

    @Autowired
    private EntityManagerFactory entityManagerFactory;

    @Bean
    public IntegrationFlow updatingGatewayFlow() {
        return f -> f
                .handle(Jpa.updatingGateway(this.entityManagerFactory),
                        e -> e.transactional(true))
                .channel(c -> c.queue("updateResults"));
    }

}

送信ゲートウェイの取得

次の例は、取得する送信ゲートウェイを構成する方法を示しています。

  • Java DSL

  • Kotlin DSL

  • Java

  • XML

@SpringBootApplication
@EntityScan(basePackageClasses = StudentDomain.class)
public class JpaJavaApplication {

    public static void main(String[] args) {
        new SpringApplicationBuilder(JpaJavaApplication.class)
            .web(false)
            .run(args);
    }

    @Autowired
    private EntityManagerFactory entityManagerFactory;

    @Bean
    public IntegrationFlow retrievingGatewayFlow() {
        return f -> f
                .handle(Jpa.retrievingGateway(this.entityManagerFactory)
                       .jpaQuery("from Student s where s.id = :id")
                       .expectSingleResult(true)
                       .parameterExpression("id", "payload"))
                .channel(c -> c.queue("retrieveResults"));
    }

}
@Bean
fun retrievingGatewayFlow() =
    integrationFlow {
        handle(Jpa.retrievingGateway(this.entityManagerFactory)
                .jpaQuery("from Student s where s.id = :id")
                .expectSingleResult(true)
                .parameterExpression("id", "payload"))
        channel { queue("retrieveResults") }
    }
@SpringBootApplication
@EntityScan(basePackageClasses = StudentDomain.class)
public class JpaJavaApplication {

    public static void main(String[] args) {
        new SpringApplicationBuilder(JpaJavaApplication.class)
            .web(false)
            .run(args);
    }

    @Autowired
    private EntityManagerFactory entityManagerFactory;


    @Bean
    public JpaExecutor jpaExecutor() {
        JpaExecutor executor = new JpaExecutor(this.entityManagerFactory);
        jpaExecutor.setJpaQuery("from Student s where s.id = :id");
        executor.setJpaParameters(Collections.singletonList(new JpaParameter("id", null, "payload")));
        jpaExecutor.setExpectSingleResult(true);
        return executor;
    }

    @Bean
    @ServiceActivator(channel = "jpaRetrievingChannel")
    public MessageHandler jpaOutbound() {
        JpaOutboundGateway adapter = new JpaOutboundGateway(jpaExecutor());
        adapter.setOutputChannelName("retrieveResults");
        adapter.setGatewayType(OutboundGatewayType.RETRIEVING);
        return adapter;
    }

}
<int-jpa:retrieving-outbound-gateway request-channel=""
    auto-startup="true"
    delete-after-poll="false"
    delete-in-batch="false"
    entity-class=""
    id-expression=""              (1)
    entity-manager=""
    entity-manager-factory=""
    expect-single-result="false"  (2)
    id=""
    jpa-operations=""
    jpa-query=""
    max-results=""                (3)
    max-results-expression=""     (4)
    first-result=""               (5)
    first-result-expression=""    (6)
    named-query=""
    native-query=""
    order=""
    parameter-source-factory=""
    reply-channel=""
    reply-timeout=""
    use-payload-as-parameter-source="true">
    <int:poller></int:poller>
    <int-jpa:transactional/>

    <int-jpa:parameter name="" type="" value=""/>
    <int-jpa:parameter name="" expression=""/>
</int-jpa:retrieving-outbound-gateway>
1(Spring Integration 4.0 以降)評価コンテキストのルートオブジェクトとしての requestMessage に対する EntityManager.find(Class entityClass, Object primaryKey) メソッドの primaryKey 値を決定する SpEL 式。entityClass 引数は、存在する場合、entity-class 属性から決定されます。それ以外の場合は、payload クラスから決定されます。id-expression を使用する場合、他のすべての属性は許可されません。オプション。
2 選択操作が単一の結果を返すと予想されるか、結果の List を返すと予想されるかを示すブールフラグ。このフラグが true に設定されている場合、単一のエンティティがメッセージのペイロードとして送信されます。複数のエンティティが返された場合は、例外がスローされます。false の場合、エンティティの List がメッセージのペイロードとして送信されます。デフォルトは false です。オプション。
3 この非ゼロ、非負の整数値は、選択操作の実行時に、指定された行数を超える行を選択しないようアダプターに指示します。デフォルトでは、この属性が設定されていない場合、指定されたクエリによってすべての可能なレコードが選択されます。この属性は max-results-expression と相互に排他的です。オプション。
4 結果セット内の結果の最大数を見つけるために使用できる式。max-results と相互に排他的です。オプション。
5 この非ゼロ、非負の整数値は、結果を取得する最初のレコードをアダプターに伝えます。この属性は first-result-expression と相互に排他的です。バージョン 3.0 はこの属性を導入しました。オプション。
6 この式はメッセージに対して評価され、結果セットの最初のレコードの位置を見つけます。この属性は、first-result と相互に排他的です。バージョン 3.0 はこの属性を導入しました。オプション。

取得時にエンティティを削除することを選択し、エンティティのコレクションを取得した場合、既定では、エンティティはエンティティごとに削除されます。これにより、パフォーマンスの問題が発生する可能性があります。

または、属性 deleteInBatch を true に設定して、バッチ削除を実行できます。ただし、そうすることの制限は、カスケード削除がサポートされないことです。

JSR 317: Java ™ Persistence 2.0 の章では、4.10 の「一括更新および削除操作」で次のように述べています。

“削除操作は、指定されたクラスとそのサブクラスのエンティティにのみ適用されます。関連するエンティティにはカスケードしません。”

詳細については、JSR 317: Java ™ Persistence 2.0 (英語) を参照してください。

バージョン 6.0 以降、クエリによってエンティティが返されない場合、Jpa.retrievingGateway() は空のリスト結果を返します。以前は、requiresReply に応じて、フローを終了するか、例外をスローして null が返されていました。または、以前の動作に戻すには、ゲートウェイの後に filter を追加して、空のリストを除外します。空のリストの処理がダウンストリームロジックの一部であるアプリケーションでは、追加の構成が必要です。空リストの処理オプションについては、スプリッタ廃棄チャネルを参照してください。

JPA 送信ゲートウェイのサンプル

このセクションには、送信ゲートウェイの更新と送信ゲートウェイの取得のさまざまな使用例が含まれています。

エンティティクラスを使用して更新する

次の例では、org.springframework.integration.jpa.test.entity.Student エンティティクラスを JPA 定義パラメーターとして使用することにより、送信ゲートウェイの更新が永続化されます。

<int-jpa:updating-outbound-gateway request-channel="entityRequestChannel"  (1)
    reply-channel="entityResponseChannel"  (2)
    entity-class="org.springframework.integration.jpa.test.entity.Student"
    entity-manager="em"/>
1 これは、送信ゲートウェイのリクエストチャネルです。outbound-channel-adapter の channel 属性に似ています。
2 これは、ゲートウェイが送信アダプターと異なる場所です。これは、JPA 操作からの応答が受信されるチャネルです。ただし、受信した応答に関心がなく、操作のみを実行する場合は、JPA outbound-channel-adapter を使用するのが適切な選択です。エンティティクラスを使用するこの例では、応答は JPA 操作の結果として作成またはマージされたエンティティオブジェクトです。

JPQL を使用した更新

次の例では、Java Persistence Query Language(JPQL)を使用してエンティティを更新します。これは、更新送信ゲートウェイの使用を義務付けています。

<int-jpa:updating-outbound-gateway request-channel="jpaqlRequestChannel"
  reply-channel="jpaqlResponseChannel"
  jpa-query="update Student s set s.lastName = :lastName where s.rollNumber = :rollNumber"  (1)
  entity-manager="em">
    <int-jpa:parameter name="lastName" expression="payload"/>
    <int-jpa:parameter name="rollNumber" expression="headers['rollNumber']"/>
</int-jpa:updating-outbound-gateway>
1 ゲートウェイが実行する JPQL クエリ。送信ゲートウェイの更新を使用したため、update および delete JPQL クエリのみが実用的な選択になります。

long 値を持つ rollNumber というヘッダーも含む String ペイロードを持つメッセージを送信すると、指定されたロール番号を持つ生徒の姓がメッセージペイロードの値に更新されます。更新ゲートウェイを使用する場合、戻り値は常に整数値であり、JPA QL の実行によって影響を受けるレコードの数を示します。

JPQL を使用したエンティティの取得

次の例では、送信ゲートウェイと JPQL の取得を使用して、データベースから 1 つ以上のエンティティを取得(選択)します。

<int-jpa:retrieving-outbound-gateway request-channel="retrievingGatewayReqChannel"
    reply-channel="retrievingGatewayReplyChannel"
    jpa-query="select s from Student s where s.firstName = :firstName and s.lastName = :lastName"
    entity-manager="em">
    <int-jpa:parameter name="firstName" expression="payload"/>
    <int-jpa:parameter name="lastName" expression="headers['lastName']"/>
</int-jpa:outbound-gateway>

id-expression を使用してエンティティを取得する

次の例では、id-expression で送信ゲートウェイを取得して、データベースから唯一のエンティティを取得(検索)します。primaryKey は id-expression 評価の結果です。entityClass は、メッセージ payload のクラスです。

<int-jpa:retrieving-outbound-gateway
	request-channel="retrievingGatewayReqChannel"
    reply-channel="retrievingGatewayReplyChannel"
    id-expression="payload.id"
    entity-manager="em"/>

名前付きクエリを使用して更新する

名前付きクエリを使用することは、基本的に JPQL クエリを直接使用することと同じです。違いは、次の例に示すように、代わりに named-query 属性が使用されることです。

<int-jpa:updating-outbound-gateway request-channel="namedQueryRequestChannel"
    reply-channel="namedQueryResponseChannel"
    named-query="updateStudentByRollNumber"
    entity-manager="em">
    <int-jpa:parameter name="lastName" expression="payload"/>
    <int-jpa:parameter name="rollNumber" expression="headers['rollNumber']"/>
</int-jpa:outbound-gateway>
Spring Integration の JPA アダプターを使用する完全なサンプルアプリケーションはこちらにあります [GitHub] (英語)