跳到主要内容

outbound-channel-adapter

QWen Plus 中英对照 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>
xml
  • 将有效 JPA 实体发送到 JPA 外向通道适配器的通道。

  • 由适配器接受并持久化到数据库中的实体类的全限定名。在大多数情况下,实际上可以省略此属性,因为适配器可以从 Spring Integration 消息负载中自动确定实体类。

  • 适配器要执行的操作。有效值为 PERSISTMERGEDELETE。默认值是 MERGE

  • 要使用的 JPA 实体管理器。

outbound-channel-adapter 的这四个属性将其配置为通过输入通道接收实体,并将它们处理为 PERSISTMERGEDELETE,以从底层数据源中操作这些实体。

备注

从 Spring Integration 3.0 开始,有效负载也可以是类型为 [java.lang.Iterable](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Iterable.html)PERSISTMERGE。在这种情况下,Iterable 返回的每个对象都被视为一个实体,并使用底层的 EntityManager 进行持久化或合并。迭代器返回的空值将被忽略。

备注

从 5.5.4 版本开始,配置了 PersistMode.DELETEJpaExecutorJpaOutboundGateway 可以接受一个 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>
xml
  • 消息发送到 outbound 通道适配器的输入通道。

  • 要执行的 JPA QL。此查询可能包含使用 parameter 元素评估的参数。

  • 适配器用于执行 JPA 操作的实体管理器。

  • 用于定义在 query 属性中指定的 JPA QL 的参数名称值的元素(每个参数一个)。

parameter 元素接受一个属性,其 name 对应于在提供的 JPA QL 中指定的命名参数(即前面示例中的第 2 点)。参数的值可以是静态的,也可以通过使用表达式派生。静态值和用于派生值的表达式分别使用 valueexpression 属性指定。这些属性是互斥的。

如果指定了 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>
xml

如前面的示例所示,你可以在一个 outbound 通道适配器元素内使用多个 parameter 元素,并且可以使用表达式定义一些参数,而其他参数则使用静态值定义。但是,要注意不要多次指定相同的参数名称。你应该为 JPA 查询中指定的每个命名参数提供一个 parameter 元素。例如,我们指定了两个参数:levelnamelevel 属性是一个类型为 java.lang.Integer 的静态值,而 name 属性则是从消息的有效负载中派生出来的。

备注

虽然为 JPA QL 指定 select 是有效的,但这样做没有意义。输出通道适配器不会返回任何结果。如果你想选择一些值,请考虑使用输出网关。

使用原生查询

本节描述了如何使用原生查询来执行 JPA 外发通道适配器的操作。使用原生查询与使用 JPA QL 类似,区别在于查询是原生数据库查询。通过使用原生查询,我们会失去使用 JPA QL 所获得的数据库供应商独立性。

通过使用原生查询,我们可以实现数据库插入操作,这是 JPA QL 无法做到的。为了执行插入操作,我们向通道适配器发送 JPA 实体,如前面所述。下面是一个小的 xml 片段,演示了使用原生查询将值插入表中。

<your_xml_code_here>
xml

请注意,实际的 XML 代码应放置在上述的占位符中。

important

命名参数可能不被你的 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>
xml
  • 由这个 outbound channel adapter 执行的原生查询。

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

使用命名查询

使用命名查询类似于使用 JPA QL本地查询,不同之处在于我们指定一个命名查询而不是查询。首先,我们介绍如何定义 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 {

...
}
java

或者,你可以使用 orm.xml 来定义命名查询,如下例所示:

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

现在我们已经展示了如何通过使用注解或 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>
xml
  • 当适配器通过通道接收到消息时,我们希望它执行的命名查询。

配置参数参考

以下列表显示了您可以在 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>
xml
  • 生命周期属性,用于指示此组件是否应在应用程序上下文启动时启动。默认值为 true。可选。

  • 传出适配器从中接收消息以执行所需操作的通道。

  • JPA 操作的实体类的完全限定名称。entity-classquerynamed-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-managerentity-manager-factoryjpa-operations 属性中的任何一个。可选。

  • 由该适配器执行的 JPA QL。可选。

  • 需要由该适配器执行的命名查询。可选。

  • 由该适配器执行的本地查询。您可以使用 jpa-querynamed-querynative-query 属性中的任何一个。可选。

  • 当注册多个消费者时,此消费者的顺序,从而管理负载均衡和故障转移。默认值为 Ordered.LOWEST_PRECEDENCE。可选。

  • 一个 o.s.i.jpa.support.parametersource.ParameterSourceFactory 的实例,用于获取 o.s.i.jpa.support.parametersource.ParameterSource 的实例,该实例用于解析查询中参数的值。如果您使用 JPA 实体执行操作,则忽略此属性。parameter 子元素与 parameter-source-factory 属性互斥,并且必须在提供的 ParameterSourceFactory 上配置。可选。

  • 接受以下之一:PERSISTMERGEDELETE。表示适配器需要执行的操作。仅在您使用实体进行 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 进行持久化或合并。迭代器返回的空值将被忽略。

  • 如果您希望在持久化、合并或删除操作后立即刷新持久化上下文,并且不想依赖于 EntityManagerflushMode,请将此值设置为 true。默认值为 false。仅当您未指定 flush-size 属性时适用。如果此属性设置为 true,则隐式将 flush-size 设置为 1(除非另有配置)。

  • 如果您希望在持久化、合并或删除操作后立即刷新持久化上下文,并且不想依赖于 EntityManagerflushMode,请将此属性设置为大于 '0' 的值。默认值设置为 0,这意味着“不刷新”。此属性适用于具有 Iterable 有效载荷的消息。例如,如果 flush-size 设置为 3,则每处理第三个实体后调用 entityManager.flush()。此外,在整个循环结束后再调用一次 entityManager.flush()。如果指定了值大于 '0' 的 flush-size 属性,则无需配置 flush 属性。

  • 如果您希望在每次刷新操作后立即清除持久化上下文,请将此值设置为 'true'。仅当 flush 属性设置为 trueflush-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

使用 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());
}

}
java