对于最新的稳定版本,请使用 Spring Integration 6.4.0! |
出站网关
JPA 入站通道适配器允许您轮询数据库以检索一个或多个 JPA 实体。 因此,检索到的数据用于启动 Spring 集成流,该流使用检索到的数据作为消息有效负载。
此外,您可以在流的末尾使用 JPA 出站通道适配器来持久保存数据,实质上是在持久化操作结束时停止流。
但是,如何在流中执行 JPA 持久性操作呢?例如,您可能在 Spring Integration 消息流中处理了业务数据,并且您希望保留这些数据,但您仍然需要进一步使用下游的其他组件。 或者,您需要执行 JPQL 查询并主动检索数据,然后在流中的后续组件中处理数据,而不是使用 Poller 轮询数据库。
这就是 JPA 出站网关发挥作用的地方。 它们使您能够保存数据以及检索数据。 为了便于这些使用, Spring 集成提供了两种类型的 JPA 出站网关:
-
更新出站网关
-
检索出站网关
每当使用出站网关执行保存、更新或单独删除数据库中某些记录的操作时,您都需要使用更新的出站网关。
例如,如果使用 an 来持久保存它,则会返回合并和持久化的实体作为结果。
在其他情况下,将返回受影响的 (更新或删除) 的记录数。entity
从数据库中检索(选择)数据时,我们使用检索出站网关。 使用检索出站网关,我们可以使用 JPQL、命名查询(本机或基于 JPQL)或本机查询 (SQL) 来选择数据和检索结果。
更新的出站网关在功能上类似于出站通道适配器,不同之处在于,更新的出站网关在执行 JPA 操作后将结果发送到网关的回复通道。
检索出站网关类似于入站通道适配器。
我们建议您首先阅读本章前面的 Outbound Channel Adapter 部分和 Inbound Channel Adapter 部分,因为大多数常见概念都在其中进行了解释。 |
这种相似性是使用 central 类来尽可能统一通用功能的主要因素。JpaExecutor
所有 JPA 出站网关都通用,类似于 ,我们可以使用它来执行各种 JPA 操作:outbound-channel-adapter
-
实体类
-
JPA 查询语言 (JPQL)
-
本机查询
-
命名查询
有关配置示例,请参阅 JPA 出站网关示例。
通用配置参数
JPA 出站网关始终可以访问 Spring 集成作为 Importing。
因此,可以使用以下参数:Message
parameter-source-factory
-
用于获取 的实例 的实例。 这用于解析查询中提供的参数的值。 如果使用 JPA 实体执行操作,则会忽略该属性。 子元素与 互斥,并且必须在提供的 . 自选。
o.s.i.jpa.support.parametersource.ParameterSourceFactory
o.s.i.jpa.support.parametersource.ParameterSource
ParameterSource
parameter-source-factory
parameter
parameter-source-factory
ParameterSourceFactory
use-payload-as-parameter-source
-
如果设置为 ,则 的有效负载将用作参数的源。 如果设置为 ,则整个可用作参数的源。 如果未传入 JPA 参数,则此属性默认为 。 这意味着,如果您使用 default ,则有效负载的 bean 属性将用作 JPA 查询的参数值的源。 但是,如果传入了 JPA 参数,则默认情况下,此属性的计算结果为 . 原因是 JPA 参数允许您提供 SPEL 表达式。 因此,访问整个 ,包括报头是非常有益的。 自选。
true
Message
false
Message
true
BeanPropertyParameterSourceFactory
false
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 | 指定网关等待将结果发送到回复通道的时间。
仅当 reply 通道本身可能阻止 send 操作(例如,当前已满的有界)时适用。
该值以毫秒为单位指定。
自选。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 起)用于确定 method 的值的 SPEL 表达式,作为评估上下文的根对象。
参数由属性(如果存在)确定。
否则,它将由类确定。
如果使用 .
自选。primaryKey EntityManager.find(Class entityClass, Object primaryKey) requestMessage entityClass entity-class payload id-expression |
2 | 一个布尔标志,指示 select 操作是应返回单个结果还是 a of results。
如果此标志设置为 ,则会将单个实体作为消息的有效负载发送。
如果返回多个实体,则会引发异常。
如果 ,则作为消息的有效负载发送的实体。
它默认为 .
自选。List true false List false |
3 | 这个非零、非负整数值告诉适配器在执行 select 操作时选择的行数不要超过指定的行数。
默认情况下,如果未设置此属性,则所有可能的记录都由给定的查询选择。
此属性与 互斥。
自选。max-results-expression |
4 | 一个表达式,可用于查找结果集中的最大结果数。
它与 互斥。
自选。max-results |
5 | 这个非零、非负整数值告诉适配器要从中检索结果的第一条记录。
此属性与 互斥。
版本 3.0 引入了此属性。
自选。first-result-expression |
6 | 根据消息计算此表达式,以查找结果集中第一条记录的位置。
此属性与 .
版本 3.0 引入了此属性。
自选。first-result |
当您选择在检索时删除实体,并且您检索了实体集合时,默认情况下,将按实体删除实体。 这可能会导致性能问题。 或者,您也可以将 attribute 设置为 ,这将执行批量删除。
但是,这样做的限制是不支持级联删除。 JSR 317:Java™ Persistence 2.0 在第 4.10 章 “批量更新和删除操作”中指出: “删除操作仅适用于指定类及其子类的实体。 它不会级联到相关实体。 有关更多信息,请参阅 JSR 317:Java™ 持久性 2.0 |
从版本 6.0 开始,当查询没有返回任何实体时,将返回空列表结果。
以前返回结束流或引发异常,具体取决于 .
或者,要恢复到之前的行为,请在网关后添加 a 以筛选出空列表。
在空列表处理是下游 logic一部分的应用程序中,它需要额外的配置。
请参阅 Splitter Discard Channel 了解可能的空列表处理选项。Jpa.retrievingGateway() null requiresReply filter |
JPA 出站网关示例
本节包含使用更新出站网关和检索出站网关的各种示例:
使用实体类更新
在以下示例中,通过使用实体类作为 JPA 定义参数来保留更新的出站网关:org.springframework.integration.jpa.test.entity.Student
<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 操作的回复的通道。
但是,如果您对收到的回复不感兴趣,而只想执行该操作,则使用 JPA 是合适的选择。
在此示例中,我们使用实体类,回复是由于 JPA 操作而创建或合并的实体对象。outbound-channel-adapter |
使用 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 查询。
由于我们使用了更新出站网关,因此只有 JPQL 查询将是明智的选择。update delete |
当您发送的有效负载消息还包含以值调用的标头时,具有指定卷号的学生的姓氏将更新为消息有效负载中的值。
使用更新网关时,返回值始终为整数值,该值表示受 JPA QL 执行影响的记录数。String
rollNumber
long
使用 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
以下示例使用检索出站网关从数据库中检索(查找)一个且仅一个实体:
这是评估的结果。
是 Message 的一个类。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 查询基本相同。
区别在于,使用的是 attribute ,如下例所示: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 适配器的完整示例应用程序。 |