出站通道适配器

JPA 出站通道适配器可讓您透過請求通道接收訊息。 Payload 可以用作要持久化的實體,或與 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 訊息 Payload 自動判斷實體類別。
3 适配器要執行的操作。 有效值為 PERSISTMERGEDELETE。 預設值為 MERGE
4 要使用的 JPA 實體管理器。

outbound-channel-adapter 的這四個屬性將其配置為接受輸入通道上的實體,並處理它們以 PERSISTMERGEDELETE 來自底層資料來源的實體。

從 Spring Integration 3.0 開始,PERSISTMERGE 的 Payload 也可以是 java.lang.Iterable 型別。 在這種情況下,Iterable 傳回的每個物件都會被視為實體,並使用底層 EntityManager 持久化或合併。 迭代器傳回的 Null 值會被忽略。
從 5.5.4 版本開始,JpaOutboundGateway 與配置了 PersistMode.DELETEJpaExecutor 可以接受 Iterable Payload,以針對提供的實體執行批次移除持久性操作。

使用 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 元素評估的參數。
3 适配器用來執行 JPA 操作的實體管理器。
4 用於定義 query 屬性中指定的 JPA QL 的參數名稱值的元素(每個參數一個)。

parameter 元素接受一個屬性,其 name 對應於提供的 JPA QL 中指定的具名參數(前述範例中的第 2 點)。 參數的值可以是靜態的,也可以透過運算式衍生而來。 靜態值和衍生值的運算式分別使用 valueexpression 屬性指定。 這些屬性是互斥的。

如果指定了 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 查詢中指定的每個具名參數提供一個 parameter 元素。 例如,我們指定兩個參數:levelnamelevel 屬性是 java.lang.Integer 型別的靜態值,而 name 屬性則衍生自訊息的 Payload。

雖然指定 select 對於 JPA QL 而言是有效的,但這樣做沒有意義。 出站通道适配器不會傳回任何結果。 如果您想要選取一些值,請考慮改用出站閘道器。

使用原生查詢

本節說明如何使用原生查詢透過 JPA 出站通道适配器執行操作。 使用原生查詢與使用 JPA QL 類似,不同之處在於查詢是原生資料庫查詢。 透過使用原生查詢,我們失去了使用 JPA QL 所獲得的資料庫供應商獨立性。

我們可以透過使用原生查詢實現的一件事是執行資料庫插入,這在使用 JPA QL 時是不可能的。 (若要執行插入,我們會將 JPA 實體傳送至通道适配器,如先前所述)。 以下是一個小型 XML 片段,示範如何使用原生查詢在表格中插入值。

您的 JPA 提供者可能不支援具名參數與原生 SQL 查詢結合使用。 雖然它們在 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 此出站通道适配器執行的原生查詢。

請注意,其他屬性(例如 channelentity-manager)和 parameter 元素具有與 JPA QL 相同的語意。

使用具名查詢

使用具名查詢與使用 JPA QL原生查詢 類似,不同之處在於我們指定具名查詢而不是查詢。 首先,我們介紹如何定義 JPA 具名查詢。 然後,我們介紹如何宣告出站通道适配器以使用具名查詢。 如果我們有一個名為 Student 的實體,我們可以使用 Student 類別上的註解來定義兩個具名查詢:selectStudentupdateStudent。 以下範例示範如何執行此操作

@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 定義具名查詢,我們現在展示一個小型 XML 片段,透過使用具名查詢來定義 outbound-channel-adapter,如下列範例所示

<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 出站适配器從中接收訊息以執行所需操作的通道。
3 JPA 操作的實體類別完整名稱。 entity-classquerynamed-query 屬性是互斥的。 選用。
4 用於執行 JPA 操作的 jakarta.persistence.EntityManager 實例。 選用。
5 用於取得 jakarta.persistence.EntityManager 實例的 jakarta.persistence.EntityManagerFactory 實例,該實例執行 JPA 操作。 選用。
6 org.springframework.integration.jpa.core.JpaOperations 的實作,用於執行 JPA 操作。 我們建議不要提供您自己的實作,而是使用預設的 org.springframework.integration.jpa.core.DefaultJpaOperations 實作。 您可以使用 entity-managerentity-manager-factoryjpa-operations 屬性中的任何一個。 選用。
7 此适配器要執行的 JPA QL。 選用。
8 此适配器需要執行的具名查詢。 選用。
9 此适配器要執行的原生查詢。 您可以使用 jpa-querynamed-querynative-query 屬性中的任何一個。 選用。
10 當註冊多個消費者時,此消費者的順序,從而管理負載平衡和容錯移轉。 預設值為 Ordered.LOWEST_PRECEDENCE。 選用。
11 o.s.i.jpa.support.parametersource.ParameterSourceFactory 的實例,用於取得 o.s.i.jpa.support.parametersource.ParameterSource 的實例,該實例用於解析查詢中參數的值。 如果您透過使用 JPA 實體執行操作,則會忽略此項。 parameter 子元素與 parameter-source-factory 屬性互斥,並且必須在提供的 ParameterSourceFactory 上配置。 選用。
12 接受下列其中一項:PERSISTMERGEDELETE。 指示适配器需要執行的操作。 僅當您將實體用於 JPA 操作時才相關。 如果您提供 JPA QL、具名查詢或原生查詢,則會忽略此項。 預設值為 MERGE。 選用。 從 Spring Integration 3.0 開始,持久化或合併的 Payload 也可以是 java.lang.Iterable 型別。 在這種情況下,Iterable 傳回的每個物件都會被視為實體,並使用底層 EntityManager 持久化或合併。 迭代器傳回的 Null 值會被忽略。
13 如果您希望在持久化、合併或刪除操作後立即刷新持久性內容,並且不希望依賴 EntityManagerflushMode,請將此值設定為 true。 預設值為 false。 僅當您未指定 flush-size 屬性時才適用。 如果此屬性設定為 true,則 flush-size 會隱含地設定為 1(如果未配置其他值)。
14 如果您希望在持久化、合併或刪除操作後立即刷新持久性內容,並且不希望依賴 EntityManagerflushMode,請將此屬性設定為大於 '0' 的值。 預設值設定為 0,表示「不刷新」。 此屬性適用於具有 Iterable Payload 的訊息。 例如,如果 flush-size 設定為 3,則每第三個實體後會呼叫 entityManager.flush()。 此外,在整個迴圈之後,會再次呼叫 entityManager.flush()。 如果指定 'flush-size' 屬性的值大於 '0',則您不需要配置 flush 屬性。
15 如果您希望在每次刷新操作後立即清除持久性內容,請將此值設定為 'true'。 僅當 flush 屬性設定為 trueflush-size 屬性設定為大於 0 的值時,才會套用此屬性的值。
16 如果設定為 true,則訊息的 Payload 會用作參數的來源。 但是,如果設定為 false,則整個 Message 都可用作參數的來源。 選用。
17 定義交易管理屬性和 JPA 适配器要使用的交易管理器參考。 選用。
18 一個或多個 parameter 屬性 — 查詢中使用的每個參數一個。 評估值或運算式以計算參數的值。 選用。

使用 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());
    }

}