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:
Entity classes
Java Persistence Query Language (JPQL) for update, select and delete (inserts are not supported by JPQL)
Native query
Named query
In the following sections we will describe each of these components in more detail.
The Spring Integration JPA support is being tested using the following persistence providers:
Hibernate
OpenJPA
EclipseLink
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.
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 | |
---|---|
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.
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"/>
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.
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" entity-class="org.springframework.integration.jpa.test.entity.Student" persist-mode="PERSIST" entity-manager="em"/ >
The channel over which a valid JPA entity will be sent to the JPA Outbound Channel Adapter. | |
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. | |
The operation that needs to be done by the adapter, valid values are PERSIST, MERGE and DELETE. The default value is MERGE. | |
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.
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" 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>
The input channel over which the message is being sent to the outbound channel adapter | |
The JPA QL that needs to be executed.This query may contain parameters that will be evaluated using the jpa:param child tag. | |
The entity manager used by the adapter to perform the JPA operations | |
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 | |
---|---|
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. |
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 | |
---|---|
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)" native-query="true" 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>
The native query that will be executed by this outbound channel adapter | |
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
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" 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>
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.
<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>
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.
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.
We will see below an xml snippet that declares an outbound-gateway using entity class.
<int-jpa:outbound-gateway request-channel="entityRequestChannel" reply-channel="entityResponseChannel" entity-class="org.springframework.integration.jpa.test.entity.Student" entity-manager="em" gateway-type="UPDATING" />
This is the request channel for the outbound gateway, this is similar to the channel attribute of the outbound-channel-adapter | |
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. | |
Valid values are RETRIEVING and UPDATING. This attribute is optional and in it's absence the value defaults to UPDATING. |
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" 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>
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 BeanPropertyParameterSourceUsing 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)
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>
<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>
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" entity-manager="em" auto-startup="true" query="select s from Student s" expect-single-result="true" max-rows="1" delete-after-poll="true"> <int:poller fixed-rate="2000" > <int:transactional propagation="REQUIRED" transaction-manager="transactionManager"/> </int:poller> </int-jpa:inbound-channel-adapter>
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. | |
The EntityManager instance that will be used to perform the required JPA operations. | |
Attribute signalling if the component should be automatically started on startup of the Application Context. The value defaults to true | |
The JPA QL that needs to be executed and whose result needs to be sent out as the payload of the message | |
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 | |
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 | |
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 ... |
<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>
Table 18.1. Common Attributes
Name | Description | Mandatory |
---|---|---|
entity-manager | An instance of javax.persistence.EntityManager that will be used to perform the JPA operations. | No |
entity-manager-factory | An 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 |
query | The JPA QL that needs to be executed by this adapter | No |
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-query | The Named JPA QL that needs to be executed by this adapter | No |
Table 18.2. Outbound adapter/gateway Attributes
Name | Description | Mandatory |
---|---|---|
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 |
Table 18.3.
Name | Description | Mandatory |
---|---|---|
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 | |
---|---|
You can find more samples for using spring integration's JPA adapter at: https://github.com/SpringSource/spring-integration-samples/tree/master/basic/jpa |