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 | 由适配器执行的操作。有效值为 PERSIST 、MERGE 和 DELETE 。默认值为 MERGE 。 |
4 | 要使用的 JPA 实体管理器。 |
`outbound-channel-adapter`的这四个属性配置它以通过输入通道接受实体,并将它们处理为从底层数据源中“持久化”、“合并”或“删除”实体。
从 Spring Integration 3.0 开始,到 |
从版本 5.5.4 开始, |
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 指定 |
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 QL 或 native query,除了我们指定的是命名查询,而不是查询。首先,我们将介绍如何定义 JPA 命名查询。然后,我们将介绍如何声明一个出站通道适配器,使其能够使用一个命名查询。如果我们有一个名为 Student
的实体,我们可以对 Student
类使用注解,以定义两个命名查询: selectStudent
和 updateStudent
。以下示例展示如何执行此操作:
@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 | 接受以下选项之一:PERSIST 、MERGE`或 `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());
}
}