JPA 支持

JPA 支持

Spring 集成的 JPA(Java 持久性 API)模块提供了使用 JPA 执行各种数据库操作的组件。spring-doc.cn

您需要将此依赖项包含在您的项目中:spring-doc.cn

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

JPA API 必须通过一些特定于供应商的实现来包含,例如 Hibernate ORM Framework。spring-doc.cn

提供了以下组件:spring-doc.cn

这些组件可用于通过向目标数据库发送和接收消息来对目标数据库执行 、 、 和 操作。selectcreateupdatedeletespring-doc.cn

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

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

例如,您可以使用出站网关(在其请求通道上接收 with a as payload)来查询数据库,检索用户实体,并将其传递到下游以进行进一步处理。MessageuserIdspring-doc.cn

认识到这些语义差异, Spring 集成提供了两个单独的 JPA 出站网关:spring-doc.cn

功能性

所有 JPA 组件都使用以下选项之一执行各自的 JPA 操作:spring-doc.cn

以下部分更详细地介绍了每个组件。spring-doc.cn

支持的持久性提供程序

Spring 集成 JPA 支持已针对 Hibernate 持久化提供程序进行了测试。spring-doc.cn

Java 实现

提供的每个组件都使用该类,而该类又使用接口的实现。 其操作方式与典型的数据访问对象 (DAO) 类似,并提供 find、persist、executeUpdate 等方法。 对于大多数用例,默认实现 () 应该就足够了。 但是,如果需要自定义行为,您可以指定自己的实现。o.s.i.jpa.core.JpaExecutoro.s.i.jpa.core.JpaOperationsJpaOperationso.s.i.jpa.core.DefaultJpaOperationsspring-doc.cn

要初始化 ,您必须使用接受以下之一的构造函数之一:JpaExecutorspring-doc.cn

以下示例显示了如何使用 an 初始化 a 并在出站网关中使用它:JpaExecutorentityManagerFactoryspring-doc.cn

@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 集成提供的 XML 名称空间支持,并向您展示如何使用 XML 名称空间支持来配置 JPA 组件。spring-doc.cn

常见的 XML 名称空间配置属性

某些配置参数由所有 JPA 组件共享:spring-doc.cn

auto-startup

Lifecycle 属性,用于指示是否应在应用程序上下文启动期间启动此组件。 默认为 。 自选。truespring-doc.cn

id

标识基础 Spring Bean 定义,该定义是 或 的实例。 自选。EventDrivenConsumerPollingConsumerspring-doc.cn

entity-manager-factory

对适配器用于创建 . 您必须提供此属性、属性或属性。EntityManagerentity-managerjpa-operationsspring-doc.cn

entity-manager

对组件使用的 JPA 实体管理器的引用。 您必须提供此属性、属性或属性。entity-manager-factoryjpa-operationsspring-doc.cn

通常,您的 Spring 应用程序上下文仅定义一个 JPA 实体管理器工厂,并且使用注释注入该工厂。 这种方法不适用于 Spring 集成 JPA 组件。 通常,注入 JPA 实体管理器工厂是最好的,但是,当您想显式注入 . 有关更多信息,请参阅相关的 JavadocEntityManager@PersistenceContextEntityManagerSharedEntityManagerBean

以下示例显示了如何显式包含实体管理器工厂:spring-doc.cn

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

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

entity-class

实体类的完全限定名称。 此属性的确切语义会有所不同,具体取决于我们是执行 or 操作,还是从数据库中检索对象。persistupdatespring-doc.cn

检索数据时,您可以指定属性以指示要从数据库中检索此类型的对象。 在这种情况下,您不得定义任何查询属性 (, , 或 )。entity-classjpa-querynative-querynamed-queryspring-doc.cn

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

jpa-query

定义要使用的 JPA 查询 (Java 持久性查询语言)。spring-doc.cn

native-query

定义要使用的本机 SQL 查询。spring-doc.cn

named-query

引用命名查询。 命名查询可以在 Native SQL 或 JPAQL 中定义,但底层 JPA 持久性提供程序在内部处理这种区别。spring-doc.cn

提供 JPA 查询参数

若要提供参数,可以使用 XML 元素。 它具有一种机制,允许您为基于 Java 持久性查询语言 (JPQL) 或本机 SQL 查询的查询提供参数。 您还可以为命名查询提供参数。parameterspring-doc.cn

基于表达式的参数

以下示例演示如何设置基于表达式的参数:spring-doc.cn

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

以下示例说明如何设置基于值的参数:spring-doc.cn

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

以下示例演示如何设置基于表达式的参数:spring-doc.cn

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

事务处理

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

<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>

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

<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>

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

与在 Spring 集成的名称空间中定义的 poller 元素不同,出站网关或适配器的元素是在 JPA 名称空间中定义的。transactionaltransactional

入站通道适配器

入站通道适配器用于使用 JPA QL 对数据库执行 select 查询并返回结果。 消息负载可以是单个实体,也可以是实体。 以下 XML 配置了一个 :Listinbound-channel-adapterspring-doc.cn

<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 在属性中执行 JPA QL 后,将消息(包含有效负载)放置到的通道。inbound-channel-adapterquery
2 用于执行所需 JPA 操作的实例。EntityManager
3 Attribute 指示组件是否应在应用程序上下文启动时自动启动。 该值默认为 。true
4 其结果作为消息的有效负载发送的 JPA QL
5 此属性指示 JPQL 查询是在结果中给出单个实体还是实体。 如果该值设置为 ,则单个实体将作为消息的有效负载发送。 但是,如果在将此设置为 后返回多个结果,则会引发 a。 该值默认为 。ListtruetrueMessagingExceptionfalse
6 此非零、非负整数值指示适配器在执行 select 操作时选择不超过给定行数的行数。 默认情况下,如果未设置此属性,则查询将选择所有可能的记录。 此属性与 互斥。 自选。max-results-expression
7 一个表达式,用于查找结果集中的最大结果数。 与 互斥。 自选。max-results
8 如果要删除在执行查询后收到的行,请将此值设置为 if you want to delete。 您必须确保该组件作为事务的一部分运行。 否则,您可能会遇到如下异常:truejava.lang.IllegalArgumentException: Removing a detached instance …​
9 如果要在删除收到的实体后立即刷新持久性上下文,并且不想依赖 . 该值默认为 。trueflushModeEntityManagerfalse

配置参数参考

下面的清单显示了可以为 :inbound-channel-adapterspring-doc.cn

<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 一个布尔标志,指示是否在适配器轮询所选记录后删除这些记录。 默认情况下,值为 (即,不删除记录)。 您必须确保该组件作为事务的一部分运行。 否则,您可能会遇到异常,例如: 。 自选。falsejava.lang.IllegalArgumentException: Removing a detached instance …​
4 一个布尔标志,指示记录是可以批量删除还是必须一次删除一条记录。 默认情况下,该值为 (即,可以批量删除记录)。 自选。false
5 要从数据库中查询的实体类的完全限定名称。 适配器根据实体类名自动构建 JPA 查询。 自选。
6 用于执行 JPA 操作的实例。 自选。jakarta.persistence.EntityManager
7 用于获取执行 JPA 操作的实例的实例。 自选。jakarta.persistence.EntityManagerFactoryjakarta.persistence.EntityManager
8 一个布尔标志,指示 select 操作是应返回单个结果还是 a of results。 如果此标志设置为 ,则所选的单个实体将作为消息的有效负载发送。 如果返回多个实体,则会引发异常。 如果 ,则 of entities 作为消息的有效负载发送。 该值默认为 。 自选。ListtruefalseListfalse
9 用于执行 JPA 操作的实现。 我们建议不要提供您自己的实现,而是使用默认实现。 您可以使用任何 、 或 属性。 自选。org.springframework.integration.jpa.core.JpaOperationsorg.springframework.integration.jpa.core.DefaultJpaOperationsentity-managerentity-manager-factoryjpa-operations
10 要由此适配器执行的 JPA QL。 自选。
11 需要由此适配器执行的命名查询。 自选。
12 此适配器执行的本机查询。 您可以使用任何 、 或 属性。 自选。jpa-querynamed-queryentity-classnative-query
13 用于解析查询中参数值的实现。 如果属性具有值,则忽略。 自选。o.s.i.jpa.support.parametersource.ParameterSourceentity-class
14 向通道发送消息时等待的最长时间(以毫秒为单位)。 自选。

使用 Java 配置进行配置

以下 Spring Boot 应用程序显示了如何使用 Java 配置入站适配器的示例:spring-doc.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 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 配置入站适配器的示例:spring-doc.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 pollingAdapterFlow() {
        return IntegrationFlow
            .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 查询的参数表达式中的标头一起使用。 以下部分介绍了执行这些操作的可能方法。spring-doc.cn

使用 Entity 类

以下 XML 将出站通道适配器配置为将实体持久保存到数据库:spring-doc.cn

<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 适配器要执行的操作。 有效值为 、 和 。 默认值为 .PERSISTMERGEDELETEMERGE
4 要使用的 JPA 实体管理器。

的这四个属性将其配置为通过输入渠道接受实体,并将其处理为 、 或基础数据源中的实体。outbound-channel-adapterPERSISTMERGEDELETEspring-doc.cn

从 Spring Integration 3.0 开始,payloads to 或 也可以是 type . 在这种情况下,返回的每个对象都被视为一个实体,并使用底层 . 迭代器返回的 Null 值将被忽略。PERSISTMERGEjava.lang.IterableIterableEntityManager
从版本 5.5.4 开始,配置了 的 可以接受有效负载,以便对提供的实体执行批量删除持久操作。JpaOutboundGatewayJpaExecutorPersistMode.DELETEIterable

使用 JPA 查询语言 (JPA QL)

上一节介绍了如何使用实体执行操作。 本节介绍如何将出站通道适配器与 JPA QL 一起使用。PERSISTspring-doc.cn

以下 XML 将出站通道适配器配置为将实体持久保存到数据库:spring-doc.cn

<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 将消息发送到出站通道适配器的 input 通道。
2 要执行的 JPA QL。 此查询可能包含使用 element 计算的参数。parameter
3 适配器用于执行 JPA 操作的实体管理器。
4 用于定义属性中指定的 JPA QL 的参数名称值的元素(每个参数一个)。query

该元素接受一个属性,该属性对应于提供的 JPA QL 中指定的命名参数(前面示例中的第 2 点)。 参数的值可以是 static,也可以是使用表达式派生的。 static 值和用于派生值的表达式分别使用 和 属性指定。 这些属性是互斥的。parameternamevalueexpressionspring-doc.cn

如果指定了该属性,则可以提供 optional 属性。 此属性的值是其值由该属性表示的类的完全限定名称。 默认情况下,类型假定为 . 以下示例显示如何定义 JPA 参数:valuetypevaluejava.lang.Stringspring-doc.cn

<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>

如前面的示例所示,您可以在出站通道适配器元素中使用多个元素,并使用表达式和其他具有静态值的参数来定义一些参数。 但是,请注意不要多次指定相同的参数名称。 您应该为 JPA 查询中指定的每个命名参数提供一个元素。 例如,我们指定两个参数: 和 . 该属性是 type 的 static 值,而该属性派生自消息的有效负载。parameterparameterlevelnameleveljava.lang.Integernamespring-doc.cn

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

使用本机查询

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

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

您的 JPA 提供程序可能不支持将命名参数与本机 SQL 查询结合使用。 虽然它们与 Hibernate 一起工作良好,但 OpenJPA 和 EclipseLink 不支持它们。 请参阅 https://issues.apache.org/jira/browse/OPENJPA-111。 JPA 2.0 规范的第 3.8.12 节规定:“只有位置参数绑定和对结果项的位置访问可以移植地用于本机查询。

以下示例使用本机查询配置 outbound-channel-adapter:spring-doc.cn

<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 此出站通道适配器执行的本机查询。

请注意,其他属性(如 和 )和元素具有与 JPA QL 相同的语义。channelentity-managerparameterspring-doc.cn

使用命名查询

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

@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 来定义命名查询,如下例所示:spring-doc.cn

<entity-mappings ...>
    ...
    <named-query name="selectStudent">
        <query>select s from Student s where s.lastName = 'Last One'</query>
    </named-query>
</entity-mappings>

现在,我们已经展示了如何使用 annotations 或 using 来定义命名查询,现在我们展示一个小的 XML 片段,该片段使用命名查询定义 ,如下例所示:orm.xmloutbound-channel-adapterspring-doc.cn

<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 我们希望适配器在通过通道接收消息时执行的命名查询。

配置参数参考

下面的清单显示了您可以在出站通道适配器上设置的所有属性:spring-doc.cn

<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 Operation 的实体类的完全限定名称。 、 和 属性是互斥的。 自选。entity-classquerynamed-query
4 用于执行 JPA 操作的实例。 自选。jakarta.persistence.EntityManager
5 用于获取 的实例 的实例,该实例执行 JPA 操作。 自选。jakarta.persistence.EntityManagerFactoryjakarta.persistence.EntityManager
6 用于执行 JPA 操作的实现。 我们建议不要提供您自己的实现,而是使用默认实现。 您可以使用 、 或 属性中的任何一个。 自选。org.springframework.integration.jpa.core.JpaOperationsorg.springframework.integration.jpa.core.DefaultJpaOperationsentity-managerentity-manager-factoryjpa-operations
7 要由此适配器执行的 JPA QL。 自选。
8 需要由此适配器执行的命名查询。 自选。
9 此适配器要执行的本机查询。 您可以使用 、 或 属性中的任何一个。 自选。jpa-querynamed-querynative-query
10 注册多个使用者时此使用者的顺序,从而管理负载均衡和故障转移。 它默认为 . 自选。Ordered.LOWEST_PRECEDENCE
11 的实例 用于获取 的实例 ,用于解析查询中参数的值。 如果您使用 JPA 实体执行操作,则忽略。 子元素与属性互斥,必须在提供的 . 自选。o.s.i.jpa.support.parametersource.ParameterSourceFactoryo.s.i.jpa.support.parametersource.ParameterSourceparameterparameter-source-factoryParameterSourceFactory
12 接受以下选项之一: , , 或 . 指示适配器需要执行的操作。 仅当您将实体用于 JPA 操作时才相关。 如果您提供 JPA QL、命名查询或本机查询,则忽略。 它默认为 . 自选。 从 Spring Integration 3.0 开始,要 persist 或 merge 的有效负载也可以是 type 。 在这种情况下,返回的每个对象都被视为一个实体,并使用基础 . 迭代器返回的 Null 值将被忽略。PERSISTMERGEDELETEMERGEjava.lang.IterableIterableEntityManager
13 如果要在执行 persist、merge 或 delete 操作后立即刷新持久性上下文,并且不想依赖 的 ,请将此值设置为 . 它默认为 . 仅在未指定属性时适用。 如果此属性设置为 ,则隐式设置为 ,如果没有其他值对其进行配置。trueflushModeEntityManagerfalseflush-sizetrueflush-size1
14 如果要在执行持久化、合并或删除操作后立即刷新持久性上下文,并且不想依赖 . 默认值设置为 ,这意味着 “'no flush'”。 此属性适用于具有有效负载的消息。 例如,如果设置为 ,则在每三个实体之后调用一次。 此外,在整个循环之后再次调用。 如果 'flush-size' 属性指定的值大于 '0',则无需配置该属性。flushModeEntityManager0Iterableflush-size3entityManager.flush()entityManager.flush()flush
15 如果要在每次刷新操作后立即清除持久性上下文,请将此值设置为 'true'。 仅当属性设置为 或属性设置为大于 的值时,才会应用该属性的值。flushtrueflush-size0
16 如果设置为 ,则消息的有效负载将用作参数的源。 但是,如果设置为 ,则 entire 可用作参数的源。 自选。truefalseMessage
17 定义事务管理属性和对 JPA 适配器要使用的事务管理器的引用。 自选。
18 一个或多个属性 — 查询中使用的每个参数对应一个属性。 计算值或表达式以计算参数的值。 自选。parameter

使用 Java 配置进行配置

Spring 下面的 Boot 应用程序显示了如何使用 Java 配置出站适配器的示例:spring-doc.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 = "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 配置出站适配器的示例:spring-doc.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 outboundAdapterFlow() {
        return f -> f
                .handle(Jpa.outboundAdapter(this.entityManagerFactory)
                                .entityClass(StudentDomain.class)
                                .persistMode(PersistMode.PERSIST),
                        e -> e.transactional());
    }

}

出站网关

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

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

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

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

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

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

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

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

我们建议您首先阅读本章前面的 Outbound Channel Adapter 部分和 Inbound Channel Adapter 部分,因为大多数常见概念都在其中进行了解释。

这种相似性是使用 central 类来尽可能统一通用功能的主要因素。JpaExecutorspring-doc.cn

所有 JPA 出站网关都通用,类似于 ,我们可以使用它来执行各种 JPA 操作:outbound-channel-adapterspring-doc.cn

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

通用配置参数

JPA 出站网关始终可以访问 Spring 集成作为 Importing。 因此,可以使用以下参数:Messagespring-doc.cn

parameter-source-factory

用于获取 的实例 的实例。 这用于解析查询中提供的参数的值。 如果使用 JPA 实体执行操作,则会忽略该属性。 子元素与 互斥,并且必须在提供的 . 自选。o.s.i.jpa.support.parametersource.ParameterSourceFactoryo.s.i.jpa.support.parametersource.ParameterSourceParameterSourceparameter-source-factoryparameterparameter-source-factoryParameterSourceFactoryspring-doc.cn

use-payload-as-parameter-source

如果设置为 ,则 的有效负载将用作参数的源。 如果设置为 ,则整个可用作参数的源。 如果未传入 JPA 参数,则此属性默认为 。 这意味着,如果您使用 default ,则有效负载的 bean 属性将用作 JPA 查询的参数值的源。 但是,如果传入了 JPA 参数,则默认情况下,此属性的计算结果为 . 原因是 JPA 参数允许您提供 SPEL 表达式。 因此,访问整个 ,包括报头是非常有益的。 自选。trueMessagefalseMessagetrueBeanPropertyParameterSourceFactoryfalseMessagespring-doc.cn

更新出站网关

以下清单显示了您可以在 updating-outbound-gateway 上设置的所有属性,并描述了关键属性:spring-doc.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 出站网关从中接收消息以执行所需操作的通道。 此属性类似于 的 . 自选。channeloutbound-channel-adapter
2 网关在执行所需的 JPA 操作后将响应发送到的通道。 如果未定义此属性,则请求消息必须具有标头。 自选。replyChannel
3 指定网关等待将结果发送到回复通道的时间。 仅当 reply 通道本身可能阻止 send 操作(例如,当前已满的有界)时适用。 默认情况下,网关无限期等待。 该值以毫秒为单位指定。 自选。QueueChannel

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

使用 Java 配置进行配置

以下 Spring Boot 应用程序显示了如何使用 Java 配置出站适配器的示例:spring-doc.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.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.cn

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

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

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

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

或者,您也可以将 attribute 设置为 ,这将执行批量删除。 但是,这样做的限制是不支持级联删除。deleteInBatchtruespring-doc.cn

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

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

有关更多信息,请参阅 JSR 317:Java™ 持久性 2.0spring-doc.cn

从版本 6.0 开始,当查询没有返回任何实体时,将返回空列表结果。 以前返回结束流或引发异常,具体取决于 . 或者,要恢复到之前的行为,请在网关后添加 a 以筛选出空列表。 在空列表处理是下游 logic一部分的应用程序中,它需要额外的配置。 请参阅 Splitter Discard Channel 了解可能的空列表处理选项。Jpa.retrievingGateway()nullrequiresReplyfilter

JPA 出站网关示例

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

使用实体类更新

在以下示例中,通过使用实体类作为 JPA 定义参数来保留更新的出站网关:org.springframework.integration.jpa.test.entity.Studentspring-doc.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 这是出站网关的请求通道。 它类似于 的属性。channeloutbound-channel-adapter
2 这是网关与出站适配器的不同之处。 这是接收来自 JPA 操作的回复的通道。 但是,如果您对收到的回复不感兴趣,而只想执行该操作,则使用 JPA 是合适的选择。 在此示例中,我们使用实体类,回复是由于 JPA 操作而创建或合并的实体对象。outbound-channel-adapter
使用 JPQL 更新

以下示例使用 Java 持久性查询语言 (JPQL) 更新实体。 这要求使用更新的出站网关:spring-doc.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 查询。 由于我们使用了更新出站网关,因此只有 JPQL 查询将是明智的选择。updatedelete

当您发送的有效负载消息还包含以值调用的标头时,具有指定卷号的学生的姓氏将更新为消息有效负载中的值。 使用更新网关时,返回值始终为整数值,该值表示受 JPA QL 执行影响的记录数。StringrollNumberlongspring-doc.cn

使用 JPQL 检索实体

以下示例使用检索出站网关和 JPQL 从数据库中检索(选择)一个或多个实体:spring-doc.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

以下示例使用检索出站网关从数据库中检索(查找)一个且仅一个实体: 这是评估的结果。 是 Message 的一个类。id-expressionprimaryKeyid-expressionentityClasspayloadspring-doc.cn

<int-jpa:retrieving-outbound-gateway
	request-channel="retrievingGatewayReqChannel"
    reply-channel="retrievingGatewayReplyChannel"
    id-expression="payload.id"
    entity-manager="em"/>
使用命名查询更新

使用命名查询与直接使用 JPQL 查询基本相同。 区别在于,使用的是 attribute ,如下例所示:named-queryspring-doc.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 适配器的完整示例应用程序。