对于最新的稳定版本,请使用 Spring Integration 6.5.1spring-doc.cadn.net.cn

出站网关

JPA 入站通道适配器允许您轮询数据库以检索一个或多个 JPA 实体。 因此,检索到的数据用于启动 Spring Integration 流,该流使用检索到的数据作为消息有效负载。spring-doc.cadn.net.cn

此外,您可以在流结束时使用 JPA 出站通道适配器来持久化数据,实质上是在持久化作结束时停止流。spring-doc.cadn.net.cn

但是,如何在流中间执行 JPA 持久化作?例如,您可能有正在 Spring Integration 消息流中处理的业务数据,并且您希望保留这些数据,但您仍然需要使用下游的其他组件。 或者,您需要执行 JPQL 查询并主动检索数据,然后在流程中的后续组件中处理数据,而不是使用轮询器轮询数据库。spring-doc.cadn.net.cn

这就是 JPA 出站网关发挥作用的地方。 它们使您能够保留数据以及检索数据。 为了方便这些用途,Spring Integration提供了两种类型的JPA出站网关:spring-doc.cadn.net.cn

每当使用出站网关执行保存、更新或仅删除数据库中某些记录的作时,都需要使用更新出站网关。 例如,如果您使用entity为了持久化它,结果会返回一个合并和持久化的实体。 在其他情况下,将返回受影响(更新或删除)的记录数。spring-doc.cadn.net.cn

从数据库中检索(选择)数据时,我们使用检索出站网关。 对于检索出站网关,我们可以使用 JPQL、命名查询(本机或基于 JPQL)或本机查询 (SQL) 来选择数据并检索结果。spring-doc.cadn.net.cn

更新出站网关在功能上与出站通道适配器类似,不同之处在于更新出站网关在执行 JPA作后将结果发送到网关的应答通道。spring-doc.cadn.net.cn

检索出站网关类似于入站通道适配器。spring-doc.cadn.net.cn

我们建议您首先阅读本章前面的 出站通道适配器 部分和 入站通道适配器 部分,因为其中解释了大多数常见概念。

这种相似性是使用中央的主要因素JpaExecutor类来尽可能统一通用功能。spring-doc.cadn.net.cn

对于所有 JPA 出站网关都是通用的,类似于outbound-channel-adapter,我们可以用来执行各种 JPA作:spring-doc.cadn.net.cn

有关配置示例,请参阅 JPA 出站网关示例spring-doc.cadn.net.cn

常用配置参数

JPA 出站网关始终可以访问 Spring IntegrationMessage作为输入。 因此,可以使用以下参数:spring-doc.cadn.net.cn

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. 自选。spring-doc.cadn.net.cn

use-payload-as-parameter-source

如果设置为true,的有效负载Message用作参数的来源。 如果设置为false,整个Message可用作参数的源。 如果没有传入 JPA 参数,则此属性默认为true. 这意味着,如果您使用默认的BeanPropertyParameterSourceFactory,则有效负载的 bean 属性用作 JPA 查询的参数值的源。 但是,如果传入 JPA 参数,则默认情况下,此属性的计算结果为false. 原因是 JPA 参数允许您提供 SpEL 表达式。 因此,能够访问整个是非常有益的Message,包括标题。 自选。spring-doc.cadn.net.cn

更新出站网关

以下列表显示了可以在 updating-outbound-gateway 上设置的所有属性,并描述了关键属性:spring-doc.cadn.net.cn

<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目前已满)。 该值以毫秒为单位指定。 自选。

其余属性在本章前面介绍。 请参阅配置参数参考配置参数参考spring-doc.cadn.net.cn

使用 Java 配置进行配置

以下 Spring Boot 应用程序显示了如何使用 Java 配置出站适配器的示例:spring-doc.cadn.net.cn

@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 配置出站适配器的示例:spring-doc.cadn.net.cn

@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"));
    }

}

检索出站网关

以下示例演示如何配置检索出站网关:spring-doc.cadn.net.cn

@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 以来)确定primaryKeyEntityManager.find(Class entityClass, Object primaryKey)方法与requestMessage作为评估上下文的根对象。 这entityClass参数由entity-class属性(如果存在)。 否则,它是从payload类。 如果使用id-expression. 自选。
2 一个布尔标志,指示 select作是返回单个结果还是List结果。 如果此标志设置为true,则单个实体将作为消息的有效负载发送。 如果返回多个实体,则会抛出异常。 如果falseList作为消息有效负载发送的实体数。 它默认为false. 自选。
3 此非零、非负整数值告诉适配器在执行选择作时不要选择超过指定行数。 默认情况下,如果未设置此属性,则给定查询将选择所有可能的记录。 此属性与max-results-expression. 自选。
4 可用于查找结果集中最大结果数的表达式。 它与max-results. 自选。
5 此非零、非负整数值告诉适配器要从中检索结果的第一条记录。 此属性与first-result-expression. 3.0 版引入了此属性。 自选。
6 根据消息计算此表达式,以查找结果集中第一条记录的位置。 此属性与first-result. 3.0 版引入了此属性。 自选。

如果选择在检索时删除实体,并且已检索实体集合,则默认情况下,将按实体删除实体。 这可能会导致性能问题。spring-doc.cadn.net.cn

或者,您可以设置属性deleteInBatchtrue,执行批量删除。 但是,这样做的局限性是不支持级联删除。spring-doc.cadn.net.cn

JSR 317: Java™ Persistence 2.0 在第 4.10 章 “批量更新和删除作”中指出:spring-doc.cadn.net.cn

“删除作仅适用于指定类及其子类的实体。 它不会级联到相关实体。spring-doc.cadn.net.cn

从 6.0 版开始,Jpa.retrievingGateway()当查询没有返回实体时,返回空列表结果。 以前null返回时结束流或抛出异常,具体取决于requiresReply. 或者,要恢复到以前的行为,请添加filter在网关之后过滤掉空列表。 在空列表处理是下游逻辑一部分的应用程序中,它需要额外的配置。 有关可能的空列表处理选项,请参阅拆分器丢弃通道。

JPA 出站网关示例

本节包含使用更新出站网关和检索出站网关的各种示例:spring-doc.cadn.net.cn

使用实体类进行更新

在以下示例中,更新的出站网关是使用org.springframework.integration.jpa.test.entity.Studententity 类作为 JPA 定义参数:spring-doc.cadn.net.cn

<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) 更新实体, 其中要求使用更新的出站网关:spring-doc.cadn.net.cn

<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 查询。 由于我们使用了更新出站网关,因此仅updatedeleteJPQL 查询将是明智的选择。

当您发送带有String有效负载,该负载还包含名为rollNumber使用longvalue,则具有指定卷号的学生的姓氏将更新为消息有效负载中的值。 使用更新网关时,返回值始终是整数值,该值表示受执行 JPA QL 影响的记录数。spring-doc.cadn.net.cn

使用 JPQL 检索实体

以下示例使用检索出站网关和 JPQL 从数据库中检索(选择)一个或多个实体:spring-doc.cadn.net.cn

<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.spring-doc.cadn.net.cn

<int-jpa:retrieving-outbound-gateway
	request-channel="retrievingGatewayReqChannel"
    reply-channel="retrievingGatewayReplyChannel"
    id-expression="payload.id"
    entity-manager="em"/>

使用命名查询进行更新

使用命名查询与直接使用 JPQL 查询基本相同。 不同之处在于named-query属性,如以下示例所示:spring-doc.cadn.net.cn

<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 适配器的完整示例应用程序。