This version is still in development and is not considered stable yet. For the latest stable version, please use Spring Integration 6.4.1!

Outbound Channel Adapter

The JPA outbound channel adapter lets you accept messages over a request channel. The payload can either be used as the entity to be persisted or used with the headers in the parameter expressions for a JPQL query. The following sections cover the possible ways of performing these operations.

Using an Entity Class

The following XML configures the outbound channel adapter to persist an entity to the database:

<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 The channel over which a valid JPA entity is sent to the JPA outbound channel adapter.
2 The fully qualified name of the entity class accepted by the adapter to be persisted in the database. You can actually leave off this attribute in most cases as the adapter can determine the entity class automatically from the Spring Integration message payload.
3 The operation to be done by the adapter. The valid values are PERSIST, MERGE, and DELETE. The default value is MERGE.
4 The JPA entity manager to be used.

These four attributes of the outbound-channel-adapter configure it to accept entities over the input channel and process them to PERSIST, MERGE, or DELETE the entities from the underlying data source.

As of Spring Integration 3.0, payloads to PERSIST or MERGE can also be of type java.lang.Iterable. In that case, each object returned by the Iterable is treated as an entity and persisted or merged using the underlying EntityManager. Null values returned by the iterator are ignored.
Starting with version 5.5.4, the JpaOutboundGateway, with a JpaExecutor configured with PersistMode.DELETE, can accept an Iterable payload to perform a batch removal persistent operation for the provided entities.

Using JPA Query Language (JPA QL)

The previous section showed how to perform a PERSIST action by using an entity. This section shows how to use an outbound channel adapter with JPA QL.

The following XML configures the outbound channel adapter to persist an entity to the database:

<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 The input channel over which the message is sent to the outbound channel adapter.
2 The JPA QL to execute. This query may contain parameters that are evaluated by using the parameter element.
3 The entity manager used by the adapter to perform the JPA operations.
4 The elements (one for each parameter) used to define the value of the parameter names for the JPA QL specified in the query attribute.

The parameter element accepts an attribute whose name corresponds to the named parameter specified in the provided JPA QL (point 2 in the preceding example). The value of the parameter can either be static or be derived by using an expression. The static value and the expression to derive the value are specified using the value and expression attributes, respectively. These attributes are mutually exclusive.

If the value attribute is specified, you can provide an optional type attribute. The value of this attribute is the fully qualified name of the class whose value is represented by the value attribute. By default, the type is assumed to be a java.lang.String. The following example shows how to define a JPA parameter:

<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>

As the preceding example shows, you can use multiple parameter elements within an outbound channel adapter element and define some parameters by using expressions and others with static values. However, take care not to specify the same parameter name multiple times. You should provide one parameter element for each named parameter specified in the JPA query. For example, we specify two parameters: level and name. The level attribute is a static value of type java.lang.Integer, while the name attribute is derived from the payload of the message.

Though specifying select is valid for JPA QL, it makes no sense to do so. Outbound channel adapters do not return any result. If you want to select some values, consider using the outbound gateway instead.

Using Native Queries

This section describes how to use native queries to perform operations with the JPA outbound channel adapter. Using native queries is similar to using JPA QL, except that the queries are native database queries. By using native queries, we lose database vendor independence, which we get using JPA QL.

One of the things we can achieve by using native queries is to perform database inserts, which is not possible with JPA QL. (To perform inserts, we send JPA entities to the channel adapter, as described earlier). Below is a small xml fragment that demonstrates the use of native query to insert values in a table.

Named parameters may not be supported by your JPA provider in conjunction with native SQL queries. While they work fine with Hibernate, OpenJPA and EclipseLink do not support them. See issues.apache.org/jira/browse/OPENJPA-111. Section 3.8.12 of the JPA 2.0 spec states: “Only positional parameter binding and positional access to result items may be portably used for native queries.”

The following example configures an outbound-channel-adapter with a native query:

<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 The native query executed by this outbound channel adapter.

Note that the other attributes (such as channel and entity-manager) and the parameter element have the same semantics as they do for JPA QL.

Using Named Queries

Using named queries is similar to using JPA QL or a native query, except that we specify a named query instead of a query. First, we cover how to define a JPA named query. Then we cover how to declare an outbound channel adapter to work with a named query. If we have an entity called Student, we can use annotations on the Student class to define two named queries: selectStudent and updateStudent. The following example shows how to do so:

@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 {

...
}

Alternatively, you can use orm.xml to define named queries as the following example shows:

<entity-mappings ...>
    ...
    <named-query name="selectStudent">
        <query>select s from Student s where s.lastName = 'Last One'</query>
    </named-query>
</entity-mappings>

Now that we have shown how to define named queries by using annotations or by using orm.xml, we now show a small XML fragment that defines an outbound-channel-adapter by using a named query, as the following example shows:

<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 The named query that we want the adapter to execute when it receives a message over the channel.

Configuration Parameter Reference

The following listing shows all the attributes that you can set on an outbound channel adapter:

<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 Lifecycle attribute signaling whether this component should start during application context startup. It defaults to true. Optional.
2 The channel from which the outbound adapter receives messages for performing the desired operation.
3 The fully qualified name of the entity class for the JPA Operation. The entity-class, query, and named-query attributes are mutually exclusive. Optional.
4 An instance of jakarta.persistence.EntityManager used to perform the JPA operations. Optional.
5 An instance of jakarta.persistence.EntityManagerFactory used to obtain an instance of jakarta.persistence.EntityManager, which performs the JPA operations. Optional.
6 An implementation of org.springframework.integration.jpa.core.JpaOperations used to perform the JPA operations. We recommend not providing an implementation of your own but using the default org.springframework.integration.jpa.core.DefaultJpaOperations implementation. You can use any one of the entity-manager, entity-manager-factory, or jpa-operations attributes. Optional.
7 The JPA QL to be executed by this adapter. Optional.
8 The named query that needs to be executed by this adapter. Optional.
9 The native query to be executed by this adapter. You can use any one of the jpa-query, named-query, or native-query attributes. Optional.
10 The order for this consumer when multiple consumers are registered, thereby managing load-balancing and failover. It defaults to Ordered.LOWEST_PRECEDENCE. Optional.
11 An instance of o.s.i.jpa.support.parametersource.ParameterSourceFactory used to get an instance of o.s.i.jpa.support.parametersource.ParameterSource, which is used to resolve the values of the parameters in the query. Ignored if you perform operations by using a JPA entity. The parameter sub-elements are mutually exclusive with the parameter-source-factory attribute and must be configured on the provided ParameterSourceFactory. Optional.
12 Accepts one of the following: PERSIST, MERGE, or DELETE. Indicates the operation that the adapter needs to perform. Relevant only if you use an entity for JPA operations. Ignored if you provide JPA QL, a named query, or a native query. It defaults to MERGE. Optional. As of Spring Integration 3.0, payloads to persist or merge can also be of type java.lang.Iterable. In that case, each object returned by the Iterable is treated as an entity and persisted or merged by using the underlying EntityManager. Null values returned by the iterator are ignored.
13 Set this value to true if you want to flush the persistence context immediately after persist, merge, or delete operations and do not want to rely on the flushMode of the EntityManager. It defaults to false. Applies only if you did not specify the flush-size attribute. If this attribute is set to true, flush-size is implicitly set to 1, if no other value configured it.
14 Set this attribute to a value greater than '0' if you want to flush the persistence context immediately after persist, merge or delete operations and do not want to rely on the flushMode of the EntityManager. The default value is set to 0, which means "'no flush'". This attribute is geared towards messages with Iterable payloads. For instance, if flush-size is set to 3, then entityManager.flush() is called after every third entity. Furthermore, entityManager.flush() is called once more after the entire loop. If the 'flush-size' attribute specified with a value greater than '0', you need not configure the flush attribute.
15 Set this value to 'true' if you want to clear the persistence context immediately after each flush operation. The attribute’s value is applied only if the flush attribute is set to true or if the flush-size attribute is set to a value greater than 0.
16 If set to true, the payload of the message is used as a source for parameters. If set to false, however, the entire Message is available as a source for parameters. Optional.
17 Defines the transaction management attributes and the reference to the transaction manager to be used by the JPA adapter. Optional.
18 One or more parameter attributes — one for each parameter used in the query. The value or expression is evaluated to compute the value of the parameter. Optional.

Configuring with Java Configuration

The following Spring Boot application shows an example of how to configure the outbound adapter with 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

The following Spring Boot application shows an example of how to configure the outbound adapter with the 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());
    }

}