outbound-channel-adapter
JPA 外发通道适配器允许您通过请求通道接收消息。有效负载可以被用作要持久化的实体,或者与标题一起在 JPQL 查询的参数表达式中使用。以下各节涵盖了执行这些操作的可能方式。
使用实体类
以下 XML 配置了 outbound 通道适配器以将实体持久化到数据库:
<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>
- 将有效 JPA 实体发送到 JPA 外向通道适配器的通道。 
- 由适配器接受并持久化到数据库中的实体类的全限定名。在大多数情况下,实际上可以省略此属性,因为适配器可以从 Spring Integration 消息负载中自动确定实体类。 
- 适配器要执行的操作。有效值为 - PERSIST、- MERGE和- DELETE。默认值是- MERGE。
- 要使用的 JPA 实体管理器。 
outbound-channel-adapter 的这四个属性将其配置为通过输入通道接收实体,并将它们处理为 PERSIST、MERGE 或 DELETE,以从底层数据源中操作这些实体。
从 Spring Integration 3.0 开始,有效负载也可以是类型为 [java.lang.Iterable](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Iterable.html) 的 PERSIST 或 MERGE。在这种情况下,Iterable 返回的每个对象都被视为一个实体,并使用底层的 EntityManager 进行持久化或合并。迭代器返回的空值将被忽略。
从 5.5.4 版本开始,配置了 PersistMode.DELETE 的 JpaExecutor 的 JpaOutboundGateway 可以接受一个 Iterable 负载,以为提供的实体执行批量删除持久化操作。
使用 JPA 查询语言 (JPA QL)
上一节展示了如何通过使用实体来执行 PERSIST 操作。本节将展示如何使用 JPA QL 的 outbound channel adapter。
以下 XML 配置了 outbound 通道适配器以将实体持久化到数据库:
<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>
- 消息发送到 outbound 通道适配器的输入通道。 
- 要执行的 JPA QL。此查询可能包含使用 - parameter元素评估的参数。
- 适配器用于执行 JPA 操作的实体管理器。 
- 用于定义在 - query属性中指定的 JPA QL 的参数名称值的元素(每个参数一个)。
parameter 元素接受一个属性,其 name 对应于在提供的 JPA QL 中指定的命名参数(即前面示例中的第 2 点)。参数的值可以是静态的,也可以通过使用表达式派生。静态值和用于派生值的表达式分别使用 value 和 expression 属性指定。这些属性是互斥的。
如果指定了 value 属性,您可以提供一个可选的 type 属性。此属性的值是表示 value 属性所代表值的类的完全限定名称。默认情况下,类型被认为是 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>
如前面的示例所示,你可以在一个 outbound 通道适配器元素内使用多个 parameter 元素,并且可以使用表达式定义一些参数,而其他参数则使用静态值定义。但是,要注意不要多次指定相同的参数名称。你应该为 JPA 查询中指定的每个命名参数提供一个 parameter 元素。例如,我们指定了两个参数:level 和 name。level 属性是一个类型为 java.lang.Integer 的静态值,而 name 属性则是从消息的有效负载中派生出来的。
虽然为 JPA QL 指定 select 是有效的,但这样做没有意义。输出通道适配器不会返回任何结果。如果你想选择一些值,请考虑使用输出网关。
使用原生查询
本节描述了如何使用原生查询来执行 JPA 外发通道适配器的操作。使用原生查询与使用 JPA QL 类似,区别在于查询是原生数据库查询。通过使用原生查询,我们会失去使用 JPA QL 所获得的数据库供应商独立性。
通过使用原生查询,我们可以实现数据库插入操作,这是 JPA QL 无法做到的。为了执行插入操作,我们向通道适配器发送 JPA 实体,如前面所述。下面是一个小的 xml 片段,演示了使用原生查询将值插入表中。
<your_xml_code_here>
请注意,实际的 XML 代码应放置在上述的占位符中。
命名参数可能不被你的 JPA 提供程序在与原生 SQL 查询结合时支持。虽然它们在 Hibernate 中可以正常工作,但 OpenJPA 和 EclipseLink 不支持它们。参见 issues.apache.org/jira/browse/OPENJPA-111。JPA 2.0 规范的 3.8.12 节指出:“对于原生查询,仅支持位置参数绑定和结果项的位置访问。”
以下示例配置了一个带有原生查询的 outbound-channel-adapter :
<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>
- 由这个 outbound channel adapter 执行的原生查询。 
请注意,其他属性(如 channel 和 entity-manager)以及 parameter 元素具有与 JPA QL 中相同的语义。
使用命名查询
@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>
- 当适配器通过通道接收到消息时,我们希望它执行的命名查询。 
配置参数参考
以下列表显示了您可以在 outbound 通道适配器上设置的所有属性:
<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>
- 生命周期属性,用于指示此组件是否应在应用程序上下文启动时启动。默认值为 - true。可选。
- 传出适配器从中接收消息以执行所需操作的通道。 
- JPA 操作的实体类的完全限定名称。 - entity-class、- query和- named-query属性是互斥的。可选。
- 用于执行 JPA 操作的 - jakarta.persistence.EntityManager的实例。可选。
- 用于获取执行 JPA 操作的 - jakarta.persistence.EntityManager实例的- jakarta.persistence.EntityManagerFactory实例。可选。
- 用于执行 JPA 操作的 - org.springframework.integration.jpa.core.JpaOperations的实现。我们建议不要提供自己的实现,而是使用默认的- org.springframework.integration.jpa.core.DefaultJpaOperations实现。您可以使用- entity-manager、- entity-manager-factory或- jpa-operations属性中的任何一个。可选。
- 由该适配器执行的 JPA QL。可选。 
- 需要由该适配器执行的命名查询。可选。 
- 由该适配器执行的本地查询。您可以使用 - jpa-query、- named-query或- native-query属性中的任何一个。可选。
- 当注册多个消费者时,此消费者的顺序,从而管理负载均衡和故障转移。默认值为 - Ordered.LOWEST_PRECEDENCE。可选。
- 一个 - o.s.i.jpa.support.parametersource.ParameterSourceFactory的实例,用于获取- o.s.i.jpa.support.parametersource.ParameterSource的实例,该实例用于解析查询中参数的值。如果您使用 JPA 实体执行操作,则忽略此属性。- parameter子元素与- parameter-source-factory属性互斥,并且必须在提供的- ParameterSourceFactory上配置。可选。
- 接受以下之一: - PERSIST、- MERGE或- DELETE。表示适配器需要执行的操作。仅在您使用实体进行 JPA 操作时相关。如果您提供了 JPA QL、命名查询或本地查询,则忽略此属性。默认值为- MERGE。可选。自 Spring Integration 3.0 起,持久化或合并的有效载荷也可以是- [java.lang.Iterable](https://docs.oracle.com/javase/7/docs/api/java/lang/Iterable.html)类型。在这种情况下,- Iterable返回的每个对象都被视为一个实体,并使用底层的- EntityManager进行持久化或合并。迭代器返回的空值将被忽略。
- 如果您希望在持久化、合并或删除操作后立即刷新持久化上下文,并且不想依赖于 - EntityManager的- flushMode,请将此值设置为- true。默认值为- false。仅当您未指定- flush-size属性时适用。如果此属性设置为- true,则隐式将- flush-size设置为- 1(除非另有配置)。
- 如果您希望在持久化、合并或删除操作后立即刷新持久化上下文,并且不想依赖于 - EntityManager的- flushMode,请将此属性设置为大于 '0' 的值。默认值设置为- 0,这意味着“不刷新”。此属性适用于具有- Iterable有效载荷的消息。例如,如果- flush-size设置为- 3,则每处理第三个实体后调用- entityManager.flush()。此外,在整个循环结束后再调用一次- entityManager.flush()。如果指定了值大于 '0' 的- flush-size属性,则无需配置- flush属性。
- 如果您希望在每次刷新操作后立即清除持久化上下文,请将此值设置为 'true'。仅当 - flush属性设置为- true或- flush-size属性设置为大于- 0的值时应用此属性的值。
- 如果设置为 - true,则消息的有效载荷用作参数的来源。然而,如果设置为- false,则整个- Message可用作参数的来源。可选。
- 定义 JPA 适配器使用的事务管理属性和事务管理器的引用。可选。 
- 一个或多个 - parameter属性——每个查询中使用的参数对应一个。计算参数值时会评估其值或表达式。可选。
使用 Java 配置进行配置
以下 Spring Boot 应用程序展示了如何使用 Java 配置 outbound 适配器的示例:
@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());
    }
}