出站通道介面卡
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 | 介面卡要執行的操作。有效值為 PERSIST、MERGE 和 DELETE。預設值為 MERGE。 |
| 4 | 要使用的 JPA 實體管理器。 |
outbound-channel-adapter 的這四個屬性配置它以透過輸入通道接受實體,並處理它們以從底層資料來源中PERSIST、MERGE 或 DELETE 實體。
從 Spring Integration 3.0 開始,用於 PERSIST 或 MERGE 的負載也可以是 java.lang.Iterable 型別。在這種情況下,Iterable 返回的每個物件都被視為一個實體,並使用底層 EntityManager 進行持久化或合併。迭代器返回的空值將被忽略。 |
從 5.5.4 版本開始,JpaOutboundGateway,配置了 PersistMode.DELETE 的 JpaExecutor,可以接受 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 點)。引數的值可以是靜態的,也可以透過表示式派生。靜態值和派生值的表示式分別透過 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 查詢中指定的每個命名引數提供一個 parameter 元素。例如,我們指定兩個引數:level 和 name。level 屬性是 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 | 此出站通道介面卡執行的原生查詢。 |
請注意,其他屬性(如 channel 和 entity-manager)以及 parameter 元素與 JPA QL 具有相同的語義。
使用命名查詢
使用命名查詢類似於使用 JPA QL 或 原生查詢,不同之處在於我們指定的是命名查詢而不是查詢。首先,我們介紹如何定義 JPA 命名查詢。然後,我們介紹如何宣告一個出站通道介面卡以使用命名查詢。如果有一個名為 Student 的實體,我們可以在 Student 類上使用註解來定義兩個命名查詢: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 定義命名查詢,接下來我們將展示一個小的 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-class、query 和 named-query 屬性是互斥的。可選。 |
| 4 | 用於執行 JPA 操作的 jakarta.persistence.EntityManager 例項。可選。 |
| 5 | 用於獲取 jakarta.persistence.EntityManager 例項(該例項執行 JPA 操作)的 jakarta.persistence.EntityManagerFactory 例項。可選。 |
| 6 | 用於執行 JPA 操作的 org.springframework.integration.jpa.core.JpaOperations 實現。我們建議不要提供您自己的實現,而是使用預設的 org.springframework.integration.jpa.core.DefaultJpaOperations 實現。您可以使用 entity-manager、entity-manager-factory 或 jpa-operations 屬性中的任何一個。可選。 |
| 7 | 此介面卡要執行的 JPA QL。可選。 |
| 8 | 此介面卡需要執行的命名查詢。可選。 |
| 9 | 此介面卡要執行的原生查詢。您可以使用 jpa-query、named-query 或 native-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 | 接受以下值之一:PERSIST、MERGE 或 DELETE。指示介面卡需要執行的操作。僅當您將實體用於 JPA 操作時才相關。如果您提供 JPA QL、命名查詢或原生查詢,則忽略。預設為 MERGE。可選。從 Spring Integration 3.0 開始,用於持久化或合併的負載也可以是 java.lang.Iterable 型別。在這種情況下,Iterable 返回的每個物件都被視為一個實體,並使用底層 EntityManager 進行持久化或合併。迭代器返回的空值將被忽略。 |
| 13 | 如果您希望在持久化、合併或刪除操作後立即重新整理持久化上下文,並且不希望依賴 EntityManager 的 flushMode,則將此值設定為 true。預設為 false。僅當您未指定 flush-size 屬性時適用。如果此屬性設定為 true,則 flush-size 將隱式設定為 1,如果沒有其他值配置它。 |
| 14 | 如果您希望在持久化、合併或刪除操作後立即重新整理持久化上下文,並且不希望依賴 EntityManager 的 flushMode,則將此屬性設定為大於 '0' 的值。預設值設定為 0,表示“不重新整理”。此屬性適用於帶有 Iterable 負載的訊息。例如,如果 flush-size 設定為 3,則在每第三個實體之後呼叫 entityManager.flush()。此外,在整個迴圈結束後還會再次呼叫 entityManager.flush()。如果 'flush-size' 屬性指定的值大於 '0',則無需配置 flush 屬性。 |
| 15 | 如果設定為“true”,則在每次重新整理操作後立即清除持久化上下文。此屬性的值僅在 flush 屬性設定為 true 或 flush-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());
}
}