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