JPA 支持

Spring Integration 的 JPA(Java Persistence API)模块提供了使用 JPA 执行各种数据库操作的组件。

您需要将此依赖项包含到您的项目中:

Maven
<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-jpa</artifactId>
    <version>5.5.13</version>
</dependency>
Gradle
compile "org.springframework.integration:spring-integration-jpa:5.5.13"

JPA API 必须通过一些特定于供应商的实现来包含,例如 Hibernate ORM 框架。

提供以下组件:

这些组件可用于通过向目标数据库发送和接收消息来对目标数据库执行、、、和select操作createupdatedelete

JPA 入站通道适配器允许您使用 JPA 从数据库中轮询和检索 ( select) 数据,而 JPA 出站通道适配器允许您创建、更新和删除实体。

您可以使用 JPA 的出站网关将实体持久保存到数据库中,让您继续流程并在下游执行更多组件。同样,您可以使用出站网关从数据库中检索实体。

例如,您可以使用出站网关(在其请求通道上接收Message带有 auserId作为有效负载的 a)来查询数据库、检索用户实体并将其传递到下游以进行进一步处理。

认识到这些语义差异,Spring Integration 提供了两个独立的 JPA 出站网关:

  • 检索出站网关

  • 更新出站网关

功能性

所有 JPA 组件都使用以下方法之一执行各自的 JPA 操作:

  • 实体类

  • 用于更新、选择和删除的 Java 持久性查询语言 (JPQL)(JPQL 不支持插入)

  • 本机查询

  • 命名查询

以下部分更详细地描述了这些组件中的每一个。

支持的持久性提供程序

Spring Integration JPA 支持已针对以下持久性提供程序进行了测试:

  • 休眠

  • EclipseLink

使用持久性提供程序时,应确保提供程序与 JPA 2.1 兼容。

Java 实现

每个提供的组件都使用o.s.i.jpa.core.JpaExecutor该类,而该类又使用o.s.i.jpa.core.JpaOperations接口的实现。 JpaOperations像典型的数据访问对象 (DAO) 一样运行,并提供诸如 find、persist、executeUpdate 等方法。对于大多数用例,默认实现 ( o.s.i.jpa.core.DefaultJpaOperations) 就足够了。但是,如果您需要自定义行为,您可以指定自己的实现。

要初始化 a JpaExecutor,您必须使用接受以下之一的构造函数之一:

  • 实体管理器工厂

  • 实体管理器

  • JpaOperations

以下示例显示如何JpaExecutor使用 an初始化 aentityManagerFactory并在出站网关中使用它:

@Bean
public JpaExecutor jpaExecutor() {
    JpaExecutor executor = new JpaExecutor(this.entityManagerFactory);
    executor.setJpaParameters(Collections.singletonList(new JpaParameter("firstName", null, "#this")));
    executor.setUsePayloadAsParameterSource(true);
    executor.setExpectSingleResult(true);
    return executor;
}

@ServiceActivator(inputChannel = "getEntityChannel")
@Bean
public MessageHandler retrievingJpaGateway() {
    JpaOutboundGateway gateway = new JpaOutboundGateway(jpaExecutor());
    gateway.setGatewayType(OutboundGatewayType.RETRIEVING);
    gateway.setOutputChannelName("resultsChannel");
    return gateway;
}

命名空间支持

当使用 XML 命名空间支持时,底层解析器类会为您实例化相关的 Java 类。因此,您通常不需要处理 JPA 适配器的内部工作。本节记录 Spring Integration 提供的 XML 命名空间支持,并向您展示如何使用 XML 命名空间支持来配置 JPA 组件。

通用 XML 命名空间配置属性

某些配置参数由所有 JPA 组件共享:

auto-startup

生命周期属性,指示此组件是否应在应用程序上下文启动期间启动。默认为true. 可选的。

id

标识底层 Spring bean 定义,它是EventDrivenConsumeror的一个实例PollingConsumer。可选的。

entity-manager-factory

适配器用于创建EntityManager. 您必须提供此属性、该entity-manager属性或该jpa-operations属性。

entity-manager

对组件使用的 JPA 实体管理器的引用。您必须提供此属性、该entity-manager-factory属性或该jpa-operations属性。

通常,您的 Spring 应用程序上下文仅定义一个 JPA 实体管理器工厂,并EntityManager使用@PersistenceContext注解注入。此方法不适用于 Spring Integration JPA 组件。通常,注入 JPA 实体管理器工厂是最好的,但是,当您想EntityManager显式注入时,您必须定义一个SharedEntityManagerBean. 有关详细信息,请参阅相关的Javadoc

以下示例显示了如何显式包含实体管理器工厂:

<bean id="entityManager"
      class="org.springframework.orm.jpa.support.SharedEntityManagerBean">
    <property name="entityManagerFactory" ref="entityManagerFactoryBean" />
</bean>
jpa-operations

对实现JpaOperations接口的 bean 的引用。在极少数情况下,最好提供您自己的JpaOperations接口实现,而不是依赖默认实现 ( org.springframework.integration.jpa.core.DefaultJpaOperations)。如果您使用该jpa-operations属性,则不得提供 JPA 实体管理器或 JPA 实体管理器工厂,因为JpaOperations包装了必要的数据源。

entity-class

实体类的完全限定名称。这个属性的确切语义会有所不同,这取决于我们是在执行持久化操作还是更新操作,或者我们是否从数据库中检索对象。

检索数据时,您可以指定entity-class属性以指示您希望从数据库中检索此类型的对象。在这种情况下,您不得定义任何查询属性(jpa-querynative-querynamed-query)。

持久化数据时,该entity-class属性指示要持久化的对象的类型。如果未指定(对于持久化操作),实体类会自动从消息的有效负载中检索。

jpa-query

定义要使用的 JPA 查询(Java 持久性查询语言)。

native-query

定义要使用的本机 SQL 查询。

named-query

指的是命名查询。可以在 Native SQL 或 JPAQL 中定义命名查询,但底层 JPA 持久性提供程序会在内部处理这种区别。

提供 JPA 查询参数

要提供参数,您可以使用parameterXML 元素。它具有一种机制,可让您为基于 Java 持久性查询语言 (JPQL) 或本机 SQL 查询的查询提供参数。您还可以为命名查询提供参数。

基于表达式的参数

以下示例显示如何设置基于表达式的参数:

<int-jpa:parameter expression="payload.name" name="firstName"/>
基于值的参数

以下示例显示了如何设置基于值的参数:

<int-jpa:parameter name="name" type="java.lang.String" value="myName"/>
位置参数

以下示例显示如何设置基于表达式的参数:

<int-jpa:parameter expression="payload.name"/>
<int-jpa:parameter type="java.lang.Integer" value="21"/>

事务处理

所有 JPA 操作(例如INSERTUPDATEDELETE)都要求事务在执行时处于活动状态。对于入站通道适配器,您无需做任何特别的事情。它的工作方式类似于我们使用轮询器配置事务管理器的方式,轮询器与其他入站通道适配器一起使用。以下 XML 示例配置了一个事务管理器,该管理器使用带有入站通道适配器的轮询器:

<int-jpa:inbound-channel-adapter
    channel="inboundChannelAdapterOne"
    entity-manager="em"
    auto-startup="true"
    jpa-query="select s from Student s"
    expect-single-result="true"
    delete-after-poll="true">
    <int:poller fixed-rate="2000" >
        <int:transactional propagation="REQUIRED"
            transaction-manager="transactionManager"/>
    </int:poller>
</int-jpa:inbound-channel-adapter>

但是,您可能需要在使用出站通道适配器或网关时专门启动事务。如果 aDirectChannel是出站适配器或网关的输入通道,并且事务在当前执行线程中处于活动状态,则 JPA 操作将在同一事务上下文中执行。您还可以将此 JPA 操作配置为作为新事务运行,如以下示例所示:

<int-jpa:outbound-gateway
    request-channel="namedQueryRequestChannel"
    reply-channel="namedQueryResponseChannel"
    named-query="updateStudentByRollNumber"
    entity-manager="em"
    gateway-type="UPDATING">
    <int-jpa:parameter name="lastName" expression="payload"/>
    <int-jpa:parameter name="rollNumber" expression="headers['rollNumber']"/>
		<int-jpa:transactional propagation="REQUIRES_NEW"
        transaction-manager="transactionManager"/>
</int-jpa:outbound-gateway>

在前面的示例中,出站网关或适配器的事务元素指定事务属性。如果您有DirectChannel作为适配器的输入通道并且您希望适配器在与调用者相同的事务上下文中执行操作,则定义此子元素是可选的。但是,如果您使用ExecutorChannel,则必须具有该transactional元素,因为调用客户端的事务上下文不会传播。

transactional在 Spring Integration 的命名空间中定义的 poller 元素不同transactional,出站网关或适配器的元素在 JPA 命名空间中定义。

入站通道适配器

入站通道适配器用于使用 JPA QL 在数据库上执行选择查询并返回结果。消息有效负载是单个实体或一个List实体。以下 XML 配置一个inbound-channel-adapter

<int-jpa:inbound-channel-adapter channel="inboundChannelAdapterOne"  (1)
                    entity-manager="em"                              (2)
                    auto-startup="true"                              (3)
                    query="select s from Student s"                  (4)
                    expect-single-result="true"                      (5)
                    max-results=""                                   (6)
                    max-results-expression=""                        (7)
                    delete-after-poll="true"                         (8)
                    flush-after-delete="true">                       (9)
    <int:poller fixed-rate="2000" >
      <int:transactional propagation="REQUIRED" transaction-manager="transactionManager"/>
    </int:poller>
</int-jpa:inbound-channel-adapter>
1 在属性inbound-channel-adapter中执行 JPA QL 后放置消息(带有有效负载)的通道。query
2 EntityManager用于执行所需 JPA 操作的实例。
3 当应用程序上下文启动时,指示组件是否应该自动启动的属性。该值默认为true
4 JPA QL,其结果作为消息的有效负载发送出去
5 此属性告诉 JPQL 查询是在结果中给出单个实体还是一个List实体。如果该值设置为true,则将单个实体作为消息的有效负载发送。但是,如果在将此设置为 后返回多个结果trueMessagingException则抛出 a。该值默认为false
6 这个非零、非负整数值告诉适配器在执行选择操作时不要选择超过给定数量的行。默认情况下,如果未设置此属性,则查询会选择所有可能的记录。此属性与 互斥max-results-expression。可选的。
7 一个表达式,用于计算结果集中的最大结果数。与 互斥max-results。可选的。
8 true如果要删除执行查询后收到的行,请将此值设置为。您必须确保该组件作为事务的一部分运行。否则,您可能会遇到异常,例如:java.lang.IllegalArgumentException: Removing a detached instance …​
9 如果您想在删除接收到的实体后立即刷新持久性true上下文并且不想依赖flushMode. EntityManager该值默认为false

配置参数参考

以下清单显示了可以为 设置的所有值inbound-channel-adapter

<int-jpa:inbound-channel-adapter
  auto-startup="true"           (1)
  channel=""                    (2)
  delete-after-poll="false"     (3)
  delete-per-row="false"        (4)
  entity-class=""               (5)
  entity-manager=""             (6)
  entity-manager-factory=""     (7)
  expect-single-result="false"  (8)
  id=""
  jpa-operations=""             (9)
  jpa-query=""                  (10)
  named-query=""                (11)
  native-query=""               (12)
  parameter-source=""           (13)
  send-timeout="">              (14)
  <int:poller ref="myPoller"/>
 </int-jpa:inbound-channel-adapter>
1 此生命周期属性表明此组件是否应在应用程序上下文启动时自动启动。此属性默认为true. 可选的。
2 适配器通过执行所需的 JPA 操作向其发送带有有效负载的消息的通道。
3 一个布尔标志,指示在适配器轮询后是否删除选定的记录。默认值为false(即不删除记录)。您必须确保该组件作为事务的一部分运行。否则,您可能会遇到异常,例如:java.lang.IllegalArgumentException: Removing a detached instance …​. 可选的。
4 一个布尔标志,指示记录是可以批量删除还是必须一次删除一条记录。默认值为false(即可以批量删除记录)。可选的。
5 要从数据库中查询的实体类的完全限定名称。适配器会根据实体类名称自动构建 JPA Query。可选的。
6 javax.persistence.EntityManager用于执行 JPA 操作的实例。可选的。
7 javax.persistence.EntityManagerFactory用于获取javax.persistence.EntityManager执行 JPA 操作的实例的实例。可选的。
8 一个布尔标志,指示选择操作是否应返回单个结果或一个List结果。如果此标志设置为true,则选择的单个实体作为消息的有效负载发送。如果返回多个实体,则会引发异常。如果false,则List实体作为消息的有效负载发送。该值默认为false。可选的。
9 org.springframework.integration.jpa.core.JpaOperations用于执行 JPA 操作的实现。我们建议不要提供您自己的实现,而是使用默认org.springframework.integration.jpa.core.DefaultJpaOperations实现。您可以使用任何entity-managerentity-manager-factoryjpa-operations属性。可选的。
10 此适配器要执行的 JPA QL。可选的。
11 需要由此适配器执行的命名查询。可选的。
12 此适配器执行的本机查询。您可以使用任何jpa-querynamed-queryentity-classnative-query属性。可选的。
13 o.s.i.jpa.support.parametersource.ParameterSource用于解析查询中参数值的实现。entity-class如果属性有值则忽略。可选的。
14 向通道发送消息时等待的最长时间(以毫秒为单位)。可选的。

使用 Java 配置进行配置

以下 Spring Boot 应用程序显示了如何使用 Java 配置入站适配器的示例:

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

    @Bean
    @InboundChannelAdapter(channel = "jpaInputChannel",
                     poller = @Poller(fixedDelay = "${poller.interval}"))
    public MessageSource<?> jpaInbound() {
        return new JpaPollingChannelAdapter(jpaExecutor());
    }

    @Bean
    @ServiceActivator(inputChannel = "jpaInputChannel")
    public MessageHandler handler() {
        return message -> System.out.println(message.getPayload());
    }

}

使用 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 pollingAdapterFlow() {
        return IntegrationFlows
            .from(Jpa.inboundAdapter(this.entityManagerFactory)
                        .entityClass(StudentDomain.class)
                        .maxResults(1)
                        .expectSingleResult(true),
                e -> e.poller(p -> p.trigger(new OnlyOnceTrigger())))
            .channel(c -> c.queue("pollingResults"))
            .get();
    }

}

出站通道适配器

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 适配器要完成的操作。有效值为PERSISTMERGEDELETE。默认值为MERGE
4 要使用的 JPA 实体管理器。

这四个属性将其outbound-channel-adapter配置为通过输入通道接受实体并将它们处理为来自底层数据源的PERSISTMERGE或实体。DELETE

从 Spring Integration 3.0 开始,payload 到PERSISTorMERGE也可以是java.lang.Iterable. 在这种情况下,由 . 返回的每个对象Iterable都被视为一个实体,并使用底层EntityManager. 迭代器返回的空值被忽略。
从版本 5.5.4 开始,JpaOutboundGateway配置JpaExecutorPersistMode.DELETE的 可以接受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 点)。参数的值可以是静态的,也可以使用表达式派生。静态值和派生值的表达式分别使用valueexpression属性指定。这些属性是互斥的。

如果value指定了属性,则可以提供可选type属性。此属性的值是其值由该value属性表示的类的完全限定名称。默认情况下,类型假定为 a 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在出站通道适配器元素中使用多个元素,并使用表达式定义一些参数,而其他参数则使用静态值。但是,请注意不要多次指定相同的参数名称。您应该为parameterJPA 查询中指定的每个命名参数提供一个元素。例如,我们指定两个参数:levelname。该level属性是 type 的静态值java.lang.Integer,而该name属性派生自消息的有效负载。

尽管指定select对 JPA QL 有效,但这样做没有任何意义。出站通道适配器不返回任何结果。如果要选择某些值,请考虑改用出站网关。

使用本机查询

本节介绍如何使用本机查询对 JPA 出站通道适配器执行操作。使用本机查询与使用 JPA QL 类似,只是查询是本机数据库查询。通过使用本机查询,我们失去了使用 JPA QL 获得的数据库供应商独立性。

我们可以通过使用本机查询来实现的一件事是执行数据库插入,而这在 JPA QL 中是不可能的。(为了执行插入,我们将 JPA 实体发送到通道适配器,如前所述)。下面是一个小的 xml 片段,它演示了使用本机查询在表中插入值。

您的 JPA 提供程序可能不支持将命名参数与本机 SQL 查询结合使用。虽然它们与 Hibernate 一起工作得很好,但 OpenJPA 和 EclipseLink 不支持它们。请参阅https://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 此出站通道适配器执行的本机查询。

请注意,其他属性(例如channelentity-manager)和parameter元素具有与 JPA QL 相同的语义。

使用命名查询

使用命名查询类似于使用JPA QL本机查询,不同之处在于我们指定了命名查询而不是查询。首先,我们介绍如何定义 JPA 命名查询。然后我们将介绍如何声明出站通道适配器以使用命名查询。如果我们有一个名为 的实体Student,我们可以在类上使用注释Student来定义两个命名查询:selectStudentupdateStudent. 以下示例显示了如何执行此操作:

@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和属性是互斥的querynamed-query可选的。
4 javax.persistence.EntityManager用于执行 JPA 操作的实例。可选的。
5 javax.persistence.EntityManagerFactory用于获取 的实例的实例,javax.persistence.EntityManager该实例执行 JPA 操作。可选的。
6 org.springframework.integration.jpa.core.JpaOperations用于执行 JPA 操作的实现。我们建议不要提供您自己的实现,而是使用默认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 接受以下之一:PERSISTMERGEDELETE。表示适配器需要执行的操作。仅当您将实体用于 JPA 操作时才相关。如果您提供 JPA QL、命名查询或本机查询,则忽略。它默认为MERGE. 可选的。从 Spring Integration 3.0 开始,持久化或合并的有效负载也可以是java.lang.Iterable. 在这种情况下,由 . 返回的每个对象Iterable都被视为一个实体,并通过使用底层EntityManager. 迭代器返回的空值被忽略。
13 如果您想在持久化、合并或删除操作之后立即刷新持久化true上下文并且不想依赖flushMode. EntityManager它默认为false. 仅当您未指定flush-size属性时才适用。如果此属性设置为trueflush-size则隐式设置为1,如果没有其他值配置它。
14 如果您想在持久化、合并或删除操作之后立即刷新持久性上下文并且不想依赖flushMode. EntityManager默认值设置为0,表示“不刷新”。此属性适用于带有Iterable有效负载的消息。例如,如果flush-size设置为3,则entityManager.flush()在每三个实体之后调用一次。此外,entityManager.flush()在整个循环之后再次调用。如果“flush-size”属性指定的值大于“0”,则不需要配置该flush属性。
15 如果要在每次刷新操作后立即清除持久性上下文,请将此值设置为“true”。仅当flush属性设置为trueflush-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());
    }

}

出站网关

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

此外,您可以在流结束时使用 JPA 出站通道适配器来持久化数据,实质​​上是在持久化操作结束时停止流。

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

这就是 JPA 出站网关发挥作用的地方。它们使您能够持久化数据以及检索数据。为了方便这些使用,Spring Integration 提供了两种类型的 JPA 出站网关:

  • 更新出站网关

  • 检索出站网关

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

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

更新出站网关在功能上类似于出站通道适配器,不同之处在于更新出站网关在执行 JPA 操作后将结果发送到网关的回复通道。

检索出站网关类似于入站通道适配器。

这种相似性是使用中心JpaExecutor类尽可能统一通用功能的主要因素。

所有 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.ParameterSourceParameterSource用于解析查询中提供的参数的值。如果您使用 JPA 实体执行操作,parameter-source-factory则忽略该属性。parameter子元素与 是互斥的,parameter-source-factory它们必须在提供的ParameterSourceFactory. 可选的。

use-payload-as-parameter-source

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

更新出站网关

以下清单显示了您可以在更新出站网关上设置的所有属性并描述了关键属性:

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

}

检索出站网关

以下示例显示了您可以在检索出站网关上设置的所有属性并描述了关键属性:

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

本章前面介绍了其余属性。请参阅配置参数参考配置参数参考

使用 Java 配置进行配置

以下 Spring Boot 应用程序显示了如何使用 Java 配置出站适配器的示例:

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

}

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

}

当您选择在检索时删除实体并且您已检索实体集合时,默认情况下,将逐个实体删除实体。这可能会导致性能问题。

或者,您可以将属性设置deleteInBatchtrue,这将执行批量删除。但是,这样做的限制是不支持级联删除。

JSR 317:Java™ Persistence 2.0 在第 4.10 章“批量更新和删除操作”中指出:

“删除操作仅适用于指定类及其子类的实体。它不会级联到相关实体。”

有关更多信息,请参阅JSR 317:Java™ 持久性 2.0

JPA 出站网关示例

本节包含使用更新出站网关和检索出站网关的各种示例:

使用实体类更新

在以下示例中,通过使用org.springframework.integration.jpa.test.entity.Student实体类作为 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 查询。由于我们使用更新出站网关,因此只有updateJPQLdelete查询是明智的选择。

当您发送带有String有效负载的消息时,该消息还包含一个rollNumberlong值调用的标头,具有指定卷号的学生的姓氏将更新为消息有效负载中的值。使用更新网关时,返回值始终为整数值,表示受 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从数据库中检索(查找)一个且只有一个实体: 这primaryKeyid-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 适配器的完整示例应用程序。

1. see XML Configuration