对于最新的稳定版本,请使用 Spring Integration 6.4.3! |
出站网关
JPA 入站通道适配器允许您轮询数据库以检索一个或多个 JPA 实体。 因此,检索到的数据用于启动 Spring 集成流,该流使用检索到的数据作为消息有效负载。
此外,您可以在流的末尾使用 JPA 出站通道适配器来持久保存数据,实质上是在持久化作结束时停止流。
但是,如何在流中执行 JPA 持久性作呢?例如,您可能在 Spring Integration 消息流中处理了业务数据,并且您希望保留这些数据,但您仍然需要进一步使用下游的其他组件。 或者,您需要执行 JPQL 查询并主动检索数据,然后在流中的后续组件中处理数据,而不是使用 Poller 轮询数据库。
这就是 JPA 出站网关发挥作用的地方。 它们使您能够保存数据以及检索数据。 为了便于这些使用, Spring 集成提供了两种类型的 JPA 出站网关:
-
更新出站网关
-
检索出站网关
每当使用出站网关执行保存、更新或单独删除数据库中某些记录的作时,您都需要使用更新的出站网关。
例如,如果您使用entity
为了持久化它,将返回一个合并和持久化的实体作为结果。
在其他情况下,将返回受影响的 (更新或删除) 的记录数。
从数据库中检索(选择)数据时,我们使用检索出站网关。 使用检索出站网关,我们可以使用 JPQL、命名查询(本机或基于 JPQL)或本机查询 (SQL) 来选择数据和检索结果。
更新的出站网关在功能上类似于出站通道适配器,不同之处在于,更新的出站网关在执行 JPA作后将结果发送到网关的回复通道。
检索出站网关类似于入站通道适配器。
我们建议您首先阅读本章前面的 Outbound Channel Adapter 部分和 Inbound Channel Adapter 部分,因为大多数常见概念都在其中进行了解释。 |
这种相似性是使用 centralJpaExecutor
类来尽可能统一通用功能。
通用于所有 JPA 出站网关,类似于outbound-channel-adapter
,我们可以使用它来执行各种 JPA作:
-
实体类
-
JPA 查询语言 (JPQL)
-
本机查询
-
命名查询
有关配置示例,请参阅 JPA 出站网关示例。
通用配置参数
JPA 出站网关始终可以访问 Spring 集成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
,包括标头。 自选。
更新出站网关
以下清单显示了您可以在 updating-outbound-gateway 上设置的所有属性,并描述了关键属性:
<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 | 出站网关从中接收消息以执行所需作的通道。
此属性类似于channel 属性的outbound-channel-adapter .
自选。 |
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 起)确定primaryKey 的值EntityManager.find(Class entityClass, Object primaryKey) 方法对requestMessage 作为 Evaluation Context 的根对象。
这entityClass 参数由entity-class 属性(如果存在)。
否则,它由payload 类。
如果您使用id-expression .
自选。 |
2 | 一个布尔标志,指示 select作是应返回单个结果还是List 的结果。
如果此标志设置为true ,则单个实体将作为消息的有效负载发送。
如果返回多个实体,则会引发异常。
如果false 这List 作为消息负载发送的实体。
它默认为false .
自选。 |
3 | 此非零、非负整数值指示适配器在执行 select作时选择不超过指定行数的行数。
默认情况下,如果未设置此属性,则所有可能的记录都由给定的 query 选择。
此属性与max-results-expression .
自选。 |
4 | 一个表达式,可用于查找结果集中的最大结果数。
它与max-results .
自选。 |
5 | 这个非零、非负整数值告诉适配器要从中检索结果的第一条记录。
此属性与first-result-expression .
版本 3.0 引入了此属性。
自选。 |
6 | 根据消息计算此表达式,以查找结果集中第一条记录的位置。
此属性与first-result .
版本 3.0 引入了此属性。
自选。 |
当您选择在检索时删除实体,并且您检索了实体集合时,默认情况下,将按实体删除实体。 这可能会导致性能问题。 或者,您可以设置 attribute JSR 317:Java™ Persistence 2.0 在第 4.10 章 “批量更新和删除作”中指出: “删除作仅适用于指定类及其子类的实体。 它不会级联到相关实体。 有关更多信息,请参阅 JSR 317:Java™ 持久性 2.0 |
从版本 6.0 开始,Jpa.retrievingGateway() 当查询没有返回任何实体时,将返回空列表结果。
以前null 返回结束流或引发异常,具体取决于requiresReply .
或者,要恢复到之前的行为,请添加filter 在 gateway 之后过滤掉空列表。
在空列表处理是下游 logic一部分的应用程序中,它需要额外的配置。
请参阅 Splitter Discard Channel 了解可能的空列表处理选项。 |
JPA 出站网关示例
本节包含使用更新出站网关和检索出站网关的各种示例:
使用实体类更新
在以下示例中,通过使用org.springframework.integration.jpa.test.entity.Student
entity 类作为 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 | 这是出站网关的请求通道。
它类似于channel 属性的outbound-channel-adapter . |
2 | 这是网关与出站适配器的不同之处。
这是接收来自 JPA作的回复的通道。
但是,如果您对收到的回复不感兴趣,而只想执行该作,请使用 JPAoutbound-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
payload 中,该 intent 还包含一个名为rollNumber
替换为long
值,则具有指定卷号的学生的姓氏将更新为 Message payload.
使用更新网关时,返回值始终为整数值,该值表示受 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
是 Message 的类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 适配器的完整示例应用程序。 |