对于最新的稳定版本,请使用 Spring Integration 6.3.4! |
对于最新的稳定版本,请使用 Spring Integration 6.3.4! |
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 出站网关示例。
我们建议您首先阅读本章前面的 Outbound Channel Adapter 部分和 Inbound Channel Adapter 部分,因为大多数常见概念都在其中进行了解释。 |
通用配置参数
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 |
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 |
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 适配器的完整示例应用程序。 |
1 | 这是出站网关的请求通道。
它类似于 的属性。channel outbound-channel-adapter |
2 | 这是网关与出站适配器的不同之处。
这是接收来自 JPA 操作的回复的通道。
但是,如果您对收到的回复不感兴趣,而只想执行该操作,则使用 JPA 是合适的选择。
在此示例中,我们使用实体类,回复是由于 JPA 操作而创建或合并的实体对象。outbound-channel-adapter |
1 | 网关执行的 JPQL 查询。
由于我们使用了更新出站网关,因此只有 JPQL 查询将是明智的选择。update delete |
您可以在此处找到使用 Spring Integration 的 JPA 适配器的完整示例应用程序。 |