此版本仍在开发中,尚未被视为稳定版。如需最新稳定版本,请使用 Spring Integration 7.0.4!spring-doc.cadn.net.cn

出站通道适配器

JPA 出站通道适配器允许您通过请求通道接受消息。 负载(payload)可用作要持久化的实体,也可在参数表达式中与标头一起用于 JPQL 查询。 以下章节将介绍执行这些操作的各种可能方式。spring-doc.cadn.net.cn

使用实体类

以下 XML 配置了出站通道适配器,用于将实体持久化到数据库:spring-doc.cadn.net.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 适配器要执行的操作。 有效值为 PERSIST、MERGE 和 DELETE。 默认值为 MERGE。
4 要使用的 JPA 实体管理器。

这四个属性配置了outbound-channel-adapter以从输入通道接受实体,并将它们处理为来自底层数据源的PERSIST、MERGE或DELETE实体。spring-doc.cadn.net.cn

自 Spring Integration 3.0 起,类型为 PERSIST 或 MERGE 的有效负载也可以是类型 java.lang.Iterable。 在这种情况下,Iterable 返回的每个对象都被视为实体,并使用底层 EntityManager 进行持久化或合并。 迭代器返回的空值将被忽略。
从版本 5.5.4 开始,配置了 PersistMode.DELETE 的 JpaExecutor 可以接受 Iterable 负载,以对提供的实体执行批量持久化删除操作。

使用 JPA 查询语言 (JPA QL)

The previous section showed how to perform a PERSIST action by using an entity. This section shows how to use an outbound channel adapter with JPA QL.spring-doc.cadn.net.cn

以下 XML 配置了出站通道适配器,用于将实体持久化到数据库:spring-doc.cadn.net.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 消息发送到的输出通道适配器的输入通道。
2 要执行的 JPA QL。 此查询可能包含参数,这些参数通过使用 parameter 元素进行求值。
3 适配器用于执行 JPA 操作所使用的实体管理器。
4 用于定义 JPA QL(在 query 属性中指定)参数名称的参数值元素(每个参数一个)。

The parameter元素接受一个属性,其name对应于提供的JPA QL中指定的命名参数(前一个示例中的第2点)。 该参数的值可以是静态的,也可以通过使用表达式推导得出。 用于推导值的静态值和表达式分别通过value和expression属性指定。 这些属性是互斥的。spring-doc.cadn.net.cn

如果指定了 value 属性,您可以提供一个可选的 type 属性。 该属性的值是 value 属性所表示值的类的完全限定名。 默认情况下,类型假定为 java.lang.String。 以下示例展示了如何定义 JPA 参数:spring-doc.cadn.net.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>

正如前面的示例所示,您可以在出站通道适配器元素中使用多个 parameter 元素,并通过表达式定义部分参数,同时使用静态值定义其他参数。 然而,请注意不要多次指定相同的参数名称。 您应为 JPA 查询中指定的每个命名参数提供一个 parameter 元素。 例如,我们指定了两个参数:level 和 name。 level 属性是类型为 java.lang.Integer 的静态值,而 name 属性则源自消息的有效负载(payload)。spring-doc.cadn.net.cn

虽然指定 select 对于 JPA QL 是有效的,但这样做没有意义。 出站通道适配器不会返回任何结果。 如果您想要选择某些值,请考虑使用出站网关代替。

使用原生查询

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

通过使用原生查询,我们可以实现数据库插入操作,而这是 JPA QL 无法做到的。 (要执行插入操作,我们将 JPA 实体发送到通道适配器,如前文所述)。 下面是一个小的 XML 片段,演示了如何使用原生查询向表中插入值。spring-doc.cadn.net.cn

命名参数可能不被您的 JPA 提供程序在与原生 SQL 查询配合使用时所支持。 虽然它们在 Hibernate 中工作正常,但 OpenJPA 和 EclipseLink 并不支持它们。 请参阅 issues.apache.org/jira/browse/OPENJPA-111。 JPA 2.0 规范的 3.8.12 节指出:“仅位置参数绑定和对结果项的位置访问可用于原生查询的便携使用。”

以下示例配置了一个使用原生查询的 outbound-channel-adapter:spring-doc.cadn.net.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 此出站通道适配器执行的本地查询。

请注意,其他属性(如 channel 和 entity-manager)以及 parameter 元素具有与 JPA QL 相同的语义。spring-doc.cadn.net.cn

使用命名查询

使用命名查询类似于使用 JPA QL 或 原生查询,区别在于我们指定的是命名查询而非具体查询。 首先,我们将介绍如何定义 JPA 命名查询。 接着,我们将介绍如何声明一个出站通道适配器以配合命名查询使用。 如果我们有一个名为 Student 的实体,可以在 Student 类上使用注解来定义两个命名查询:selectStudent 和 updateStudent。 以下示例展示了如何实现:spring-doc.cadn.net.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.cadn.net.cn

<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,如下例所示:spring-doc.cadn.net.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.cadn.net.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 操作的实体类的完全限定名。 entity-class、query和named-query属性是互斥的。 可选。
4 一个用于执行 JPA 操作的 jakarta.persistence.EntityManager 实例。 可选。
5 一个 jakarta.persistence.EntityManagerFactory 的实例用于获取 jakarta.persistence.EntityManager 的实例,后者执行 JPA 操作。 可选。
6 用于执行 JPA 操作的 org.springframework.integration.jpa.core.JpaOperations 实现。 我们建议您不要提供自己的实现,而是使用默认的 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 接受以下之一:PERSIST、MERGE或DELETE。 表示适配器需要执行的操作。 仅在使用实体进行 JPA 操作时相关。 如果提供了 JPA QL、命名查询或原生查询,则此选项将被忽略。 默认值为MERGE。 可选参数。 自 Spring Integration 3.0 起,用于持久化或合并的负载也可以是java.lang.Iterable类型。 在这种情况下,Iterable返回的每个对象都被视为实体,并使用底层的EntityManager进行持久化或合并。 迭代器返回的 null 值将被忽略。
13 如果您希望在持久化、合并或删除操作后立即刷新持久化上下文,并且不希望依赖flushMode的EntityManager,请将此值设置为true。 它默认为false。 仅当您未指定flush-size属性时适用。 如果将此属性设置为true,且未配置其他值,则flush-size将隐式设置为1。
14 如果您希望在持久化、合并或删除操作后立即刷新持久化上下文,并且不想依赖flushMode的EntityManager,请将此属性设置为大于'0'的值。 默认值设置为0,这意味着“不刷新”(no flush)。 此属性适用于负载为Iterable的消息。 例如,如果将flush-size设置为3,那么每处理第三个实体后就会调用entityManager.flush()。 此外,在循环结束后还会再次调用entityManager.flush()。 如果使用大于'0'的值指定了'flush-size'属性,则无需配置flush属性。
15 如果您希望在每次刷新操作后立即清除持久化上下文,请将此值设置为'true'。 该属性的值仅在flush属性设置为true或flush-size属性设置为大于0的值时才会生效。
16 如果设置为true,则消息的有效负载用作参数的来源。 然而,如果设置为false,整个Message将作为参数的来源可用。 可选。
17 定义事务管理属性以及JPA适配器要使用的事务管理器引用。 可选。
18 一个或多个 parameter 属性——每个在查询中使用的参数对应一个。 该值或表达式被求值以计算参数的值。 可选。

使用 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 = "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.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 outboundAdapterFlow() {
        return f -> f
                .handle(Jpa.outboundAdapter(this.entityManagerFactory)
                                .entityClass(StudentDomain.class)
                                .persistMode(PersistMode.PERSIST),
                        e -> e.transactional());
    }

}