如需使用最新稳定版本,请使用 Spring Integration 7.0.4spring-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 适配器要执行的操作。 有效值为 PERSISTMERGEDELETE。 默认值为 MERGE
4 要使用的 JPA 实体管理器。

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

自 Spring Integration 3.0 起,类型为 PERSISTMERGE 的有效负载也可以是类型 java.lang.Iterable。 在这种情况下,Iterable 返回的每个对象都被视为实体,并使用底层 EntityManager 进行持久化或合并。 迭代器返回的空值将被忽略。
从版本 5.5.4 开始,配置了 PersistMode.DELETEJpaExecutor 可以接受 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点)。 该参数的值可以是静态的,也可以通过使用表达式推导得出。 用于推导值的静态值和表达式分别通过valueexpression属性指定。 这些属性是互斥的。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 元素。 例如,我们指定了两个参数:levelnamelevel 属性是类型为 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 此出站通道适配器执行的本地查询。

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

使用命名查询

使用命名查询类似于使用 JPA QL原生查询,区别在于我们指定的是命名查询而非具体查询。 首先,我们将介绍如何定义 JPA 命名查询。 接着,我们将介绍如何声明一个出站通道适配器以配合命名查询使用。 如果我们有一个名为 Student 的实体,可以在 Student 类上使用注解来定义两个命名查询:selectStudentupdateStudent。 以下示例展示了如何实现: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-classquerynamed-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-managerentity-manager-factoryjpa-operations 属性中的任意一个。 可选。
7 此适配器要执行的 JPA QL。 可选。
8 此适配器需要执行的命名查询。 可选。
9 此适配器执行的本地查询。 您可以使用 jpa-querynamed-querynative-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进行持久化或合并。 迭代器返回的 null 值将被忽略。
13 如果您希望在持久化、合并或删除操作后立即刷新持久化上下文,并且不希望依赖flushModeEntityManager,请将此值设置为true。 它默认为false。 仅当您未指定flush-size属性时适用。 如果将此属性设置为true,且未配置其他值,则flush-size将隐式设置为1
14 如果您希望在持久化、合并或删除操作后立即刷新持久化上下文,并且不想依赖flushModeEntityManager,请将此属性设置为大于'0'的值。 默认值设置为0,这意味着“不刷新”(no flush)。 此属性适用于负载为Iterable的消息。 例如,如果将flush-size设置为3,那么每处理第三个实体后就会调用entityManager.flush()。 此外,在循环结束后还会再次调用entityManager.flush()。 如果使用大于'0'的值指定了'flush-size'属性,则无需配置flush属性。
15 如果您希望在每次刷新操作后立即清除持久化上下文,请将此值设置为'true'。 该属性的值仅在flush属性设置为trueflush-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());
    }

}