出站通道介面卡

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 的這四個屬性配置它從輸入通道接收實體,並處理這些實體,以從底層資料來源中進行 PERSISTMERGEDELETE 操作。

從 Spring Integration 3.0 開始,用於 PERSISTMERGE 的載荷也可以是 java.lang.Iterable 型別。在這種情況下,Iterable 返回的每個物件都被視為一個實體,並使用底層的 EntityManager 進行持久化或合併。迭代器返回的 null 值將被忽略。
從版本 5.5.4 開始,配置了 PersistMode.DELETEJpaExecutorJpaOutboundGateway 可以接受 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 元素求值的引數。
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 屬性則從訊息的載荷派生。

雖然在 JPA QL 中指定 select 是有效的,但這樣做沒有意義。出站通道介面卡不返回任何結果。如果你想選擇某些值,請考慮使用出站閘道器。

使用原生查詢

本節介紹瞭如何使用原生查詢與 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 節指出:“原生查詢中可移植地使用的只有位置引數繫結和對結果項的位置訪問。”

以下示例使用原生查詢配置了出站通道介面卡

<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 開始,用於持久化或合併的載荷也可以是 java.lang.Iterable 型別。在這種情況下,Iterable 返回的每個物件都被視為一個實體,並使用底層的 EntityManager 進行持久化或合併。迭代器返回的 null 值將被忽略。
13 如果要立即在持久化、合併或刪除操作後重新整理持久化上下文,並且不想依賴於 EntityManagerflushMode,則將此值設定為 true。預設為 false。僅在你未指定 flush-size 屬性時適用。如果此屬性設定為 true,則在未配置其他值的情況下,flush-size 會被隱式設定為 1
14 如果你想在持久化、合併或刪除操作後立即重新整理持久化上下文,並且不想依賴於 EntityManagerflushMode,則將此屬性設定為大於 '0' 的值。預設值設定為 0,表示“不重新整理”。此屬性主要針對帶有 Iterable 載荷的訊息。例如,如果 flush-size 設定為 3,則每隔三個實體就會呼叫 entityManager.flush()。此外,在整個迴圈結束後會再次呼叫 entityManager.flush()。如果指定了大於 '0' 的 'flush-size' 屬性值,則無需配置 flush 屬性。
15 如果在每次重新整理操作後立即清除持久化上下文,則將此值設定為 'true'。此屬性值僅在 flush 屬性設定為 trueflush-size 屬性設定為大於 0 的值時適用。
16 如果設定為 true,訊息的載荷將用作引數源。如果設定為 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());
    }

}