出站閘道器
JPA 入站通道介面卡允許您輪詢資料庫以檢索一個或多個 JPA 實體。檢索到的資料隨後用於啟動一個 Spring Integration 流,該流使用檢索到的資料作為訊息負載。
此外,您可以在流的末尾使用 JPA 出站通道介面卡來持久化資料,從而在持久化操作結束時停止流。
但是,如何在流中間執行 JPA 持久化操作?例如,您可能正在 Spring Integration 訊息流中處理業務資料,並希望將其持久化,但仍需要使用其他下游元件。或者,您不需要使用輪詢器輪詢資料庫,而是需要執行 JPQL 查詢並主動檢索資料,然後由流中的後續元件處理。
這就是 JPA 出站閘道器發揮作用的地方。它們使您能夠持久化資料以及檢索資料。為了方便這些用途,Spring Integration 提供了兩種型別的 JPA 出站閘道器
-
更新出站閘道器
-
檢索出站閘道器
每當出站閘道器用於執行儲存、更新或僅刪除資料庫中某些記錄的操作時,您需要使用更新出站閘道器。例如,如果您使用一個實體來持久化它,則合併並持久化的實體將作為結果返回。在其他情況下,受影響的記錄數(已更新或已刪除)將替代返回。
當從資料庫檢索(選擇)資料時,我們使用檢索出站閘道器。使用檢索出站閘道器,我們可以使用 JPQL、命名查詢(本機或基於 JPQL)或本機查詢(SQL)來選擇資料並檢索結果。
更新出站閘道器在功能上與出站通道介面卡相似,不同之處在於更新出站閘道器在執行 JPA 操作後將結果傳送到閘道器的回覆通道。
檢索出站閘道器類似於入站通道介面卡。
這種相似性是使用中央JpaExecutor類儘可能統一常見功能的主要因素。
所有 JPA 出站閘道器都與outbound-channel-adapter相似,可用於執行各種 JPA 操作
-
實體類
-
JPA 查詢語言 (JPQL)
-
本機查詢
-
命名查詢
有關配置示例,請參見JPA 出站閘道器示例。
通用配置引數
JPA 出站閘道器始終可以訪問 Spring Integration Message 作為輸入。因此,以下引數可用
引數源工廠-
o.s.i.jpa.support.parametersource.ParameterSourceFactory的例項,用於獲取o.s.i.jpa.support.parametersource.ParameterSource的例項。ParameterSource用於解析查詢中提供的引數的值。如果您使用 JPA 實體執行操作,則忽略parameter-source-factory屬性。parameter子元素與parameter-source-factory互斥,它們必須在提供的ParameterSourceFactory上配置。可選。 使用負載作為引數源-
如果設定為
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 | 一個布林標誌,指示選擇操作是預期返回單個結果還是 List 結果。如果此標誌設定為 true,則單個實體將作為訊息的有效負載傳送。如果返回多個實體,則會丟擲異常。如果為 false,則將實體 List 作為訊息的有效負載傳送。它預設為 false。可選。 |
| 3 | 這個非零、非負整數值告訴介面卡在執行選擇操作時不要選擇超過指定數量的行。預設情況下,如果未設定此屬性,則給定查詢會選擇所有可能的記錄。此屬性與 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 持久化查詢語言 (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 介面卡的完整示例應用程式。 |