INT-3333: Return empty list as is from DB gateway (#3907)
* INT-3333: Return empty list as is from DB gateway Fixes https://jira.spring.io/browse/INT-3333 In `JdbcOutboundGateway` and `JpaOutboundGateway` the empty result list is treated as "no reply" and therefore `null` is returned cause the flow to stop at this point. It is better to return such a result as is and the target application to decided what to do with it, e.g. a `discardChannel` on a downstream splitter configuration. NOTE: the `MongoDbOutboundGateway` doesn't treat an empty result as a `null` * * Fix language in docs Co-authored-by: Gary Russell <grussell@vmware.com> Co-authored-by: Gary Russell <grussell@vmware.com>
This commit is contained in:
@@ -365,6 +365,10 @@ It can also have a `SqlParameterSourceFactory` injected to control the binding o
|
||||
Starting with the version 4.2, the `request-prepared-statement-setter` attribute is available on the `<int-jdbc:outbound-gateway>` as an alternative to `request-sql-parameter-source-factory`.
|
||||
It lets you specify a `MessagePreparedStatementSetter` bean reference, which implements more sophisticated `PreparedStatement` preparation before its execution.
|
||||
|
||||
Starting with the version 6.0, the `JdbcOutboundGateway` returns an empty list result as is instead of converting it to `null` as it was before with the meaning "no reply".
|
||||
This caused an extra configuration in applications where handling of empty lists is a part of downstream logic.
|
||||
See <<./splitter.adoc#split-stream-and-flux,Splitter Discard Channel>> for possible empty list handling option.
|
||||
|
||||
See <<jdbc-outbound-channel-adapter>> for more information about `MessagePreparedStatementSetter`.
|
||||
|
||||
[[jdbc-message-store]]
|
||||
|
||||
@@ -1011,10 +1011,89 @@ public class JpaJavaApplication {
|
||||
[[jpa-retrieving-outbound-gateway]]
|
||||
==== Retrieving Outbound Gateway
|
||||
|
||||
The following example shows all the attributes that you can set on a retrieving outbound gateway and describes the key attributes:
|
||||
The following example demonstrates how to configure a retrieving outbound gateway:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
[source, java, role="primary"]
|
||||
.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 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"));
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
[source, kotlin, role="secondary"]
|
||||
.Kotlin DSL
|
||||
----
|
||||
@Bean
|
||||
fun retrievingGatewayFlow() =
|
||||
integrationFlow {
|
||||
handle(Jpa.retrievingGateway(this.entityManagerFactory)
|
||||
.jpaQuery("from Student s where s.id = :id")
|
||||
.expectSingleResult(true)
|
||||
.parameterExpression("id", "payload"))
|
||||
channel { queue("retrieveResults") }
|
||||
}
|
||||
----
|
||||
[source, java, role="secondary"]
|
||||
.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 s where s.id = :id");
|
||||
executor.setJpaParameters(Collections.singletonList(new JpaParameter("id", null, "payload")));
|
||||
jpaExecutor.setExpectSingleResult(true);
|
||||
return executor;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ServiceActivator(channel = "jpaRetrievingChannel")
|
||||
public MessageHandler jpaOutbound() {
|
||||
JpaOutboundGateway adapter = new JpaOutboundGateway(jpaExecutor());
|
||||
adapter.setOutputChannelName("retrieveResults");
|
||||
adapter.setGatewayType(OutboundGatewayType.RETRIEVING);
|
||||
return adapter;
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
[source, xml, role="secondary"]
|
||||
.XML
|
||||
----
|
||||
<int-jpa:retrieving-outbound-gateway request-channel=""
|
||||
auto-startup="true"
|
||||
@@ -1075,86 +1154,6 @@ Version 3.0 introduced this attribute.
|
||||
Optional.
|
||||
====
|
||||
|
||||
The remaining attributes are described earlier in this chapter.
|
||||
See <<jpaInboundChannelAdapterParameters>> and <<jpaOutboundChannelAdapterParameters>>.
|
||||
|
||||
==== Configuring with Java Configuration
|
||||
|
||||
The following Spring Boot application shows an example of how configure the outbound adapter with Java:
|
||||
|
||||
====
|
||||
[source, 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 s where s.id = :id");
|
||||
executor.setJpaParameters(Collections.singletonList(new JpaParameter("id", null, "payload")));
|
||||
jpaExecutor.setExpectSingleResult(true);
|
||||
return executor;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ServiceActivator(channel = "jpaRetrievingChannel")
|
||||
public MessageHandler jpaOutbound() {
|
||||
JpaOutboundGateway adapter = new JpaOutboundGateway(jpaExecutor());
|
||||
adapter.setOutputChannelName("retrieveResults");
|
||||
adapter.setGatewayType(OutboundGatewayType.RETRIEVING);
|
||||
return adapter;
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
==== Configuring with the Java DSL
|
||||
|
||||
The following Spring Boot application shows an example of how to configure the outbound adapter with the Java DSL:
|
||||
|
||||
====
|
||||
[source, 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 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"));
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
[IMPORTANT]
|
||||
====
|
||||
When you choose to delete entities upon retrieval, and you have retrieved a collection of entities, by default, entities are deleted on a per-entity basis.
|
||||
@@ -1171,6 +1170,12 @@ It does not cascade to related entities.`"
|
||||
For more information, see https://jcp.org/en/jsr/detail?id=317[JSR 317: Java™ Persistence 2.0]
|
||||
====
|
||||
|
||||
NOTE: Starting with version 6.0, the `Jpa.retrievingGateway()` returns an empty list result when there are no entities returned by the query.
|
||||
Previously `null` was returned ending the flow, or throwing an exception, depending on `requiresReply`.
|
||||
Or, to revert to the previous behavior, add a `filter` after the gateway to filter out empty lists.
|
||||
It requires extra configuration in applications where empty list handling is a part of the downstream logic.
|
||||
See <<./splitter.adoc#split-stream-and-flux,Splitter Discard Channel>> for possible empty list handling options.
|
||||
|
||||
[[outboundGatewaySamples]]
|
||||
==== JPA Outbound Gateway Samples
|
||||
|
||||
|
||||
@@ -100,6 +100,9 @@ See <<./gateway.adoc#async-gateway, Asynchronous Gateway>> for more information.
|
||||
|
||||
The `integrationGlobalProperties` bean is now declared by the framework as an instance of `org.springframework.integration.context.IntegrationProperties` instead of the previously deprecated `java.util.Properties`.
|
||||
|
||||
Message handlers which produce a collection as a reply (e.g. `JpaOutboundGateway`, `JdbcOutboundGateway` and other DB-based gateways) now return an empty result list if no records are returned by the query.
|
||||
Previously, `null` was returned ending the flow, or throwing an exception, depending on `requiresReply`.
|
||||
|
||||
[[x6.0-rmi]]
|
||||
=== RMI Removal
|
||||
|
||||
|
||||
Reference in New Issue
Block a user