[[jpa-outbound-gateways]]
= Outbound Gateways

The JPA inbound channel adapter lets you poll a database to retrieve one or more JPA entities.
The retrieved data is consequently used to start a Spring Integration flow that uses the retrieved data as message payload.

Additionally, you can use JPA outbound channel adapters at the end of your flow in order to persist data, essentially stopping the flow at the end of the persistence operation.

However, how can you execute JPA persistence operations in the middle of a flow? For example, you may have business data that you are processing in your Spring Integration message flow and that you would like to persist, yet you still need to use other components further downstream.
Alternatively, instead of polling the database using a poller, you need to execute JPQL queries and actively retrieve data, which is then processed in subsequent components within your flow.

This is where JPA Outbound Gateways come into play.
They give you the ability to persist data as well as retrieving data.
To facilitate these uses, Spring Integration provides two types of JPA outbound gateways:

* Updating outbound gateway
* Retrieving outbound gateway

Whenever the outbound gateway is used to perform an action that saves, updates, or solely deletes some records in the database, you need to use an updating outbound gateway.
If, for example, you use an `entity` to persist it, a merged and persisted entity is returned as a result.
In other cases, the number of records affected (updated or deleted) is returned instead.

When retrieving (selecting) data from the database, we use a retrieving outbound gateway.
With a retrieving outbound gateway, we can use JPQL, Named Queries (native or JPQL-based), or Native Queries (SQL) for selecting the data and retrieving the results.

An updating outbound gateway is functionally similar to an outbound channel adapter, except that an updating outbound gateway sends a result to the gateway's reply channel after performing the JPA operation.

A retrieving outbound gateway is similar to an inbound channel adapter.

NOTE: We recommend you first read the xref:jpa/outbound-channel-adapter.adoc[Outbound Channel Adapter] section and the xref:jpa/inbound-channel-adapter.adoc[Inbound Channel Adapter] sections earlier in this chapter, as most of the common concepts are explained there.

This similarity was the main factor to use the central `JpaExecutor` class to unify common functionality as much as possible.

Common for all JPA outbound gateways and similar to the `outbound-channel-adapter`, we can use for performing various JPA operations:

* Entity classes
* JPA Query Language (JPQL)
* Native query
* Named query

For configuration examples see xref:jpa/outbound-gateways.adoc#outboundGatewaySamples[JPA Outbound Gateway Samples].

[[jpa-outbound-gateway-common-parameters]]
== Common Configuration Parameters

JPA Outbound Gateways always have access to the Spring Integration `Message` as input.
Consequently, the following parameters is available:

`parameter-source-factory`::
An instance of `o.s.i.jpa.support.parametersource.ParameterSourceFactory` used to get an instance of `o.s.i.jpa.support.parametersource.ParameterSource`.
The `ParameterSource` is used to resolve the values of the parameters provided in the query.
If you perform operations by using a JPA entity, the `parameter-source-factory` attribute is ignored.
The `parameter` sub-elements are mutually exclusive with the `parameter-source-factory` and they have to be configured on the provided `ParameterSourceFactory`.
Optional.

`use-payload-as-parameter-source`::
If set to `true`, the payload of the `Message` is used as a source for parameters.
If set to `false`, the entire `Message` is available as a source for parameters.
If no JPA Parameters are passed in, this property defaults to `true`.
This means that, if you use a default `BeanPropertyParameterSourceFactory`, the bean properties of the payload are used as a source for parameter values for the JPA query.
However, if JPA Parameters are passed in, this property, by default, evaluates to `false`.
The reason is that JPA Parameters let you provide SpEL Expressions.
Therefore, it is highly beneficial to have access to the entire `Message`, including the headers.
Optional.

[[jpa-updating-outbound-gateway]]
== Updating Outbound Gateway

The following listing shows all the attributes that you can set on an updating-outbound-gateway and describes the key attributes:

[source,xml]
----
<int-jpa:updating-outbound-gateway request-channel=""  <1>
    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=""  <2>
    reply-timeout=""  <3>
    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>
----

<1> The channel from which the outbound gateway receives messages for performing the desired operation.
This attribute is similar to the `channel` attribute of the `outbound-channel-adapter`.
Optional.
<2> The channel to which the gateway send the response after performing the required JPA operation.
If this attribute is not defined, the request message must have a `replyChannel` header.
Optional.
<3> Specifies the time the gateway waits to send the result to the reply channel.
Only applies when the reply channel itself might block the send operation (for example, a bounded `QueueChannel` that is currently full).
The value is specified in milliseconds.
Optional.

The remaining attributes are described earlier in this chapter.
See xref:jpa/inbound-channel-adapter.adoc#jpaInboundChannelAdapterParameters[Configuration Parameter Reference] and xref:jpa/outbound-channel-adapter.adoc#jpaOutboundChannelAdapterParameters[Configuration Parameter Reference].

[[configuring-with-java-configuration]]
== 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)
@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 = "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;
    }

}
----

[[configuring-with-the-java-dsl]]
== Configuring with the Java DSL

The following Spring Boot application shows an example of how to configure the outbound adapter using 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 updatingGatewayFlow() {
        return f -> f
                .handle(Jpa.updatingGateway(this.entityManagerFactory),
                        e -> e.transactional(true))
                .channel(c -> c.queue("updateResults"));
    }

}
----


[[jpa-retrieving-outbound-gateway]]
== Retrieving Outbound Gateway

The following example demonstrates how to configure a retrieving outbound gateway:

[tabs]
======
Java DSL::
+
[source, java, role="primary"]
----
@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"));
    }

}
----

Kotlin DSL::
+
[source, kotlin, role="secondary"]
----
@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") }
    }
----

Java::
+
[source, java, role="secondary"]
----
@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;
    }

}
----

XML::
+
[source, xml, role="secondary"]
----
<int-jpa:retrieving-outbound-gateway request-channel=""
    auto-startup="true"
    delete-after-poll="false"
    delete-in-batch="false"
    entity-class=""
    id-expression=""              <1>
    entity-manager=""
    entity-manager-factory=""
    expect-single-result="false"  <2>
    id=""
    jpa-operations=""
    jpa-query=""
    max-results=""                <3>
    max-results-expression=""     <4>
    first-result=""               <5>
    first-result-expression=""    <6>
    named-query=""
    native-query=""
    order=""
    parameter-source-factory=""
    reply-channel=""
    reply-timeout=""
    use-payload-as-parameter-source="true">
    <int:poller></int:poller>
    <int-jpa:transactional/>

    <int-jpa:parameter name="" type="" value=""/>
    <int-jpa:parameter name="" expression=""/>
</int-jpa:retrieving-outbound-gateway>
----
======

<1> (Since Spring Integration 4.0) The SpEL expression that determines the `primaryKey` value for `EntityManager.find(Class entityClass, Object primaryKey)` method against the `requestMessage` as the root object of evaluation context.
The `entityClass` argument is determined from the `entity-class` attribute, if present.
Otherwise, it is determined from the `payload` class.
All other attributes are disallowed if you use `id-expression`.
Optional.
<2> A boolean flag indicating whether the select operation is expected to return a single result or a `List` of results.
If this flag is set to `true`, a single entity is sent as the payload of the message.
If multiple entities are returned, an exception thrown.
If `false`, the `List` of entities sent as the payload of the message.
It defaults to `false`.
Optional.
<3> This non-zero, non-negative integer value tells the adapter not to select more than the specified number of rows on execution of the select operation.
By default, if this attribute is not set, all the possible records are selected by given query.
This attribute is mutually exclusive with `max-results-expression`.
Optional.
<4> An expression that can be used to find the maximum number of results in a result set.
It is mutually exclusive with `max-results`.
Optional.
<5> This non-zero, non-negative integer value tells the adapter the first record from which results are to be retrieved.
This attribute is mutually exclusive with `first-result-expression`.
Version 3.0 introduced this attribute.
Optional.
<6> This expression is evaluated against the message, to find the position of the first record in the result set.
This attribute is mutually exclusive to `first-result`.
Version 3.0 introduced this attribute.
Optional.

[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.
This may cause performance issues.

Alternatively, you can set attribute `deleteInBatch` to `true`, which performs a batch delete.
However, the limitation of doing so is that cascading deletes are not supported.

JSR 317: Java™ Persistence 2.0 states in chapter 4.10, "`Bulk Update and Delete Operations`" that:

"`A delete operation only applies to entities of the specified class and its subclasses.
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 xref:splitter.adoc#split-stream-and-flux[Splitter Discard Channel] for possible empty list handling options.

[[outboundGatewaySamples]]
== JPA Outbound Gateway Samples

This section contains various examples of using the updating outbound gateway and the retrieving outbound gateway:

[[update-by-using-an-entity-class]]
=== Update by Using an Entity Class

In the following example, an updating outbound gateway is persisted by using the `org.springframework.integration.jpa.test.entity.Student` entity class as a JPA defining parameter:

[source,xml]
----
<int-jpa:updating-outbound-gateway request-channel="entityRequestChannel"  <1>
    reply-channel="entityResponseChannel"  <2>
    entity-class="org.springframework.integration.jpa.test.entity.Student"
    entity-manager="em"/>
----

<1> This is the request channel for the outbound gateway.
It is similar to the `channel` attribute of the `outbound-channel-adapter`.
<2> This is where a gateway differs from an outbound adapter.
This is the channel over which the reply from the JPA operation is received.
If, however, you are not interested in the reply received and want only to perform the operation, using a JPA `outbound-channel-adapter` is the appropriate choice.
In this example, where we use an entity class, the reply is the entity object that was created or merged as a result of the JPA operation.

[[update-using-jpql]]
=== Update using JPQL

The following example updates an entity by using the Java Persistence Query Language (JPQL),
which mandates using an updating outbound gateway:

[source,xml]
----
<int-jpa:updating-outbound-gateway request-channel="jpaqlRequestChannel"
  reply-channel="jpaqlResponseChannel"
  jpa-query="update Student s set s.lastName = :lastName where s.rollNumber = :rollNumber"  <1>
  entity-manager="em">
    <int-jpa:parameter name="lastName" expression="payload"/>
    <int-jpa:parameter name="rollNumber" expression="headers['rollNumber']"/>
</int-jpa:updating-outbound-gateway>
----

<1> The JPQL query that the gateway executes.
Since we used updating outbound gateway, only `update` and `delete` JPQL queries would be sensible choices.

When you send a message with a `String` payload that also contains a header called `rollNumber` with a `long` value, the last name of the student with the specified roll number is updated to the value in the message payload.
When using an updating gateway, the return value is always an integer value, which denotes the number of records affected by execution of the JPA QL.

[[retrieving-an-entity-using-jpql]]
=== Retrieving an Entity using JPQL

The following example uses a retrieving outbound gateway and JPQL to retrieve (select) one or more entities from the database:

[source,xml]
----
<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>
----

[[retrieving-an-entity-by-using-id-expression]]
=== Retrieving an Entity by Using `id-expression`

The following example uses a retrieving outbound gateway with `id-expression` to retrieve (find) one and only one entity from the database:
The `primaryKey` is the result of `id-expression` evaluation.
The `entityClass` is a class of Message `payload`.

[source,xml]
----
<int-jpa:retrieving-outbound-gateway
	request-channel="retrievingGatewayReqChannel"
    reply-channel="retrievingGatewayReplyChannel"
    id-expression="payload.id"
    entity-manager="em"/>
----

[[update-using-a-named-query]]
=== Update using a Named Query

Using a named query is basically the same as using a JPQL query directly.
The difference is that the `named-query` attribute is used instead, as the following example shows:

[source,xml]
----
<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>
----

NOTE: You can find a complete sample application that uses Spring Integration's JPA adapter https://github.com/spring-projects/spring-integration-samples/tree/main/basic/jpa[here].
