18. JPA Support

Spring Integration's JPA (Java Persistence API) module provides components for performing various database operations using JPA. The following components are provided:

These components can be used to perform select, create, update and delete operations on the targeted database by sending/receiving messages to them.

The above operations can be performed using either one of the of the following:

In the following sections we will describe each of these components in more detail.

18.1 Supported Persistence Providers

The Spring Integration JPA support is being tested using the following persistence providers:

  • Hibernate

  • OpenJPA

  • EclipseLink

18.2 Java Implementation

Each of the provided components will use the org.springframework.integration.jpa.core.JpaExecutor class which in turn will use an implementation of the org.springframework.integration.jpa.core.JpaOperations interface. JpaOperations operates like a typical Data Access Object (Dao) and provides methods such as find, persiste, executeUpdate etc. For most use-cases the provided default implementation org.springframework.integration.jpa.core.DefaultJpaOperations should be sufficient. Nevertheless, the provided components allow you to optionally specify your own implementation in case you require custom behavior.

That means, that for initializing a JpaExecutor you have to use one of 3 available constructors that accept either a:

  • EntityManagerFactory

  • EntityManager or

  • JpaOperations

Example

The following example of an JPA Outbound Gateway is purely configured through Java. In typical usage scenarios you will most likely prefer the XML Namespace Support described further below. However, the example illustrates how the classes are wired up.

First, we instantiate a JpaExecutor using an EntityManager as constructor argument. The JpaExecutor is then used as constructor argument for the o.s.i.jpa.outbound.JpaOutboundGateway and the JpaOutboundGateway will be passed as constructor argument in to the EventDrivenConsumer.

<bean id="jpaExecutor" class="o.s.i.jpa.core.JpaExecutor">
	<constructor-arg name="entityManager" ref="entityManager"/>
	<property name="entityClass"        value="o.s.i.jpa.test.entity.StudentDomain"/>
	<property name="jpaQuery"           value="select s from Student s where s.id = :id"/>
	<property name="expectSingleResult" value="true"/>
	<property name="jpaParameters" >
		<util:list>
			<bean class="org.springframework.integration.jpa.support.JpaParameter">
				<property name="name"       value="id"/>
				<property name="expression" value="payload"/>
			</bean>
		</util:list>
	</property>
</bean>

<bean id="jpaOutboundGateway" class="o.s.i.jpa.outbound.JpaOutboundGateway">
	<constructor-arg ref="jpaExecutor"/>
	<property        name="gatewayType"   value="RETRIEVING"/>
	<property        name="outputChannel" ref="studentReplyChannel"/>
</bean>

<bean id="getStudentEndpoint"
	  class="org.springframework.integration.endpoint.EventDrivenConsumer">
	<constructor-arg name="inputChannel" ref="getStudentChannel"/>
	<constructor-arg name="handler"      ref="jpaOutboundGateway"/>
</bean>

When using XML Namespace Support the unlerying parser classes will instantiate the classes for you. Thus, you typically don't have to deal with the inner workings but in case you need to debug the JPA Components of your message flow, or if you need to provide customization understanding it will be helpful. The following section will describe how to use the XML Namespace Support to configure the Jpa components.

18.3 Common Configuration Attributes

Certain configuration parameters are shared among all Jpa components and are described below:

auto-startup

Lifecycle attribute signaling if this component should be started during Application Context startup. Defaults to true. Optional.

id

Identifies the underlying Spring bean definition, which is an instance of either EventDrivenConsumer or PollingConsumer. Optional.

entity-manager-factory

The reference to the JPA Entity Manager Factory that will be used by the adapter to create the EntityManager. Either this attribute or the enity-manager attribute or the jpa-operations attribute must be provided.

entity-manager

The reference to the JPA Entity Manager that will be used by the component. Either this attribute or the enity-manager-factory attribute or the jpa-operations attribute must be provided.

[Note]Note
Usually your Spring Application Context only defines a JPA Entity Manager Factory and the EntityManager is injected using the @PersistenceContext annotation. This however is not applicable in regards to the Spring Integration Jpa components. Usually, injecting the JPA Entity Manager Factory will be best but in case you want to inject an EntityManager explicitly, you have to define a SharedEntityManagerBean. For more information, please see the relevant JavaDoc.
<bean id="entityManager"
      class="org.springframework.orm.jpa.support.SharedEntityManagerBean">
    <property name="entityManagerFactory" ref="entityManagerFactoryBean" />
</bean>

jpa-operations

Reference to a bean implementing the JpaOperations interface. In rare cases it might be advisable to provide your own implementation of the JpaOperations interface, instead of relying on the default implementation org.springframework.integration.jpa.core.DefaultJpaOperations. As JpaOperations wraps the necessay datasource; the JPA Entity Manager or JPA Entity Manager Factory must not be provided, if the jpa-operations attribute is used.

entity-class

The reference to the JPA Persistence Entity. The exact sermantics of this attribute vary, depending wether we are performing a persist/update operation or wether we are retrieving objects from the database.

When retrieving data, you can specify the entity-class attribute to indicate that you would like to retrieve objects of this type from the database. In that case you must not define any of the query attributes ( jpa-query, native-query or named-query )

When persisting data, the entity-class attribute will indicate the type of object to persist. If not specified (for persist operations) the entity class will be automatically retrieved from the Message's payload.

jpa-query

Defines the JPA query (Java Persistence Query Language) to be used.

native-query

Defines the native SQL query to be used.

named-query

This attribute refers to a named query. A named query can either be defined in Native SQL or JPAQL but the underlying JPA persistence provider handles that distinction internally.

18.4 Providing JPA Query Parameters

For providing parameters, the JPA Parameter XML sub-element can be used. It provides a mechanism to provide parameters for the queries that are either based on the Java Persistence Query Language (JPQL) or native SQL queries. Parameters can also be provided for Named Queries.

Expression based Parameters

<int-jpa:parameter expression="payload.name" name="firstName"/>

Value based Parameters

<int-jpa:parameter name="name" type="java.lang.String" value="myName"/>

Positional Parameters

<int-jpa:parameter expression="payload.name"/>
<int-jpa:parameter type="java.lang.Integer" value="21"/>

18.5 Transaction Handling

TBD

18.6 Outbound Channel Adapter

The JPA Outbound channel adapter allows you to accept messages via a request channel. The payload can either be used to persist it to the database or the Message payload and its headers can be used as parameters for a defined JPQL query to be executed. In the following sub sections we shall see what those possible ways of performing these operations are.

18.6.1 Using an Entity Class

The XML snippet below shows how we can use 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 will be sent to the JPA Outbound Channel Adapter.

2

The fully qualified name of the entity class that would be 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 that needs to be done by the adapter, valid values are PERSIST, MERGE and DELETE. The default value is MERGE.

4

The JPA entity manager to be used.

As we can see above these 4 attributes of the outbound-channel-adapter are all we need to configure it to accept entities over the input channel and process them to PERSIST,MERGE or DELETE it from the underlying data source.

18.6.2 Using JPA Query Language (JPA QL)

We have seen in the above sub section how to perform a PERSIST action using an entity We will now see how to use the outbound channel adapter which uses JPA QL (Java Persistence API Query Language)

<int-jpa:outbound-channel-adapter channel="jpaQlChannel" 1
  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 being sent to the outbound channel adapter

2

The JPA QL that needs to be executed.This query may contain parameters that will be evaluated using the jpa:param child tag.

3

The entity manager used by the adapter to perform the JPA operations

4

This sub element, one for each parameter will be used to evaluate the value of the parameter names specified in the JPA QL specified in the query attribute

We will see a bit more about the param sub element here. The param sub element accepts an attribute name which corresponds to the named parameter specified in the provided JPA QL (point 2 in the above mentioned sample). The value of the parameter can either be static or can be derived using an expression. The static value and the expression to derive the value is specified using the value and the expression attributes respectively. These attributes are mutually exclusive.

If the value attribute is specified we 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.

<int-jpa:outbound-channel-adapter ... >
    <int-jpa:param name="level" value="2" type="java.lang.Integer"/>
    <int-jpa:param name="name" expression="payload['name']"/>
</int-jpa:outbound-channel-adapter>

As seen in the above snippet, it is perfectly valid to use multiple param sub elements within an outbound channel adapter tag and derive some parameters using expressions and some with static value. However, care should be taken not to specify the same parameter name multiple times, and, provide one param sub element for each named parameter specified in the JPA query. For example, we are specifying two parameters level and name where level attribute is a static value of type java.lang.Integer, where as the name attribute is derived from the payload of the message

[Note]Note
Though specifying select is valid for JPA QL, it makes no sense as outbound channel adapters will not be returning any result. If you want to select some values, consider using the outbound gateway instead.

18.6.3 Using Native query

In this section we will see how to use native queries to perform the operations using JPA outbound channel adapter. Using native queries is similar to using JPA QL, except that the query specified here is a native database query. By choosing native queries we lose the database vendor independence which we get using JPA QL.

One of the things we can achieve using native queries is to perform database inserts, which is not possible using JPA QL (To perform inserts we send JPA entities to the channel adapter as we have seen earlier). Below is a small xml fragment that demonstrates the use of native query to insert values in a table. Please note that we have only mentioned the important attributes below. All other attributes like channel, entity-manager and the param sub element have the same semantics as when we use JPA QL.

[Important]Important
Please be aware that named parameters may not be supported by your JPA provider in conjunction with native SQL queries. While they work fine using Hibernate, OpenJPA and EclipseLink do NOT support them: https://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."

<int-jpa:outbound-channel-adapter channel="nativeQlChannel"
  query="insert into STUDENT_TABLE(FIRST_NAME,LAST_UPDATED) values (:lastName,:lastUpdated)" 1
  native-query="true" 2
  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 that will be executed by this outbound channel adapter

2

The flag that indicates whether the specified query is a JPA QL or a native query. Not specifying this attribute or setting it's value to false will lead to the value specified in the query attribute to be evaluated as a JPA QL.

TODO: The above xml declaration will change and the native-query may no longer hold the flag but will become the native query itself. However, the document is as per the code currently. Change this when the changes are made in the code

18.6.4 Using Named query

We will now see how to use named queries after seeing using entity, JPA QL and native query in previous sub sections. Using named query is also very similar to using JPA QL or a native query, except that we specify a named query instead of a query. Before we go further and see the xml fragment for the declaration of the outbound-channel-adapter, we will see how named JPA named queries are defined.

In our case, if we have an entity called Student, then we have the following in the class to define two named queries selectStudent and updateStudent. Below is a way to define named queries using annotations

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

...

You can alternatively use the orm.xml to define named queries as seen below

<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 seen how we can define named queries using annotations or using orm.xml, we will now see a small xml fragment for defining an outbound-channel-adapter using named query

<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

We have now seen four possible ways of defining the outbound-channel-adapter in the previous sub sections. We will now see how to use outbound gateways in the next section.

18.6.5 Configuration Parameter Reference

<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"
  use-payload-as-parameter-source="true">
	<int:poller/>
	<int-jpa:transactional/>
	<int-jpa:parameter/>
</int-jpa:outbound-channel-adapter>

18.7 Outbound Gateway

Outbound gateways are similar to outbound channel adapter except that it can also be used to get a result on the reply channel after performing the given JPA operation . If you are directly referring to this outbound gateway section, we would recommend you to first go through the outbound channel adapter section given above, as most of the common concepts have been explained there.

Simlar to the outbound-channel-adapter, we can use

  • Entity classes

  • JPA Query Language (JPQL)

  • Native query

  • Named query

for performing various JPA operations. We will be seeing each of these in the following four sub sections. Since we are assuming you are already familiar with the outbound-channel-adapter, we will only discuss portions relevant to outbound-gateway.

18.7.1 Difference between UPDATING and RETRIEVING gateway

Before we continue, let us see what are the types of JPA outbound gateways. JPA outbound gateways are either UPDATING or RETRIEVING types. The type is specified using the gateway-type attribute. If this attribute is not specified, the gateway type if defaulted to an UPDATING type of the gateway

Whenever the gateway intends to perform an action that updates or deletes some records in the database using JPA, you need to use an UPDATING type of gateway. If an entity is used, a merged/persisted entity is returned. In any other case the number of records affected (updated or deleted) are returned.

If the calling application requires to select/retrieve some data from the database using outbound-gateway, we use a RETRIEVING type of gateway. With a RETRIEVING type of gateway, we can use either of JPA QL, Named Query or Native Query for selecting the data and retrieving the result.

18.7.2 Using Entity class

We will see below an xml snippet that declares an outbound-gateway using entity class.

<int-jpa:outbound-gateway	request-channel="entityRequestChannel" 1
    reply-channel="entityResponseChannel" 2
    entity-class="org.springframework.integration.jpa.test.entity.Student"
    entity-manager="em"
    gateway-type="UPDATING" 3/>

1

This is the request channel for the outbound gateway, this 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 of the JPA operation performed is received. If,however, you are not interested in the reply received and just want to perform the operation, then outbound-channel-adapter is an appropriate choice. In above case, where we are using entity class, the reply will be the entity object that was created/merged as a result of the JPA operation performed.

3

Valid values are RETRIEVING and UPDATING. This attribute is optional and in it's absence the value defaults to UPDATING.

18.7.3 Using JPA Query Language (JPA QL)

We will now see how we can use JPA QL in an outbound gateway. Below xml snippet is a declaration of the outbound-gateway.

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

1

The JPA QL that will be executed by the gateway. Since the gateway-type is UPDATING, only update and delete JPA QL will be acceptable.

On sending a message with string payload and containing a header rollNumber with a long value, the last name of the student with the provided roll number is updated to the value provided in the message payload. When using a gateway of type UPDATING, the return value is always an integer value which denotes the number of records affected by execution of the JPA QL.

TODO: Show one RETRIEVING type of gateway sample, also somewhere in the manual show a sample usage of BeanPropertyParameterSource

18.7.4 Using Native query

Using native query is very identical to using the JPA QL except that the query attribute now holds the native SQL Query and an additional attribute native-query set to true (TODO: Change this description once the change for the attribute names is done)

18.7.5 Using Named query

Using named query is also very similar to using a JPA QL except that we have the named-query attribute as seen in the xml snippet below

<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:outbound-gateway>

18.7.6 Configuration Parameter Reference

<int-jpa:outbound-gateway request-channel=""
  auto-startup="true"
  entity-class=""
  entity-manager=""
  entity-manager-factory=""
  expect-single-result="false"
  gateway-type="UPDATING"
  id=""
  jpa-operations=""
  jpa-query=""
  max-number-of-results=""
  named-query=""
  native-query=""
  order=""
  parameter-source-factory=""
  persist-mode="MERGE"
  reply-channel=""
  reply-timeout=""
  use-payload-as-parameter-source="true">
  <int:poller></int:poller>
  <int-jpa:transactional transaction-manager="transactionManager"/>
  <int-jpa:parameter expression="" name="" type="" value=""/>
</int-jpa:outbound-gateway>

18.8 Inbound Channel Adapter

An inbound channel adapter is used to execute a select query over the database using JPA QL and return the result. The message payload will be either a single entity or a List of entities. Below is a sample xml snippet that shows a sample usage of inbound-channel-adapter.

<int-jpa:inbound-channel-adapter channel="inboundChannelAdapterOne" 1
                    entity-manager="em" 2
                    auto-startup="true" 3
                    query="select s from Student s" 4
                    expect-single-result="true" 5
                    max-rows="1" 6
                    delete-after-poll="true"> 7
    <int:poller fixed-rate="2000" >
      <int:transactional propagation="REQUIRED" transaction-manager="transactionManager"/>
    </int:poller>
</int-jpa:inbound-channel-adapter>
              

1

The channel over which the inbound-channel-adapter will put the messages with the payload received after executing the provided JPA QL in the query attribute.

2

The EntityManager instance that will be used to perform the required JPA operations.

3

Attribute signalling if the component should be automatically started on startup of the Application Context. The value defaults to true

4

The JPA QL that needs to be executed and whose result needs to be sent out as the payload of the message

5

The attribute that tells if the executed JPA QL gives a single entity in the result or a List of entities. If the value is set to true, the single entity retrieved is sent as the payload of the message. If, however, multiple results are returned after setting this to true, a MessageHandlingException is thrown. The value defaults to false

6

The maximum number of rows that should be retrieved on execution of the given JPA QL. Relevant only if the query can potentially receive multiple records

7

Set this value to true if you want to delete the rows received after execution of the query. Please ensure that the component is operating as part of a transaction. Otherwise, you may encounter an Exception such as: java.lang.IllegalArgumentException: Removing a detached instance ...

18.8.1 Configuration Parameter Reference

<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:transactional/>

</int-jpa:inbound-channel-adapter>

18.9 JPA Adapters XML attributes quick reference

18.9.1 Common XML Attributes

Table 18.1. Common Attributes

NameDescriptionMandatory
entity-managerAn instance of javax.persistence.EntityManager that will be used to perform the JPA operations. No
entity-manager-factoryAn instance of javax.persistence.EntityManagerFactory that will be used to obtain an instance of javax.persistence.EntityManager that will perform the JPA operations. Either of entity-manager-factory and entity-manager attributes is mandatory. No
jpa-operations An implementation of org.springframework.integration.jpa.core.JpaOperations that would be used to perform the JPA operations. It is recommended not to provide an implementation of your own but use the default org.springframework.integration.jpa.core.DefaultJpaOperations implementation. No
queryThe JPA QL that needs to be executed by this adapterNo
native-query The boolean flag that indicates that the string value given in query attribute is a native query. By default the value is false (TODO: change this after the changes are made in adapter) No
named-queryThe Named JPA QL that needs to be executed by this adapterNo

18.9.2 Outbound adapter/gateway XML Attributes

Table 18.2. Outbound adapter/gateway Attributes

NameDescriptionMandatory
channel The channel over which the outbound adapter will receive messages for performing the desired operation. This attribute is relevant for outbound-channel-adapter only. Yes
request-channel The channel over which the outbound gateway will receive messages for performing the desired operation. This attribute is relevant for outbound-gateway only. This attribute is similar to channel attribute of the outbound-channel-adapter Yes
entity-class The fully qualified name of the entity class of the entities that would be sent to this adapter to perform JPA Operation using entity. The attributes entity-class, query and named-query are mutually exclusive. No
reply-channel The channel over which the gateway will send the response after performing the required JPA operation. This attribute is relevant for outbound-gateway only. If this attribute is not defined, the request message must have a replyChannel header No
persist-mode Accepts one of the PERSIST,MERGE or DELETE. Indicates the operation that the adapter needs to perform. Relevant only if an entity is being used for JPA operations. Ignored if JPA QL, named query or native query is provided. Defaults to MERGE No
gateway-type Valid values are UPDATING and RETRIEVING, the value defaults to UPDATING. The difference between these types of gateways is explained earlier in the manual. This attribute is relevant to outbound-gateway only No
parameter-source-factory An instance of org.springframework.integration.jpa.support.parametersource.ParameterSourceFactory that will be used to get an instance of org.springframework.integration.jpa.support.parametersource.ParameterSource which will be used to resolve the values of the parameters provided in the query. Ignored if operations are done using JPA entity. If a param sub element is used, the factory must be of type org.springframework.integration.jpa.support.parametersource.ExpressionEvaluatingParameterSourceFactory No

18.9.3 Inbound adapter XML Attributes

Table 18.3. 

NameDescriptionMandatory
channel The channel over which the adapter will send a message with the payload that was received after performing desired the JPA operation Yes
delete-after-poll A boolean flag that indicates whether the records selected are to be deleted after they are being polled by the adapter. By default the value is false, that is, the records will not be deleted. Please ensure that the component is operating as part of a transaction. Otherwise, you may encounter an Exception such as: java.lang.IllegalArgumentException: Removing a detached instance ... No
delete-per-row A boolean flag that indicates whether the records can be deleted in bulk or are deleted one record at a time. By default the value is false, that is, the records are bulk deleted No
max-rows This non zero, non negative integer value tells the adapter not to select more than given 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. No
expect-single-result A boolean flag indicates whether the select operation gives a single result or a List or results. If this flag is set to true, the single entity selected is sent as the payload of the message. If however, multiple entities are selected, an exception is thrown. If false, the List of entities is being sent as the payload of the message. Even a single entity will be sent a List or size one when the value is set to false. By default the value is false No

[Note]Note

You can find more samples for using spring integration's JPA adapter at:

https://github.com/SpringSource/spring-integration-samples/tree/master/basic/jpa