Spring Integration的JPA(Java Persistence API)模块提供了使用JPA执行各种数据库操作的组件。
您需要将此依赖项包含在项目中:
<dependency> <groupId>org.springframework.integration</groupId> <artifactId>spring-integration-jpa</artifactId> <version>6.0.0</version></dependency>JPA API必须通过一些特定于供应商的实现来包含,例如Hibernate ORM框架。
提供了以下组件:
- 入站通道适配器
- 出站通道适配器
- 更新出站网关
- 检索出站网关
这些组件可用于通过向目标数据库发送和接收消息来对目标数据库执行 、 和操作。selectcreateupdatedelete
JPA 入站通道适配器允许您使用 JPA 轮询和检索数据库中的 () 数据,而 JPA 出站通道适配器允许您创建、更新和删除实体。select
您可以使用 JPA 的出站网关将实体持久保存到数据库,从而继续流并在下游执行更多组件。 同样,您可以使用出站网关从数据库中检索实体。
例如,您可以使用出站网关(在其请求通道上接收具有 as 有效负载的 )来查询数据库、检索用户实体并将其传递到下游以进行进一步处理。MessageuserId
认识到这些语义差异,Spring 集成提供了两个独立的 JPA 出站网关:
- 检索出站网关
- 更新出站网关
功能性
所有 JPA 组件都使用以下方法之一执行各自的 JPA 操作:
- 实体类
- Java 持久性查询语言 (JPQL) 用于更新、选择和删除(JPQL 不支持插入)
- 本机查询
- 命名查询
以下各节将更详细地介绍其中每个组件。
支持的持久性提供程序
Spring Integration JPA 支持已经针对 Hibernate 持久性提供程序进行了测试。
爪哇实现
提供的每个组件都使用该类,而类又使用该接口的实现。 操作类似于典型的数据访问对象 (DAO),并提供查找、持久、执行更新等方法。 对于大多数用例,默认实现 () 应该足够了。 但是,如果需要自定义行为,可以指定自己的实现。o.s.i.jpa.core.JpaExecutoro.s.i.jpa.core.JpaOperationsJpaOperationso.s.i.jpa.core.DefaultJpaOperations
若要初始化 ,必须使用接受以下之一的构造函数之一:JpaExecutor
- 实体经理工厂
- 实体经理
- 摩根大通运营
以下示例演示如何使用 初始化 并在出站网关中使用它:JpaExecutorentityManagerFactory
@Beanpublic JpaExecutor jpaExecutor() { JpaExecutor executor = new JpaExecutor(this.entityManagerFactory); executor.setJpaParameters(Collections.singletonList(new JpaParameter("firstName", null, "#this"))); executor.setUsePayloadAsParameterSource(true); executor.setExpectSingleResult(true); return executor;}@ServiceActivator(inputChannel = "getEntityChannel")@Beanpublic MessageHandler retrievingJpaGateway() { JpaOutboundGateway gateway = new JpaOutboundGateway(jpaExecutor()); gateway.setGatewayType(OutboundGatewayType.RETRIEVING); gateway.setOutputChannelName("resultsChannel"); return gateway;}命名空间支持
使用 XML 命名空间支持时,基础解析器类会为您实例化相关的 Java 类。 因此,您通常不需要处理 JPA 适配器的内部工作原理。 本节记录了 Spring 集成提供的 XML 命名空间支持,并向您展示如何使用 XML 命名空间支持来配置 JPA 组件。
常见 XML 命名空间配置属性
某些配置参数由所有 JPA 组件共享:
auto-startup
指示是否应在应用程序上下文启动期间启动此组件的生命周期属性。 默认值为 。 自选。true
id
标识基础 Spring Bean 定义,该定义是 或 的实例。 自选。EventDrivenConsumerPollingConsumer
entity-manager-factory
对适配器用于创建 的 JPA 实体管理器工厂的引用。 您必须提供此属性、属性或属性。EntityManagerentity-managerjpa-operations
entity-manager
对组件使用的 JPA 实体管理器的引用。 您必须提供此属性、属性或属性。entity-manager-factoryjpa-operations
通常,您的 Spring 应用程序上下文仅定义一个 JPA 实体管理器工厂,并且使用注释注入。 此方法不适用于 Spring Integration JPA 组件。 通常,注入 JPA 实体管理器工厂是最好的,但是,当您想要显式注入 . 有关更多信息,请参阅相关的 Javadoc。EntityManager@PersistenceContextEntityManagerSharedEntityManagerBean
下面的示例演示如何显式包含实体管理器工厂:
<bean id="entityManager" class="org.springframework.orm.jpa.support.SharedEntityManagerBean"> <property name="entityManagerFactory" ref="entityManagerFactoryBean" /></bean>jpa-operations
对实现接口的 Bean 的引用。 在极少数情况下,建议提供自己的接口实现,而不是依赖默认实现 ()。 如果使用该属性,则不得提供 JPA 实体管理器或 JPA 实体管理器工厂,因为包装了必要的数据源。JpaOperationsJpaOperationsorg.springframework.integration.jpa.core.DefaultJpaOperationsjpa-operationsJpaOperations
entity-class
实体类的完全限定名。 此属性的确切语义会有所不同,具体取决于我们是执行 or 操作还是从数据库中检索对象。persistupdate
检索数据时,可以指定属性以指示要从数据库中检索此类型的对象。 在这种情况下,不得定义任何查询属性 (、 或 )。entity-classjpa-querynative-querynamed-query
持久保存数据时,该属性指示要持久保存的对象类型。 如果未指定(对于持久化操作),则会自动从消息的有效负载中检索实体类。entity-class
jpa-query
定义要使用的 JPA 查询(Java 持久性查询语言)。
native-query
定义要使用的本机 SQL 查询。
named-query
引用命名查询。 命名查询可以在本机 SQL 或 JPAQL 中定义,但基础 JPA 持久性提供程序在内部处理这种区别。
提供 JPA 查询参数
若要提供参数,可以使用 XML 元素。 它具有一种机制,允许您为基于 Java 持久性查询语言 (JPQL) 或本机 SQL 查询的查询提供参数。 还可以为命名查询提供参数。parameter
基于表达式的参数
以下示例演示如何设置基于表达式的参数:
<int-jpa:parameter expression="payload.name" name="firstName"/>基于值的参数
以下示例演示如何设置基于值的参数:
<int-jpa:parameter name="name" type="java.lang.String" value="myName"/>位置参数
以下示例演示如何设置基于表达式的参数:
<int-jpa:parameter expression="payload.name"/><int-jpa:parameter type="java.lang.Integer" value="21"/>交易处理
所有 JPA 操作(如 、 和 )都要求事务在执行时处于活动状态。 对于入站通道适配器,您无需执行任何特殊操作。 它的工作方式类似于我们使用与其他入站通道适配器一起使用的轮询器配置事务管理器的方式。 以下 XML 示例配置使用带有入站通道适配器的轮询器的事务管理器:INSERTUPDATEDELETE
<int-jpa:inbound-channel-adapter channel="inboundChannelAdapterOne" entity-manager="em" auto-startup="true" jpa-query="select s from Student s" expect-single-result="true" delete-after-poll="true"> <int:poller fixed-rate="2000" > <int:transactional propagation="REQUIRED" transaction-manager="transactionManager"/> </int:poller></int-jpa:inbound-channel-adapter>但是,在使用出站通道适配器或网关时,可能需要专门启动事务。 如果 是出站适配器或网关的输入通道,并且事务在当前执行线程中处于活动状态,那么 JPA 操作将在同一事务上下文中执行。 您还可以将此 JPA 操作配置为作为新事务运行,如以下示例所示:DirectChannel
<int-jpa:outbound-gateway request-channel="namedQueryRequestChannel" reply-channel="namedQueryResponseChannel" named-query="updateStudentByRollNumber" entity-manager="em" gateway-type="UPDATING"> <int-jpa:parameter name="lastName" expression="payload"/> <int-jpa:parameter name="rollNumber" expression="headers['rollNumber']"/> <int-jpa:transactional propagation="REQUIRES_NEW" transaction-manager="transactionManager"/></int-jpa:outbound-gateway>在前面的示例中,出站网关或适配器的事务元素指定事务属性。 如果将此子元素作为适配器的输入通道,并且希望适配器在与调用方相同的事务上下文中执行操作,则定义此子元素是可选的。 但是,如果使用 ,则必须具有该元素,因为不会传播调用客户端的事务上下文。DirectChannelExecutorChanneltransactional
与在 Spring 集成的命名空间中定义的轮询器元素不同,出站网关或适配器的元素是在 JPA 命名空间中定义的。transactionaltransactional
入站通道适配器
入站通道适配器用于使用 JPA QL 对数据库执行选择查询并返回结果。 消息负载可以是单个实体,也可以是实体的实体。 以下 XML 配置:Listinbound-channel-adapter
<int-jpa:inbound-channel-adapter channel="inboundChannelAdapterOne" entity-manager="em" auto-startup="true" query="select s from Student s" expect-single-result="true" max-results="" max-results-expression="" delete-after-poll="true" flush-after-delete="true"> <int:poller fixed-rate="2000" > <int:transactional propagation="REQUIRED" transaction-manager="transactionManager"/> </int:poller></int-jpa:inbound-channel-adapter>在属性中执行 JPA QL 后放置消息(带有有效负载)的通道。inbound-channel-adapterquery
用于执行所需 JPA 操作的实例。EntityManager
属性指示组件是否应在应用程序上下文启动时自动启动。 该值默认为 。true
JPA QL,其结果作为消息的有效负载发送出去
此属性告知 JPQL 查询是在结果中提供单个实体还是提供实体。 如果该值设置为 ,则单个实体将作为消息的有效负载发送。 但是,如果在将其设置为 后返回多个结果,则会抛出 a。 该值默认为 。ListtruetrueMessagingExceptionfalse
此非零、非负整数值告知适配器在执行选择操作时选择的行数不要超过给定的行数。 默认情况下,如果未设置此属性,则查询将选择所有可能的记录。 此属性与 互斥。 自选。max-results-expression
计算以查找结果集中的最大结果数的表达式。 与 互斥。 自选。max-results
如果要删除执行查询后收到的行,请将此值设置为 。 必须确保组件作为事务的一部分运行。 否则,您可能会遇到异常,例如:truejava.lang.IllegalArgumentException: Removing a detached instance …
如果要在删除收到的实体后立即刷新持久性上下文,并且不想依赖 的 ,请将此值设置为 。 该值默认为 。trueflushModeEntityManagerfalse
配置参数参考
以下清单显示了可以为 设置的所有值:inbound-channel-adapter
<int-jpa:inbound-channel-adapter auto-startup="true" channel="" delete-after-poll="false" delete-per-row="false" entity-class="" entity-manager="" entity-manager-factory="" expect-single-result="false" id="" jpa-operations="" jpa-query="" named-query="" native-query="" parameter-source="" send-timeout=""> <int:poller ref="myPoller"/> </int-jpa:inbound-channel-adapter>此生命周期属性指示此组件是否应在应用程序上下文启动时自动启动。 此属性默认为 。 自选。true
适配器从执行所需的 JPA 操作向该通道发送消息,其中包含有效负载。
一个布尔标志,指示在适配器轮询所选记录后是否删除这些记录。 默认情况下,该值为 (即,不会删除记录)。 必须确保组件作为事务的一部分运行。 否则,您可能会遇到异常,例如:。 自选。falsejava.lang.IllegalArgumentException: Removing a detached instance …
一个布尔标志,指示是可以批量删除记录还是必须一次删除一条记录。 默认情况下,该值为 (即,可以批量删除记录)。 自选。false
要从数据库中查询的实体类的完全限定名。 适配器根据实体类名自动构建 JPA 查询。 自选。
用于执行 JPA 操作的实例。 自选。jakarta.persistence.EntityManager
用于获取执行 JPA 操作的实例的实例。 自选。jakarta.persistence.EntityManagerFactoryjakarta.persistence.EntityManager
一个布尔标志,指示选择操作是应返回单个结果还是一个结果。 如果此标志设置为 ,则所选的单个实体将作为消息的有效负载发送。 如果返回多个实体,则会引发异常。 如果为 ,则实体作为消息的有效负载发送。 该值默认为 。 自选。ListtruefalseListfalse
用于执行 JPA 操作的实现。 我们建议不要提供自己的实现,而是使用默认实现。 可以使用任何 、 或 属性。 自选。org.springframework.integration.jpa.core.JpaOperationsorg.springframework.integration.jpa.core.DefaultJpaOperationsentity-managerentity-manager-factoryjpa-operations
要由此适配器执行的 JPA QL。 自选。
需要由此适配器执行的命名查询。 自选。
此适配器执行的本机查询。 您可以使用任何 、、 或 属性。 自选。jpa-querynamed-queryentity-classnative-query
用于解析查询中参数值的实现。 如果属性具有值,则忽略。 自选。o.s.i.jpa.support.parametersource.ParameterSourceentity-class
向通道发送消息时等待的最长时间(以毫秒为单位)。 自选。
使用 Java 配置进行配置
以下 Spring 引导应用程序显示了如何使用 Java 配置入站适配器的示例:
@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 JpaExecutor jpaExecutor() { JpaExecutor executor = new JpaExecutor(this.entityManagerFactory); jpaExecutor.setJpaQuery("from Student"); return executor; } @Bean @InboundChannelAdapter(channel = "jpaInputChannel", poller = @Poller(fixedDelay = "${poller.interval}")) public MessageSource<?> jpaInbound() { return new JpaPollingChannelAdapter(jpaExecutor()); } @Bean @ServiceActivator(inputChannel = "jpaInputChannel") public MessageHandler handler() { return message -> System.out.println(message.getPayload()); }}使用 Java DSL 进行配置
以下 Spring 引导应用程序显示了如何使用 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 pollingAdapterFlow() { return IntegrationFlow .from(Jpa.inboundAdapter(this.entityManagerFactory) .entityClass(StudentDomain.class) .maxResults(1) .expectSingleResult(true), e -> e.poller(p -> p.trigger(new OnlyOnceTrigger()))) .channel(c -> c.queue("pollingResults")) .get(); }}出站通道适配器
JPA 出站通道适配器允许您通过请求通道接受消息。 有效负载可以用作要保留的实体,也可以与 JPQL 查询的参数表达式中的标头一起使用。 以下各节介绍执行这些操作的可能方法。
使用实体类
以下 XML 将出站通道适配器配置为将实体保存到数据库:
<int-jpa:outbound-channel-adapter channel="entityTypeChannel" entity-class="org.springframework.integration.jpa.test.entity.Student" persist-mode="PERSIST" entity-manager="em"/ >将有效 JPA 实体发送到 JPA 出站通道适配器的通道。
适配器接受的要在数据库中保留的实体类的完全限定名。 在大多数情况下,您实际上可以省略此属性,因为适配器可以从 Spring 集成消息有效负载中自动确定实体类。
适配器要完成的操作。 有效值为 、 和 。 默认值为 。PERSISTMERGEDELETEMERGE
要使用的 JPA 实体管理器。
的这四个属性将其配置为通过输入通道接受实体,并将其处理为 、 或基础数据源中的实体。outbound-channel-adapterPERSISTMERGEDELETE
从 Spring Integration 3.0 开始,有效负载 或 也可以是 . 在这种情况下,由 返回的每个对象都被视为一个实体,并使用基础 . 迭代器返回的空值将被忽略。PERSISTMERGEjava.lang.IterableIterableEntityManager
从版本 5.5.4 开始,配置了 的 可以接受有效负载,以便对提供的实体执行批量删除持久性操作。JpaOutboundGatewayJpaExecutorPersistMode.DELETEIterable
使用 JPA 查询语言 (JPA QL)
上一节演示了如何使用实体执行操作。 本节说明如何将出站通道适配器与 JPA QL 配合使用。PERSIST
以下 XML 将出站通道适配器配置为将实体保存到数据库:
<int-jpa:outbound-channel-adapter channel="jpaQlChannel" jpa-query="update Student s set s.firstName = :firstName where s.rollNumber = :rollNumber" entity-manager="em"> <int-jpa:parameter name="firstName" expression="payload['firstName']"/> <int-jpa:parameter name="rollNumber" expression="payload['rollNumber']"/></int-jpa:outbound-channel-adapter>消息发送到出站通道适配器的输入通道。
要执行的 JPA QL。 此查询可能包含使用该元素计算的参数。parameter
适配器用于执行 JPA 操作的实体管理器。
用于定义属性中指定的 JPA QL 的参数名称值的元素(每个参数一个)。query
该元素接受一个属性,该属性对应于提供的 JPA QL 中指定的命名参数(前面示例中的第 2 点)。 参数的值可以是静态的,也可以是使用表达式派生的。 静态值和派生值的表达式分别使用 和 属性指定。 这些属性是互斥的。parameternamevalueexpression
如果指定了该属性,则可以提供可选属性。 此属性的值是其值由该属性表示的类的完全限定名。 默认情况下,假定类型为 . 以下示例演示如何定义 JPA 参数:valuetypevaluejava.lang.String
<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>如前面的示例所示,可以在出站通道适配器元素中使用多个元素,并使用表达式定义一些参数,并使用具有静态值的其他参数来定义一些参数。 但是,请注意不要多次指定相同的参数名称。 应为 JPA 查询中指定的每个命名参数提供一个元素。 例如,我们指定两个参数:和 . 该属性是 类型的静态值,而该属性派生自消息的有效负载。parameterparameterlevelnameleveljava.lang.Integername
尽管指定对 JPA QL 有效,但这样做是没有意义的。 出站通道适配器不返回任何结果。 如果要选择某些值,请考虑改用出站网关。select
使用本机查询
本节描述如何使用本机查询对 JPA 出站通道适配器执行操作。 使用本机查询类似于使用 JPA QL,不同之处在于查询是本机数据库查询。 通过使用本机查询,我们失去了使用 JPA QL 获得的数据库供应商独立性。
通过使用本机查询,我们可以实现的一件事是执行数据库插入,这在 JPA QL 中是不可能的。 (为了执行插入,我们将 JPA 实体发送到通道适配器,如前所述)。 下面是一个小的 xml 片段,演示如何使用本机查询在表中插入值。
JPA 提供程序可能不支持将命名参数与本机 SQL 查询结合使用。 虽然它们在Hibernate上工作正常,但OpenJPA和EclipseLink不支持它们。 请参阅 https://issues.apache.org/jira/browse/OPENJPA-111。 JPA 2.0 规范的第 3.8.12 节指出:“只有位置参数绑定和对结果项的位置访问才能移植用于本机查询。
以下示例使用本机查询配置出站通道适配器:
<int-jpa:outbound-channel-adapter channel="nativeQlChannel" native-query="insert into STUDENT_TABLE(FIRST_NAME,LAST_UPDATED) values (:lastName,:lastUpdated)" 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>此出站通道适配器执行的本机查询。
请注意,其他属性(例如 和 )和元素具有与 JPA QL 相同的语义。channelentity-managerparameter
使用命名查询
使用命名查询类似于使用 JPA QL 或本机查询,不同之处在于我们指定命名查询而不是查询。 首先,我们将介绍如何定义 JPA 命名查询。 然后,我们将介绍如何声明出站通道适配器以使用命名查询。 如果我们有一个名为 的实体,我们可以在类上使用注释来定义两个命名查询:和 . 以下示例演示如何执行此操作:StudentStudentselectStudentupdateStudent
@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>现在我们已经演示了如何使用批注或使用 来定义命名查询,现在我们显示一个小的 XML 片段,该片段使用命名查询定义 ,如以下示例所示:orm.xmloutbound-channel-adapter
<int-jpa:outbound-channel-adapter channel="namedQueryChannel" named-query="updateStudent" 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" channel="" entity-class="" entity-manager="" entity-manager-factory="" id="" jpa-operations="" jpa-query="" named-query="" native-query="" order="" parameter-source-factory="" persist-mode="MERGE" flush="true" flush-size="10" clear-on-flush="true" use-payload-as-parameter-source="true" <int:poller/> <int-jpa:transactional/> <int-jpa:parameter/> </int-jpa:outbound-channel-adapter>生命周期属性,指示此组件是否应在应用程序上下文启动期间启动。 默认为 . 自选。true
出站适配器从中接收消息以执行所需操作的通道。
JPA 操作的实体类的完全限定名。 、 和 属性是互斥的。 自选。entity-classquerynamed-query
用于执行 JPA 操作的实例。 自选。jakarta.persistence.EntityManager
用于获取执行 JPA 操作的 实例的实例。 自选。jakarta.persistence.EntityManagerFactoryjakarta.persistence.EntityManager
用于执行 JPA 操作的实现。 我们建议不要提供自己的实现,而是使用默认实现。 可以使用 、 或 属性中的任何一个。 自选。org.springframework.integration.jpa.core.JpaOperationsorg.springframework.integration.jpa.core.DefaultJpaOperationsentity-managerentity-manager-factoryjpa-operations
要由此适配器执行的 JPA QL。 自选。
需要由此适配器执行的命名查询。 自选。
此适配器要执行的本机查询。 可以使用 、 或 属性中的任何一个。 自选。jpa-querynamed-querynative-query
注册多个使用者时此使用者的顺序,从而管理负载平衡和故障转移。 默认为 . 自选。Ordered.LOWEST_PRECEDENCE
用于获取 的实例,用于解析查询中参数的值。 如果使用 JPA 实体执行操作,则忽略。 子元素与属性互斥,必须在提供的 . 自选。o.s.i.jpa.support.parametersource.ParameterSourceFactoryo.s.i.jpa.support.parametersource.ParameterSourceparameterparameter-source-factoryParameterSourceFactory
接受下列选项之一:、 或 。 指示适配器需要执行的操作。 仅当您使用实体进行 JPA 操作时才相关。 如果提供 JPA QL、命名查询或本机查询,则忽略。 默认为 . 自选。 从 Spring Integration 3.0 开始,要持久化或合并的有效负载也可以是 . 在这种情况下,由 返回的每个对象都被视为一个实体,并使用基础 . 迭代器返回的空值将被忽略。PERSISTMERGEDELETEMERGEjava.lang.IterableIterableEntityManager
如果要在执行持久化、合并或删除操作后立即刷新持久性上下文,并且不希望依赖 的 ,请将此值设置为 。 默认为 . 仅当未指定属性时才适用。 如果此属性设置为 ,则隐式设置为 ,如果没有其他值配置它。trueflushModeEntityManagerfalseflush-sizetrueflush-size1
如果要在持久化、合并或删除操作后立即刷新持久性上下文,并且不想依赖 的 ,请将此属性设置为大于“0”的值。 默认值设置为 ,表示“不刷新”。 此属性适用于具有有效负载的消息。 例如,如果设置为 ,则在每三个实体之后调用。 此外,在整个循环之后再次调用。 如果使用大于“0”的值指定“flush-size”属性,则无需配置该属性。flushModeEntityManager0Iterableflush-size3entityManager.flush()entityManager.flush()flush
如果要在每次刷新操作后立即清除持久性上下文,请将此值设置为“true”。 仅当属性设置为 或属性设置为大于 的值时,才会应用属性的值。flushtrueflush-size0
如果设置为 ,则消息的有效负载将用作参数源。 但是,如果设置为 ,则整个参数可用作参数的源。 自选。truefalseMessage
定义事务管理属性和对 JPA 适配器要使用的事务管理器的引用。 自选。
一个或多个属性 — 查询中使用的每个参数对应一个属性。 计算值或表达式以计算参数的值。 自选。parameter
使用 Java 配置进行配置
以下 Spring 引导应用程序显示了如何使用 Java 配置出站适配器的示例:
@SpringBootApplication@EntityScan(basePackageClasses = StudentDomain.class)@IntegrationComponentScanpublic 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 引导应用程序显示了如何使用 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()); }}出站网关
JPA 入站通道适配器允许您轮询数据库以检索一个或多个 JPA 实体。 因此,检索到的数据用于启动 Spring 集成流,该流将检索到的数据用作消息有效负载。
此外,您可以在流程结束时使用 JPA 出站通道适配器来持久化数据,实质上是在持久性操作结束时停止流。
但是,如何在流中间执行 JPA 持久性操作?例如,您可能在 Spring 集成消息流中处理了业务数据,并且想要保留这些数据,但您仍然需要在下游使用其他组件。 或者,您需要执行 JPQL 查询并主动检索数据,然后在流中的后续组件中处理这些数据,而不是使用轮询器轮询数据库。
这就是 JPA 出站网关发挥作用的地方。 它们使您能够保留数据和检索数据。 为了方便这些用途,Spring 集成提供了两种类型的 JPA 出站网关:
- 更新出站网关
- 检索出站网关
每当使用出站网关执行保存、更新或仅删除数据库中某些记录的操作时,都需要使用更新出站网关。 例如,如果使用 来持久化它,则会返回合并和持久化的实体作为结果。 在其他情况下,将返回受影响的记录数(更新或删除)。entity
从数据库中检索(选择)数据时,我们使用检索出站网关。 通过检索出站网关,我们可以使用 JPQL、命名查询(本机或基于 JPQL)或本机查询 (SQL) 来选择数据和检索结果。
更新出站网关在功能上类似于出站通道适配器,不同之处在于更新出站网关在执行 JPA 操作后将结果发送到网关的回复通道。
检索出站网关类似于入站通道适配器。
我们建议您首先阅读本章前面的出站通道适配器部分和入站通道适配器部分,因为那里介绍了大多数常见概念。
这种相似性是使用中心类尽可能统一通用功能的主要因素。JpaExecutor
所有 JPA 出站网关通用,类似于 ,我们可用于执行各种 JPA 操作:outbound-channel-adapter
- 实体类
- JPA 查询语言 (JPQL)
- 本机查询
- 命名查询
有关配置示例,请参阅 JPA 出站网关示例。
常用配置参数
JPA 出站网关始终可以访问 Spring 集成作为输入。 因此,以下参数可用:Message
parameter-source-factory
用于获取 的实例的实例。 用于解析查询中提供的参数的值。 如果使用 JPA 实体执行操作,那么将忽略该属性。 子元素与 互斥,必须在提供的 上配置。 自选。o.s.i.jpa.support.parametersource.ParameterSourceFactoryo.s.i.jpa.support.parametersource.ParameterSourceParameterSourceparameter-source-factoryparameterparameter-source-factoryParameterSourceFactory
use-payload-as-parameter-source
如果设置为 ,则 的有效负载将用作参数的源。 如果设置为 ,则整个参数可用作参数源。 如果未传入任何 JPA 参数,那么此属性缺省为 。 这意味着,如果使用缺省值 ,则有效负载的 Bean 属性将用作 JPA 查询的参数值的源。 但是,如果传入 JPA 参数,则缺省情况下此属性的计算结果为 。 原因是 JPA 参数允许您提供 SpEL 表达式。 因此,访问整个 ,包括标头是非常有益的。 自选。trueMessagefalseMessagetrueBeanPropertyParameterSourceFactoryfalseMessage
更新出站网关
以下清单显示了可以在更新出站网关上设置的所有属性,并描述了关键属性:
<int-jpa:updating-outbound-gateway request-channel="" auto-startup="true" entity-class="" entity-manager="" entity-manager-factory="" id="" jpa-operations="" jpa-query="" named-query="" native-query="" order="" parameter-source-factory="" persist-mode="MERGE" reply-channel="" reply-timeout="" use-payload-as-parameter-source="true"> <int:poller/> <int-jpa:transactional/> <int-jpa:parameter name="" type="" value=""/> <int-jpa:parameter name="" expression=""/></int-jpa:updating-outbound-gateway>出站网关从中接收消息以执行所需操作的通道。 此属性类似于 的属性。 自选。channeloutbound-channel-adapter
网关在执行所需的 JPA 操作后向其发送响应的通道。 如果未定义此属性,则请求消息必须具有标头。 自选。replyChannel
指定网关等待将结果发送到回复通道的时间。 仅当回复通道本身可能阻止发送操作(例如,当前已满的有界通道)时,才适用。 默认情况下,网关无限期等待。 该值以毫秒为单位指定。 自选。QueueChannel
其余属性在本章前面部分介绍。 请参阅配置参数参考和配置参数参考。
使用 Java 配置进行配置
以下 Spring 引导应用程序显示了如何使用 Java 配置出站适配器的示例:
@SpringBootApplication@EntityScan(basePackageClasses = StudentDomain.class)@IntegrationComponentScanpublic 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 = "jpaUpdateChannel") @Transactional void updateStudent(StudentDomain payload); } @Bean @ServiceActivator(channel = "jpaUpdateChannel") public MessageHandler jpaOutbound() { JpaOutboundGateway adapter = new JpaOutboundGateway(new JpaExecutor(this.entityManagerFactory)); adapter.setOutputChannelName("updateResults"); return adapter; }}使用 Java DSL 进行配置
以下 Spring 引导应用程序显示了如何使用 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 updatingGatewayFlow() { return f -> f .handle(Jpa.updatingGateway(this.entityManagerFactory), e -> e.transactional(true)) .channel(c -> c.queue("updateResults")); }}检索出站网关
以下示例演示如何配置检索出站网关:
@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 retrievingGatewayFlow() { return f -> f .handle(Jpa.retrievingGateway(this.entityManagerFactory) .jpaQuery("from Student s where s.id = :id") .expectSingleResult(true) .parameterExpression("id", "payload")) .channel(c -> c.queue("retrieveResults")); }}(自春季集成4.0以来)一个 SpEL 表达式,用于确定方法的值,该表达式作为计算上下文的根对象。 参数由属性(如果存在)确定。 否则,它由类确定。 如果使用 ,则不允许所有其他属性。 自选。primaryKeyEntityManager.find(Class entityClass, Object primaryKey)requestMessageentityClassentity-classpayloadid-expression
一个布尔标志,指示选择操作是应返回单个结果还是一个结果。 如果此标志设置为 ,则单个实体将作为消息的有效负载发送。 如果返回多个实体,则会引发异常。 如果为 ,则实体作为消息的有效负载发送。 默认为 . 自选。ListtruefalseListfalse
此非零、非负整数值告知适配器在执行选择操作时选择的行数不要超过指定的行数。 默认情况下,如果未设置此属性,则给定查询将选择所有可能的记录。 此属性与 互斥。 自选。max-results-expression
可用于查找结果集中最大结果数的表达式。 它与 互斥。 自选。max-results
此非零、非负整数值告知适配器要从中检索结果的第一条记录。 此属性与 互斥。 版本 3.0 引入了此属性。 自选。first-result-expression
根据消息计算此表达式,以查找结果集中第一条记录的位置。 此属性与 互斥。 版本 3.0 引入了此属性。 自选。first-result
如果选择在检索时删除实体,并且已检索到实体集合,则默认情况下,将基于每个实体删除实体。 这可能会导致性能问题。
或者,可以将属性设置为 ,这将执行批量删除。 但是,这样做的限制是不支持级联删除。deleteInBatchtrue
JSR 317:Java™ 持久性 2.0 在第 4.10 章 “批量更新和删除操作”中指出:
“删除操作仅适用于指定类及其子类的实体。 它不会级联到相关实体。
有关更多信息,请参阅 JSR 317:Java™ 持久性 2.0
从版本 6.0 开始,当查询未返回任何实体时,将返回空列表结果。 以前返回结束流或引发异常,具体取决于 。 或者,若要还原到以前的行为,请在网关后添加 以筛选出空列表。 在空列表处理是下游逻辑一部分的应用程序中,它需要额外的配置。 有关可能的空列表处理选项,请参阅拆分器丢弃通道。Jpa.retrievingGateway()nullrequiresReplyfilter
JPA 出站网关示例
本节包含使用更新出站网关和检索出站网关的各种示例:
使用实体类进行更新
在以下示例中,通过将实体类用作 JPA 定义参数来保留更新的出站网关:org.springframework.integration.jpa.test.entity.Student
<int-jpa:updating-outbound-gateway request-channel="entityRequestChannel" reply-channel="entityResponseChannel" entity-class="org.springframework.integration.jpa.test.entity.Student" entity-manager="em"/>这是出站网关的请求通道。 它类似于 的属性。channeloutbound-channel-adapter
这就是网关与出站适配器的不同之处。 这是从 JPA 操作接收答复的通道。 但是,如果您对收到的答复不感兴趣,只想执行操作,则使用 JPA 是合适的选择。 在此示例中,我们使用实体类,回复是作为 JPA 操作的结果而创建或合并的实体对象。outbound-channel-adapter
使用 JPQL 更新
以下示例使用 Java 持久性查询语言 (JPQL) 更新实体, 这要求使用更新的出站网关:
<int-jpa:updating-outbound-gateway request-channel="jpaqlRequestChannel" reply-channel="jpaqlResponseChannel" jpa-query="update Student s set s.lastName = :lastName where s.rollNumber = :rollNumber" entity-manager="em"> <int-jpa:parameter name="lastName" expression="payload"/> <int-jpa:parameter name="rollNumber" expression="headers['rollNumber']"/></int-jpa:updating-outbound-gateway>网关执行的 JPQL 查询。 由于我们使用了更新出站网关,因此只有 JPQL 查询才是明智的选择。updatedelete
当您发送有效负载的消息时,该有效负载还包含使用值调用的标头,具有指定卷号的学生的姓氏将更新为消息有效负载中的值。 使用更新网关时,返回值始终为整数值,表示受 JPA QL 执行影响的记录数。StringrollNumberlong
使用 JPQL 检索实体
以下示例使用检索出站网关和 JPQL 从数据库中检索(选择)一个或多个实体:
<int-jpa:retrieving-outbound-gateway request-channel="retrievingGatewayReqChannel" reply-channel="retrievingGatewayReplyChannel" jpa-query="select s from Student s where s.firstName = :firstName and s.lastName = :lastName" entity-manager="em"> <int-jpa:parameter name="firstName" expression="payload"/> <int-jpa:parameter name="lastName" expression="headers['lastName']"/></int-jpa:outbound-gateway>使用 检索实体id-expression
以下示例使用检索出站网关从数据库中检索(查找)一个且仅一个实体: 这是评估的结果。 是消息的类。id-expressionprimaryKeyid-expressionentityClasspayload
<int-jpa:retrieving-outbound-gateway request-channel="retrievingGatewayReqChannel" reply-channel="retrievingGatewayReplyChannel" id-expression="payload.id" entity-manager="em"/>使用命名查询进行更新
使用命名查询与直接使用 JPQL 查询基本相同。 不同之处在于改用属性,如以下示例所示:named-query
<int-jpa:updating-outbound-gateway request-channel="namedQueryRequestChannel" reply-channel="namedQueryResponseChannel" named-query="updateStudentByRollNumber" entity-manager="em"> <int-jpa:parameter name="lastName" expression="payload"/> <int-jpa:parameter name="rollNumber" expression="headers['rollNumber']"/></int-jpa:outbound-gateway>