跳到主要内容
版本:7.0.2

出站通道适配器

DeepSeek V3 中英对照 Outbound Channel Adapter

JPA出站通道适配器允许您通过请求通道接收消息。消息的有效负载既可用作待持久化的实体,也可与标头一起用于JPQL查询的参数表达式中。以下章节将介绍执行这些操作的可能方式。

使用实体类

以下 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>
  • 通过该通道将有效的 JPA 实体发送至 JPA 出站通道适配器。

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

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

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

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

备注

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

备注

从版本 5.5.4 开始,配置了 PersistMode.DELETEJpaExecutorJpaOutboundGateway 可以接受一个 Iterable 负载,为提供的实体执行批量删除持久化操作。

使用 JPA 查询语言 (JPA QL)

上一节展示了如何使用实体执行 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>
  • 输入通道,消息通过该通道发送到出站通道适配器。

  • 要执行的 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>

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

备注

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

使用原生查询

本节描述了如何使用原生查询来配合JPA出站通道适配器执行操作。使用原生查询类似于使用JPA QL,不同之处在于查询是数据库的原生查询。通过使用原生查询,我们将失去使用JPA QL时获得的数据库供应商独立性。

使用原生查询可以实现的功能之一是执行数据库插入操作,这在JPA QL中是无法实现的(要执行插入操作,我们需要将JPA实体发送到通道适配器,如前文所述)。以下是一个小型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>
  • 此出站通道适配器执行的原生查询。

请注意,其他属性(例如 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 {

...
}

或者,您也可以使用 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 来定义命名查询,现在我们将展示一个使用命名查询定义 outbound-channel-adapter 的小型 XML 片段,如下例所示:

<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>
  • 当适配器通过通道接收到消息时,我们希望它执行的有名查询。

配置参数参考

以下列表展示了您可以在出站通道适配器上设置的所有属性:

<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-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.ParameterSource 实例的 o.s.i.jpa.support.parametersource.ParameterSourceFactory 实例,该实例用于解析查询中的参数值。如果使用 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 进行持久化或合并。迭代器返回的 null 值将被忽略。

  • 如果希望在持久化、合并或删除操作后立即刷新持久化上下文,并且不希望依赖 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配置出站适配器的示例:

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

}