出站閘道器
JPA 入站通道介面卡允許您輪詢資料庫以檢索一個或多個 JPA 實體。檢索到的資料隨後用於啟動一個 Spring Integration 流,該流將檢索到的資料用作訊息載荷。
此外,您可以在流的末尾使用 JPA 出站通道介面卡來持久化資料,這實際上會在持久化操作結束時停止流。
但是,如何在流的中間執行 JPA 持久化操作呢?例如,您可能在 Spring Integration 訊息流中處理業務資料,並且希望將其持久化,但您仍然需要進一步下游使用其他元件。或者,除了使用輪詢器輪詢資料庫外,您還需要執行 JPQL 查詢並主動檢索資料,這些資料隨後在流中的後續元件中進行處理。
這時,JPA 出站閘道器就派上用場了。它們使您能夠持久化資料以及檢索資料。為了方便這些用途,Spring Integration 提供了兩種型別的 JPA 出站閘道器
-
更新出站閘道器
-
檢索出站閘道器
每當出站閘道器用於執行儲存、更新或僅刪除資料庫中某些記錄的操作時,您都需要使用更新出站閘道器。例如,如果您使用一個 entity
來持久化它,結果會返回一個已合併並持久化的實體。在其他情況下,則返回受影響的記錄數(更新或刪除)。
從資料庫中檢索(選擇)資料時,我們使用檢索出站閘道器。透過檢索出站閘道器,我們可以使用 JPQL、命名查詢(native 或基於 JPQL)或 native 查詢(SQL)來選擇資料並檢索結果。
更新出站閘道器在功能上類似於出站通道介面卡,不同之處在於更新出站閘道器在執行 JPA 操作後會將結果傳送到閘道器的回覆通道。
檢索出站閘道器類似於入站通道介面卡。
這種相似性是使用核心 JpaExecutor
類儘可能統一常見功能的主要因素。
所有 JPA 出站閘道器的共同點,類似於 outbound-channel-adapter
,我們可以用它們來執行各種 JPA 操作
-
實體類
-
JPA 查詢語言 (JPQL)
-
原生查詢
-
命名查詢
有關配置示例,請參閱JPA 出站閘道器示例。
通用配置引數
JPA 出站閘道器始終可以訪問 Spring Integration Message
作為輸入。因此,以下引數可用
parameter-source-factory
-
o.s.i.jpa.support.parametersource.ParameterSourceFactory
的一個例項,用於獲取o.s.i.jpa.support.parametersource.ParameterSource
的例項。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
(包括頭部)是非常有益的。可選。
更新出站閘道器
以下列表顯示了您可以在更新出站閘道器上設定的所有屬性,並描述了關鍵屬性
<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 起) 一個 SpEL 表示式,用於確定 EntityManager.find(Class entityClass, Object primaryKey) 方法的 primaryKey 值,將 requestMessage 作為評估上下文的根物件。如果存在 entity-class 屬性,則 entityClass 引數由此確定。否則,由 payload 類確定。如果使用 id-expression ,則不允許使用所有其他屬性。可選。 |
2 | 一個布林標誌,指示 select 操作期望返回單個結果還是結果的 List 。如果此標誌設定為 true ,則單個實體作為訊息的載荷傳送。如果返回多個實體,則丟擲異常。如果設定為 false ,則實體 List 作為訊息的載荷傳送。預設為 false 。可選。 |
3 | 此非零、非負整數值告訴介面卡在執行 select 操作時,選擇的行數不超過指定數量。預設情況下,如果未設定此屬性,則給定查詢會選擇所有可能的記錄。此屬性與 max-results-expression 互斥。可選。 |
4 | 一個表示式,可用於查詢結果集中的最大結果數。它與 max-results 互斥。可選。 |
5 | 此非零、非負整數值告訴介面卡從哪個第一條記錄開始檢索結果。此屬性與 first-result-expression 互斥。版本 3.0 引入了此屬性。可選。 |
6 | 此表示式針對訊息進行評估,以查詢結果集中第一條記錄的位置。此屬性與 first-result 互斥。版本 3.0 引入了此屬性。可選。 |
當您選擇在檢索後刪除實體,並且您檢索到了一組實體時,預設情況下,實體是逐個刪除的。這可能會導致效能問題。 或者,您可以將屬性 JSR 317: Java™ Persistence 2.0 在第 4.10 章“批次更新和刪除操作”中指出 “刪除操作僅適用於指定類及其子類的實體。它不級聯到相關實體。” 更多資訊,請參閱JSR 317: Java™ Persistence 2.0 |
從 6.0 版本開始,當查詢沒有返回任何實體時,Jpa.retrievingGateway() 會返回一個空列表結果。以前會返回 null ,從而結束流或丟擲異常,具體取決於 requiresReply 。或者,要恢復以前的行為,可以在閘道器後新增一個 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 查詢是合理的選擇。 |
當您傳送一條訊息,其載荷為 String
且包含一個名為 rollNumber
的頭部,其值為 long
時,具有指定學號的學生的姓氏將更新為訊息載荷中的值。使用更新閘道器時,返回值始終是一個整數值,表示執行 JPA QL 影響的記錄數。
使用 JPQL 檢索實體
以下示例使用檢索出站閘道器和 JPQL 從資料庫中檢索(選擇)一個或多個實體
<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 介面卡的完整示例應用程式。 |