送信チャネルアダプター

JPA 送信チャネルアダプターを使用すると、リクエストチャネルを介してメッセージを受け入れることができます。ペイロードは、永続化されるエンティティとして使用するか、JPQL クエリのパラメーター式のヘッダーで使用できます。次のセクションでは、これらの操作を実行する方法について説明します。

エンティティクラスの使用

次の XML は、エンティティをデータベースに永続化するように送信チャネルアダプターを構成します。

<int-jpa:outbound-channel-adapter channel="entityTypeChannel"               (1)
    entity-class="org.springframework.integration.jpa.test.entity.Student"  (2)
    persist-mode="PERSIST"                                                  (3)
    entity-manager="em"/ >                                                  (4)
1 有効な JPA エンティティが JPA 発信チャネルアダプターに送信されるチャネル。
2 データベースに永続化するためにアダプターによって受け入れられるエンティティクラスの完全修飾名。アダプターは Spring Integration メッセージペイロードからエンティティクラスを自動的に決定できるため、ほとんどの場合、この属性は実際に省略できます。
3 アダプターによって実行される操作。有効な値は PERSISTMERGEDELETE です。デフォルト値は MERGE です。
4 使用する JPA エンティティマネージャー。

outbound-channel-adapter のこれらの 4 つの属性は、入力チャネルを介してエンティティを受け入れ、基になるデータソースからエンティティを PERSISTMERGEDELETE に処理するように構成します。

Spring Integration 3.0 以降、PERSIST または MERGE へのペイロードも java.lang.Iterable (標準 Javadoc) 型にすることができます。その場合、Iterable によって返される各オブジェクトはエンティティとして扱われ、基になる EntityManager を使用して永続化またはマージされます。イテレータから返された null 値は無視されます。
バージョン 5.5.4 以降、PersistMode.DELETE で構成された JpaExecutor を備えた JpaOutboundGateway は、Iterable ペイロードを受け入れて、提供されたエンティティに対してバッチ削除の永続操作を実行できます。

JPA クエリ言語の使用 (JPA QL)

前のセクションでは、エンティティを使用して PERSIST アクションを実行する方法を示しました。このセクションでは、JPA QL で送信チャネルアダプターを使用する方法を示します。

次の XML は、エンティティをデータベースに永続化するように送信チャネルアダプターを構成します。

<int-jpa:outbound-channel-adapter channel="jpaQlChannel"                                      (1)
  jpa-query="update Student s set s.firstName = :firstName where s.rollNumber = :rollNumber"  (2)
  entity-manager="em">                                                                        (3)
    <int-jpa:parameter name="firstName"  expression="payload['firstName']"/>                  (4)
    <int-jpa:parameter name="rollNumber" expression="payload['rollNumber']"/>
</int-jpa:outbound-channel-adapter>
1 メッセージが送信チャネルアダプターに送信される入力チャネル。
2 実行する JPA QL。このクエリには、parameter 要素を使用して評価されるパラメーターが含まれる場合があります。
3JPA 操作を実行するためにアダプターが使用するエンティティマネージャー。
4query 属性で指定された JPA QL のパラメーター名の値を定義するために使用される要素(パラメーターごとに 1 つ)。

parameter エレメントは、提供された JPA QL で指定された名前付きパラメーターに対応する name を持つ属性を受け入れます(前の例のポイント 2)。パラメーターの値は、静的にすることも、式を使用して導出することもできます。静的値と値を導出する式は、それぞれ value 属性と expression 属性を使用して指定されます。これらの属性は相互に排他的です。

value 属性が指定されている場合、オプションの type 属性を提供できます。この属性の値は、その値が value 属性で表されるクラスの完全修飾名です。デフォルトでは、型は java.lang.String であると想定されています。次の例は、JPA パラメーターを定義する方法を示しています。

<int-jpa:outbound-channel-adapter ...
>
    <int-jpa:parameter name="level" value="2" type="java.lang.Integer"/>
    <int-jpa:parameter name="name" expression="payload['name']"/>
</int-jpa:outbound-channel-adapter>

前の例が示すように、送信チャネルアダプター要素内で複数の parameter 要素を使用し、静的な値を持つ式などを使用して一部のパラメーターを定義できます。ただし、同じパラメーター名を複数回指定しないように注意してください。JPA クエリで指定された名前付きパラメーターごとに 1 つの parameter 要素を提供する必要があります。例: level と name の 2 つのパラメーターを指定します。level 属性は、型 java.lang.Integer の静的な値ですが、name 属性はメッセージのペイロードから派生しています。

select の指定は JPA QL に対して有効ですが、そうすることは意味がありません。送信チャネルアダプターは結果を返しません。一部の値を選択する場合は、代わりに送信ゲートウェイの使用を検討してください。

ネイティブクエリの使用

このセクションでは、ネイティブクエリを使用して JPA 発信チャネルアダプターで操作を実行する方法について説明します。ネイティブクエリの使用は、クエリがネイティブデータベースクエリであることを除いて、JPA QL の使用に似ています。ネイティブクエリを使用すると、データベースベンダーの独立性が失われ、JPA QL を使用することになります。

ネイティブクエリを使用して達成できることの 1 つは、データベースの挿入を実行することです。これは、JPA QL では不可能です。(挿入を実行するには、前述のように、JPA エンティティをチャネルアダプターに送信します)。以下は、ネイティブクエリを使用してテーブルに値を挿入する方法を示す小さな xml フラグメントです。

名前付きパラメーターは、ネイティブ SQL クエリと組み合わせて JPA プロバイダーによってサポートされない場合があります。Hibernate では正常に機能しますが、OpenJPA および EclipseLink はそれらをサポートしません。issues.apache.org/jira/browse/OPENJPA-111 (英語) を参照してください。JPA 2.0 仕様のセクション 3.8.12 には、「ネイティブクエリには、位置パラメーターのバインドと結果アイテムへの位置アクセスのみを移植可能に使用できる」と記載されています。

次の例では、ネイティブクエリで outbound-channel-adapter を構成します。

<int-jpa:outbound-channel-adapter channel="nativeQlChannel"
  native-query="insert into STUDENT_TABLE(FIRST_NAME,LAST_UPDATED) values (:lastName,:lastUpdated)"  (1)
  entity-manager="em">
    <int-jpa:parameter name="lastName" expression="payload['updatedLastName']"/>
    <int-jpa:parameter name="lastUpdated" expression="new java.util.Date()"/>
</int-jpa:outbound-channel-adapter>
1 この送信チャネルアダプターによって実行されるネイティブクエリ。

他の属性(channel や entity-manager など)および parameter 要素は、JPA QL の場合と同じセマンティクスを持っていることに注意してください。

名前付きクエリの使用

名前付きクエリの使用は、JPA QL またはネイティブクエリの使用に似ていますが、クエリではなく名前付きクエリを指定する点が異なります。まず、JPA という名前のクエリを定義する方法について説明します。次に、名前付きクエリで機能する送信チャネルアダプターを宣言する方法について説明します。Student というエンティティがある場合、Student クラスのアノテーションを使用して、2 つの名前付きクエリ selectStudent と updateStudent を定義できます。次の例は、その方法を示しています。

@Entity
@Table(name="Student")
@NamedQueries({
    @NamedQuery(name="selectStudent",
        query="select s from Student s where s.lastName = 'Last One'"),
    @NamedQuery(name="updateStudent",
        query="update Student s set s.lastName = :lastName,
               lastUpdated = :lastUpdated where s.id in (select max(a.id) from Student a)")
})
public class Student {

...
}

または、次の例に示すように、orm.xml を使用して名前付きクエリを定義できます。

<entity-mappings ...>
    ...
    <named-query name="selectStudent">
        <query>select s from Student s where s.lastName = 'Last One'</query>
    </named-query>
</entity-mappings>

アノテーションまたは orm.xml を使用して名前付きクエリを定義する方法を示したため、次の例に示すように、名前付きクエリを使用して outbound-channel-adapter を定義する小さな XML フラグメントを示します。

<int-jpa:outbound-channel-adapter channel="namedQueryChannel"
            named-query="updateStudent"	 (1)
            entity-manager="em">
        <int-jpa:parameter name="lastName" expression="payload['updatedLastName']"/>
        <int-jpa:parameter name="lastUpdated" expression="new java.util.Date()"/>
</int-jpa:outbound-channel-adapter>
1 チャネル経由でメッセージを受信したときにアダプターに実行させる名前付きクエリ。

構成パラメーターのリファレンス

次のリストは、送信チャネルアダプターに設定できるすべての属性を示しています。

<int-jpa:outbound-channel-adapter
  auto-startup="true"  (1)
  channel=""  (2)
  entity-class=""  (3)
  entity-manager=""  (4)
  entity-manager-factory=""  (5)
  id=""
  jpa-operations=""  (6)
  jpa-query=""  (7)
  named-query=""  (8)
  native-query=""  (9)
  order=""  (10)
  parameter-source-factory=""   (11)
  persist-mode="MERGE"   (12)
  flush="true"   (13)
  flush-size="10"   (14)
  clear-on-flush="true"   (15)
  use-payload-as-parameter-source="true"   (16)
	<int:poller/>
	<int-jpa:transactional/>    (17)
	<int-jpa:parameter/>    (18)
</int-jpa:outbound-channel-adapter>
1 このコンポーネントがアプリケーションコンテキストの起動中に起動する必要があるかどうかを示すライフサイクル属性。デフォルトは true です。オプション。
2 送信アダプターが目的の操作を実行するためのメッセージを受信するチャネル。
3JPA オペレーションのエンティティクラスの完全修飾名。entity-classquerynamed-query 属性は相互に排他的です。オプション。
4JPA 操作の実行に使用される jakarta.persistence.EntityManager のインスタンス。オプション。
5JPA 操作を実行する jakarta.persistence.EntityManager のインスタンスを取得するために使用される jakarta.persistence.EntityManagerFactory のインスタンス。オプション。
6JPA 操作の実行に使用される org.springframework.integration.jpa.core.JpaOperations の実装。独自の実装を提供するのではなく、デフォルトの org.springframework.integration.jpa.core.DefaultJpaOperations 実装を使用することをお勧めします。entity-managerentity-manager-factoryjpa-operations 属性のいずれかを使用できます。オプション。
7 このアダプターによって実行される JPA QL。オプション。
8 このアダプターで実行する必要がある名前付きクエリ。オプション。
9 このアダプターによって実行されるネイティブクエリ。jpa-querynamed-querynative-query 属性のいずれかを使用できます。オプション。
10 複数のコンシューマーが登録されている場合のこのコンシューマーの順序。これにより、負荷分散とフェイルオーバーが管理されます。デフォルトは Ordered.LOWEST_PRECEDENCE です。オプション。
11o.s.i.jpa.support.parametersource.ParameterSource のインスタンスを取得するために使用される o.s.i.jpa.support.parametersource.ParameterSourceFactory のインスタンス。これは、クエリ内のパラメーターの値を解決するために使用されます。JPA エンティティを使用して操作を実行する場合は無視されます。parameter サブ要素は parameter-source-factory 属性と相互に排他的であり、提供された ParameterSourceFactory で構成する必要があります。オプション。
12 次のいずれかを受け入れます: PERSISTMERGEDELETE。アダプターが実行する必要のある操作を示します。JPA 操作にエンティティを使用する場合にのみ関連します。JPA QL、名前付きクエリ、ネイティブクエリを提供する場合は無視されます。デフォルトは MERGE です。オプション。Spring Integration 3.0 の時点で、永続化またはマージするペイロードも java.lang.Iterable (標準 Javadoc) (英語) 型にすることができます。その場合、Iterable によって返される各オブジェクトはエンティティとして扱われ、基になる EntityManager を使用して永続化またはマージされます。イテレータによって返される NULL 値は無視されます。
13 永続化、マージ、削除操作の直後に永続化コンテキストをフラッシュし、EntityManager の flushMode に依存したくない場合は、この値を true に設定します。デフォルトは false です。flush-size 属性を指定しなかった場合にのみ適用されます。この属性が true に設定されている場合、他の値が構成されていなければ、flush-size は暗黙的に 1 に設定されます。
14 永続化、マージ、削除操作の直後に永続化コンテキストをフラッシュし、flushMode または EntityManager に依存したくない場合は、この属性を "0" より大きい値に設定します。デフォルト値は 0 に設定されており、これは「フラッシュなし」を意味します。この属性は、Iterable ペイロードを含むメッセージを対象としています。たとえば、flush-size が 3 に設定されている場合、エンティティが 3 つおきに entityManager.flush() が呼び出されます。さらに、ループ全体の後に entityManager.flush() がもう一度呼び出されます。'flush-size' 属性 に "0" より大きい値を指定した場合、flush 属性を構成する必要はありません。
15 各フラッシュ操作の直後に永続コンテキストをクリアする場合は、この値を "true" に設定します。属性の値が適用されるのは、flush 属性が true に設定されている場合、または flush-size 属性が 0 より大きい値に設定されている場合のみです。
16true に設定すると、メッセージのペイロードがパラメーターのソースとして使用されます。ただし、false に設定されている場合、Message 全体がパラメーターのソースとして使用可能です。オプション。
17JPA アダプターが使用するトランザクション管理属性とトランザクションマネージャーへの参照を定義します。オプション。
181 つ以上の parameter 属性 — クエリで使用される各パラメーターに 1 つ。値または式が評価され、パラメーターの値が計算されます。オプション。

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 = "jpaPersistChannel")
       @Transactional
       void persistStudent(StudentDomain payload);

    }

    @Bean
    public JpaExecutor jpaExecutor() {
        JpaExecutor executor = new JpaExecutor(this.entityManagerFactory);
        jpaExecutor.setEntityClass(StudentDomain.class);
        jpaExecutor.setPersistMode(PersistMode.PERSIST);
        return executor;
    }

    @Bean
    @ServiceActivator(channel = "jpaPersistChannel")
    public MessageHandler jpaOutbound() {
        JpaOutboundGateway adapter = new JpaOutboundGateway(jpaExecutor());
        adapter.setProducesReply(false);
        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 outboundAdapterFlow() {
        return f -> f
                .handle(Jpa.outboundAdapter(this.entityManagerFactory)
                                .entityClass(StudentDomain.class)
                                .persistMode(PersistMode.PERSIST),
                        e -> e.transactional());
    }

}