Outbound Channel Adapter

JPA 出站通道适配器允许您接受一个请求通道上的消息。有效负载可以被用作要持久化的实体,也可以与参数表达式中的标头一起用于 JPQL 查询。以下几节涵盖了执行这些操作的可能方式。

Using an Entity Class

以下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`的这四个属性配置它以通过输入通道接受实体,并将它们处理为从底层数据源中“持久化”、“合并”或“删除”实体。

从 Spring Integration 3.0 开始,到 PERSISTMERGE 的有效负载也可以是 java.lang.Iterable 类型的。在这种情况下,Iterable 返回的每个对象都被视为一个实体,并使用底层的 EntityManager 持久化或合并。迭代器返回的 Null 值将被忽略。

从版本 5.5.4 开始,JpaOutboundGateway 使用配置了 JpaExecutorPersistMode.DELETE,可以接受 Iterable 有效负载,以对提供的实体执行批量删除持久性操作。

Using JPA Query Language (JPA QL)

previous section 展示了如何使用实体执行一个 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 元素接受一个属性,其“名称”对应于在提供的 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>

正如前面的示例所示,您可以在输出通道适配器元素中使用多个`parameter`元素,并通过使用表达式和具有静态值定义一些参数。但是,注意不要多次指定相同的参数名称。您应该为 JPA 查询中指定的每个具名参数提供一个`parameter`元素。例如,我们指定两个参数:'level’和’name'。`level`属性是类型为`java.lang.Integer`的静态值,而`name`属性派生自消息的有效负载。

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

Using Native Queries

本节介绍了如何使用原生命令来使用 JPA 输出通道适配器执行操作。使用原生命令类似于使用 JPA QL,只是查询是本机数据库查询。通过使用原生命令,我们会失去 JPA QL 提供的数据库供应商独立性。

通过使用原生查询,我们可以实现的事情之一是执行数据库插入,这是 JPA QL 办不到的。(要执行插入,我们会将 JPA 实体发送到通道适配器,就像 described earlier)。以下是演示如何使用原生查询在表中插入值的一个小 XML 片段。

你的 JPA 提供器可能不支持命名的参数与本机 SQL 查询结合使用。虽然它们在 Hibernate 中工作良好,但 OpenJPA 和 EclipseLink 不支持它们。请参阅 [role="bare"][role="bare"]https://issues.apache.org/jira/browse/OPENJPA-111. JPA 2.0 规范的 3.8.12 节规定:“Only positional parameter binding and positional access to result items may be portably used for native queries.”

以下示例使用原生命令配置输出通道适配器:

<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 的语义相同。

Using Named Queries

使用命名查询类似于使用 JPA QLnative query,除了我们指定的是命名查询,而不是查询。首先,我们将介绍如何定义 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 当通过此通道接收到一条消息时,我们希望适配器执行的已命名查询。

Configuration Parameter Reference

以下清单显示了可以在输出通道适配器上设置的所有属性:

<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.EntityManager`实例的 `jakarta.persistence.EntityManagerFactory`的实例,用于执行 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.ParameterSource`实例的 `o.s.i.jpa.support.parametersource.ParameterSourceFactory`实例,用于解析查询中的参数值。如果您使用 JPA 实体执行操作,请忽略它。`parameter`子元素与 `parameter-source-factory`属性是互斥的,并且必须在所提供的 `ParameterSourceFactory`上进行配置。可选。
12 接受以下选项之一:PERSISTMERGE`或 `DELETE。表示适配器需要执行的操作。仅在您使用一个实体对 JPA 操作时才相关。如果您提供 JPA QL、已命名的查询或本机查询,则忽略。默认为 MERGE。可选。从 Spring Integration 3.0 开始,用于持久化或合并的有效负载也可以是 `https://docs.oracle.com/javase/7/docs/api/java/lang/Iterable.html[java.lang.Iterable]`类型。在这种情况下,`Iterable`返回的每个对象都被视为一个实体,并使用底层的 `EntityManager`进行持久化或合并。迭代器返回的空值将被忽略。
13 如果您希望在执行持久化、合并或删除操作后立即刷新持久性上下文,并且不想依赖 EntityManager`的 `flushMode,请将该值设置为 true。默认为 false。仅在您未指定 flush-size`属性时适用。如果此属性设置为 `true,则如果不配置其他值,那么 flush-size`会隐式设置为 `1
14 如果您希望在执行持久化、合并或删除操作后立即刷新持久性上下文,并且不想依赖 EntityManager`的 `flushMode,请将此属性设置为大于 '0' 的值。默认值设置为 0,表示“'无刷新'”。此属性面向具有 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`属性 - 每个属性对应查询中使用的每个参数。此值或表达式经过评估可计算参数的值。可选。

Configuring with Java Configuration

以下 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;
    }

}

Configuring with the 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());
    }

}