INT-2582 - Refactor JPA Outbound Gateway

* Split JPA Outbound Gateway into "updating-outbound-gateway" and "retrieving-outbound-gateway"
* INT-2573 Need a mechanism to have the max-number-of-results attribute for JPA outbound gateway and inbound adapters
* Cleanup
* Add more comments
* Add unit tests

For reference see: https://jira.springsource.org/browse/INT-2573

INT-2582 - Fixed comment + fixed spelling in assertion

INT-2582 - Throw MessagingException instead of MessageHandlingException

INT-2582 - Code review changes

* Changed bean naming strategy for jpaExecutor to `Id of JPA Component + ".jpaExecutor"`
* added tests
* changed `delete-per-row` XML attribute to `delete-in-batch`
* changed `deletePerRow` property in JpaExecutor to `deleteInBatch`
* delete-in-batch defaults to "false" to follow the default in Spring Data JPA
* fixed junit test
This commit is contained in:
Gunnar Hillert
2012-05-31 17:20:32 -04:00
committed by Gary Russell
parent d21b1f1316
commit 94e5e45fd9
17 changed files with 653 additions and 402 deletions

View File

@@ -16,9 +16,7 @@
package org.springframework.integration.jpa.config.xml;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.xml.AbstractConsumerEndpointParser;
@@ -29,43 +27,24 @@ import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
/**
* The Parser for JPA Outbound Gateway, the MessageHandler implementation is same as the
* outbound chanel adapter and hence we extend the class and setting the few additional
* attributes that we wish to in the MessageSource
* The Abstract Parser for the JPA Outbound Gateways.
*
* @author Amol Nayak
* @author Gunnar Hillert
*
* @since 2.2
*
* @see RetrievingJpaOutboundGatewayParser
* @see UpdatingJpaOutboundGatewayParser
*
*/
public class JpaOutboundGatewayParser extends AbstractConsumerEndpointParser {
public abstract class AbstractJpaOutboundGatewayParser extends AbstractConsumerEndpointParser {
@Override
protected BeanDefinitionBuilder parseHandler(Element gatewayElement, ParserContext parserContext) {
final BeanDefinitionBuilder jpaExecutorBuilder = JpaParserUtils.getJpaExecutorBuilder(gatewayElement, parserContext);
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaExecutorBuilder, gatewayElement, "persist-mode");
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaExecutorBuilder, gatewayElement, "parameter-source-factory");
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaExecutorBuilder, gatewayElement, "use-payload-as-parameter-source");
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaExecutorBuilder, gatewayElement, "delete-after-poll");
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaExecutorBuilder, gatewayElement, "delete-per-row");
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaExecutorBuilder, gatewayElement, "expect-single-result");
final BeanDefinition jpaExecutorBuilderBeanDefinition = jpaExecutorBuilder.getBeanDefinition();
final String jpaExecutorBeanName = BeanDefinitionReaderUtils.generateBeanName(jpaExecutorBuilderBeanDefinition, parserContext.getRegistry());
parserContext.registerBeanComponent(new BeanComponentDefinition(jpaExecutorBuilderBeanDefinition, jpaExecutorBeanName));
final BeanDefinitionBuilder jpaOutboundGatewayBuilder = BeanDefinitionBuilder
.genericBeanDefinition(JpaOutboundGatewayFactoryBean.class);
jpaOutboundGatewayBuilder.addConstructorArgReference(jpaExecutorBeanName);
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaOutboundGatewayBuilder, gatewayElement, "gateway-type");
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaOutboundGatewayBuilder, gatewayElement, "reply-timeout");
final String replyChannel = gatewayElement.getAttribute("reply-channel");

View File

@@ -19,7 +19,6 @@ import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.xml.AbstractPollingInboundChannelAdapterParser;
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
@@ -30,30 +29,32 @@ import org.w3c.dom.Element;
* The JPA Inbound Channel adapter parser
*
* @author Amol Nayak
* @since 2.2
* @author Gunnar Hillert
*
* @since 2.2
*
*/
public class JpaInboundChannelAdapterParser extends AbstractPollingInboundChannelAdapterParser{
protected BeanMetadataElement parseSource(Element element,
ParserContext parserContext) {
final BeanDefinitionBuilder jpaExecutorBuilder = JpaParserUtils.getJpaExecutorBuilder(element, parserContext);
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaExecutorBuilder, element, "delete-after-poll");
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaExecutorBuilder, element, "delete-per-row");
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaExecutorBuilder, element, "expect-single-result");
final BeanDefinition jpaExecutorBuilderBeanDefinition = jpaExecutorBuilder.getBeanDefinition();
final String jpaExecutorBeanName = BeanDefinitionReaderUtils.generateBeanName(jpaExecutorBuilderBeanDefinition, parserContext.getRegistry());
parserContext.registerBeanComponent(new BeanComponentDefinition(jpaExecutorBuilderBeanDefinition, jpaExecutorBeanName));
protected BeanMetadataElement parseSource(Element element, ParserContext parserContext) {
final BeanDefinitionBuilder jpaPollingChannelAdapterBuilder = BeanDefinitionBuilder
.genericBeanDefinition(JpaPollingChannelAdapter.class);
final BeanDefinitionBuilder jpaExecutorBuilder = JpaParserUtils.getJpaExecutorBuilder(element, parserContext);
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaExecutorBuilder, element, "max-number-of-results");
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaExecutorBuilder, element, "delete-after-poll");
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaExecutorBuilder, element, "delete-in-batch");
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaExecutorBuilder, element, "expect-single-result");
final BeanDefinition jpaExecutorBuilderBeanDefinition = jpaExecutorBuilder.getBeanDefinition();
final String channelAdapterId = this.resolveId(element, jpaPollingChannelAdapterBuilder.getRawBeanDefinition(), parserContext);
final String jpaExecutorBeanName = channelAdapterId + ".jpaExecutor";
parserContext.registerBeanComponent(new BeanComponentDefinition(jpaExecutorBuilderBeanDefinition, jpaExecutorBeanName));
jpaPollingChannelAdapterBuilder.addConstructorArgReference(jpaExecutorBeanName);
return jpaPollingChannelAdapterBuilder.getBeanDefinition();

View File

@@ -19,7 +19,7 @@ import org.springframework.integration.config.xml.AbstractIntegrationNamespaceHa
/**
* The namespace handler for the JPA namespace
*
*
* @author Amol Nayak
* @author Gunnar Hillert
* @since 2.2
@@ -33,6 +33,7 @@ public class JpaNamespaceHandler extends AbstractIntegrationNamespaceHandler {
public void init() {
this.registerBeanDefinitionParser("inbound-channel-adapter", new JpaInboundChannelAdapterParser());
this.registerBeanDefinitionParser("outbound-channel-adapter", new JpaOutboundChannelAdapterParser());
this.registerBeanDefinitionParser("outbound-gateway", new JpaOutboundGatewayParser());
this.registerBeanDefinitionParser("updating-outbound-gateway", new UpdatingJpaOutboundGatewayParser());
this.registerBeanDefinitionParser("retrieving-outbound-gateway", new RetrievingJpaOutboundGatewayParser());
}
}

View File

@@ -19,7 +19,6 @@ import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser;
@@ -52,19 +51,19 @@ public class JpaOutboundChannelAdapterParser extends AbstractOutboundChannelAdap
@Override
protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) {
final BeanDefinitionBuilder jpaOutboundChannelAdapterBuilder = BeanDefinitionBuilder.genericBeanDefinition(JpaOutboundGatewayFactoryBean.class);
final BeanDefinitionBuilder jpaExecutorBuilder = JpaParserUtils.getJpaExecutorBuilder(element, parserContext);
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaExecutorBuilder, element, "persist-mode");
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaExecutorBuilder, element, "parameter-source-factory");
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaExecutorBuilder, element, "use-payload-as-parameter-source");
final BeanDefinition jpaExecutorBuilderBeanDefinition = jpaExecutorBuilder.getBeanDefinition();
final String jpaExecutorBeanName = BeanDefinitionReaderUtils.generateBeanName(jpaExecutorBuilderBeanDefinition, parserContext.getRegistry());
final String channelAdapterId = this.resolveId(element, jpaOutboundChannelAdapterBuilder.getRawBeanDefinition(), parserContext);
final String jpaExecutorBeanName = channelAdapterId + ".jpaExecutor";
parserContext.registerBeanComponent(new BeanComponentDefinition(jpaExecutorBuilderBeanDefinition, jpaExecutorBeanName));
final BeanDefinitionBuilder jpaOutboundChannelAdapterBuilder = BeanDefinitionBuilder.genericBeanDefinition(JpaOutboundGatewayFactoryBean.class);
jpaOutboundChannelAdapterBuilder.addConstructorArgReference(jpaExecutorBeanName);
jpaOutboundChannelAdapterBuilder.addPropertyValue("producesReply", Boolean.FALSE);

View File

@@ -37,8 +37,8 @@ import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
/**
* The common method for generating the BeanDefinition for the common MessageHandler
* is implemented in this class
* Contains various utility methods for parsing JPA Adapter specific namesspace
* elements and generation the respective {@link BeanDefinition}s.
*
* @author Amol Nayak
* @author Gunnar Hillert
@@ -59,15 +59,15 @@ public final class JpaParserUtils {
* Create a new {@link BeanDefinitionBuilder} for the class {@link JpaExecutor}.
* Initialize the wrapped {@link JpaExecutor} with common properties.
*
* @param element Must not be Null
* @param parserContext Must not be Null
* @param element Must not be null
* @param parserContext Must not be null
* @return The BeanDefinitionBuilder for the JpaExecutor
*/
public static BeanDefinitionBuilder getJpaExecutorBuilder(final Element element,
final ParserContext parserContext) {
final ParserContext parserContext) {
Assert.notNull(element, "The provided element must not be Null.");
Assert.notNull(parserContext, "The provided parserContext must not be Null.");
Assert.notNull(element, "The provided element must not be null.");
Assert.notNull(parserContext, "The provided parserContext must not be null.");
final Object source = parserContext.extractSource(element);
@@ -127,12 +127,44 @@ public final class JpaParserUtils {
}
/**
* @param jpaComponent
* @param parserContext
* Create a new {@link BeanDefinitionBuilder} for the class {@link JpaExecutor}
* that is specific for JPA Outbound Gateways.
*
* Initializes the wrapped {@link JpaExecutor} with common properties.
* Delegates to {@link JpaParserUtils#getJpaExecutorBuilder(Element, ParserContext)}
*
* @param element Must not be null
* @param parserContext Must not be null
*
* @return The BeanDefinitionBuilder for the JpaExecutor
*/
public static BeanDefinitionBuilder getOutboundGatewayJpaExecutorBuilder(final Element gatewayElement,
final ParserContext parserContext) {
final BeanDefinitionBuilder jpaExecutorBuilder = JpaParserUtils.getJpaExecutorBuilder(gatewayElement, parserContext);
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaExecutorBuilder, gatewayElement, "parameter-source-factory");
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaExecutorBuilder, gatewayElement, "use-payload-as-parameter-source");
return jpaExecutorBuilder;
}
/**
* Create a {@link ManagedList} of {@link BeanDefinition}s containing parsed
* JPA Parameters.
*
* @param jpaComponent Must not be null
* @param parserContext Must not be null
*
* @return {@link ManagedList} of {@link BeanDefinition}s
*/
public static ManagedList<BeanDefinition> getJpaParameterBeanDefinitions(
Element jpaComponent, ParserContext parserContext) {
Assert.notNull(jpaComponent, "The provided element must not be null.");
Assert.notNull(parserContext, "The provided parserContext must not be null.");
final ManagedList<BeanDefinition> parameterList = new ManagedList<BeanDefinition>();
final List<Element> parameterChildElements = DomUtils

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.jpa.config.xml;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
import org.springframework.integration.jpa.support.OutboundGatewayType;
import org.w3c.dom.Element;
/**
* The Parser for the Retrieving Jpa Outbound Gateway.
*
* @author Amol Nayak
* @author Gunnar Hillert
*
* @since 2.2
*
*/
public class RetrievingJpaOutboundGatewayParser extends AbstractJpaOutboundGatewayParser {
@Override
protected BeanDefinitionBuilder parseHandler(Element gatewayElement, ParserContext parserContext) {
final BeanDefinitionBuilder jpaOutboundGatewayBuilder = super.parseHandler(gatewayElement, parserContext);
final BeanDefinitionBuilder jpaExecutorBuilder = JpaParserUtils.getOutboundGatewayJpaExecutorBuilder(gatewayElement, parserContext);
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaExecutorBuilder, gatewayElement, "max-number-of-results");
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaExecutorBuilder, gatewayElement, "delete-after-poll");
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaExecutorBuilder, gatewayElement, "delete-in-batch");
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaExecutorBuilder, gatewayElement, "expect-single-result");
final BeanDefinition jpaExecutorBuilderBeanDefinition = jpaExecutorBuilder.getBeanDefinition();
final String gatewayId = this.resolveId(gatewayElement, jpaOutboundGatewayBuilder.getRawBeanDefinition(), parserContext);
final String jpaExecutorBeanName = gatewayId + ".jpaExecutor";
parserContext.registerBeanComponent(new BeanComponentDefinition(jpaExecutorBuilderBeanDefinition, jpaExecutorBeanName));
jpaOutboundGatewayBuilder.addConstructorArgReference(jpaExecutorBeanName);
jpaOutboundGatewayBuilder.addPropertyValue("gatewayType", OutboundGatewayType.RETRIEVING);
return jpaOutboundGatewayBuilder;
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.jpa.config.xml;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
import org.springframework.integration.jpa.support.OutboundGatewayType;
import org.w3c.dom.Element;
/**
* The Parser for Updating JPA Outbound Gateway.
*
* @author Amol Nayak
* @author Gunnar Hillert
*
* @since 2.2
*
*/
public class UpdatingJpaOutboundGatewayParser extends AbstractJpaOutboundGatewayParser {
@Override
protected BeanDefinitionBuilder parseHandler(Element gatewayElement, ParserContext parserContext) {
final BeanDefinitionBuilder jpaOutboundGatewayBuilder = super.parseHandler(gatewayElement, parserContext);
final BeanDefinitionBuilder jpaExecutorBuilder = JpaParserUtils.getOutboundGatewayJpaExecutorBuilder(gatewayElement, parserContext);
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaExecutorBuilder, gatewayElement, "persist-mode");
final BeanDefinition jpaExecutorBuilderBeanDefinition = jpaExecutorBuilder.getBeanDefinition();
final String gatewayId = this.resolveId(gatewayElement, jpaOutboundGatewayBuilder.getRawBeanDefinition(), parserContext);
final String jpaExecutorBeanName = gatewayId + ".jpaExecutor";
parserContext.registerBeanComponent(new BeanComponentDefinition(jpaExecutorBuilderBeanDefinition, jpaExecutorBeanName));
jpaOutboundGatewayBuilder.addConstructorArgReference(jpaExecutorBeanName);
jpaOutboundGatewayBuilder.addPropertyValue("gatewayType", OutboundGatewayType.UPDATING);
return jpaOutboundGatewayBuilder;
}
}

View File

@@ -19,10 +19,11 @@ import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Query;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.integration.Message;
import org.springframework.integration.MessageHandlingException;
import org.springframework.integration.MessagingException;
import org.springframework.integration.jpa.support.JpaParameter;
import org.springframework.integration.jpa.support.PersistMode;
import org.springframework.integration.jpa.support.parametersource.BeanPropertyParameterSourceFactory;
@@ -75,7 +76,7 @@ public class JpaExecutor implements InitializingBean {
private volatile ParameterSource parameterSource;
private volatile boolean deleteAfterPoll = false;
private volatile boolean deletePerRow = false;
private volatile boolean deleteInBatch = false;
private volatile boolean expectSingleResult = false;
@@ -87,8 +88,6 @@ public class JpaExecutor implements InitializingBean {
*/
private volatile Boolean usePayloadAsParameterSource = null;
//~~~~Constructors~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* Constructor taking an {@link EntityManagerFactory} from which the
* {@link EntityManager} can be obtained.
@@ -133,9 +132,6 @@ public class JpaExecutor implements InitializingBean {
this.jpaOperations = jpaOperations;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
*
* Verifies and sets the parameters. E.g. initializes the to be used
@@ -278,7 +274,7 @@ public class JpaExecutor implements InitializingBean {
}
else {
throw new MessageHandlingException(requestMessage,
throw new MessagingException(requestMessage,
"The Jpa operation returned more than "
+ "1 result object but expectSingleResult was 'true'.");
}
@@ -293,14 +289,14 @@ public class JpaExecutor implements InitializingBean {
if (payload != null && this.deleteAfterPoll) {
if (payload instanceof Iterable) {
if (this.deletePerRow) {
if (this.deleteInBatch) {
this.jpaOperations.deleteInBatch((Iterable<Object>) payload);
}
else {
for (Object entity : (Iterable<?>) payload) {
this.jpaOperations.delete(entity);
}
}
else {
this.jpaOperations.deleteInBatch((Iterable<Object>) payload);
}
}
else {
this.jpaOperations.delete(payload);
@@ -354,11 +350,9 @@ public class JpaExecutor implements InitializingBean {
return payload;
}
//~~~~Setters~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* Sets the class type which is being used to poll the database or to also
* update the persistence store.
* Sets the class type which is being used for retrieving entities from the
* database.
*
* @param entityClass Must not be null.
*/
@@ -422,16 +416,24 @@ public class JpaExecutor implements InitializingBean {
}
/**
* If not set, this property default to 'true', which means that deletion
* occur on a per object basis.
* If not set, this property defaults to <code>false</code>, which means that
* deletion occurs on a per object basis if a collection of entities is being
* deleted.
*
* If set to 'false' the elements of the payload are deleted as a batch
* operation. Be aware that this exhibit issues in regards to cascaded deletes. //TODO further information needed
* If set to 'true' the elements of the payload are deleted as a batch
* operation. Be aware that this exhibits issues in regards to cascaded deletes.
*
* @param deletePerRow Defaults to 'true'.
* The specification 'JSR 317: Java Persistence API, Version 2.0' does not
* support cascaded deletes in batch operations. The specification states in
* chapter 4.10:
*
* "A delete operation only applies to entities of the specified class and
* its subclasses. It does not cascade to related entities."
*
* @param deleteInBatch Defaults to 'false' if not set.
*/
public void setDeletePerRow(boolean deletePerRow) {
this.deletePerRow = deletePerRow;
public void setDeleteInBatch(boolean deleteInBatch) {
this.deleteInBatch = deleteInBatch;
}
/**
@@ -446,16 +448,7 @@ public class JpaExecutor implements InitializingBean {
/**
*
* @param maxNumberOfResults Must not be negative.
*/
public void setMaxRows(int maxNumberOfResults) {
Assert.isTrue(maxNumberOfResults >= 0, "maxRows must not be negative.");
this.maxNumberOfResults = maxNumberOfResults;
}
/**
*
* @param parameterSourceFactory
* @param parameterSourceFactory Must not be null
*/
public void setParameterSourceFactory(
ParameterSourceFactory parameterSourceFactory) {
@@ -464,41 +457,46 @@ public class JpaExecutor implements InitializingBean {
}
/**
* Specifies the {@link ParameterSource} that would be used to provide
* additional parameters.
*
* @param parameterSource
* @param parameterSource Must not be null.
*/
public void setParameterSource(ParameterSource parameterSource) {
Assert.notNull(parameterSource, "parameterSource must not be null.");
this.parameterSource = parameterSource;
}
/**
*
* This parameter indicates that only one result object shall be returned as
* a result from the executed JPA operation. If set to <code>true</code> and
* the result list from the JPA operations contains only 1 element, then that
* 1 element is extracted and returned as payload.
*
* If the result map contains more than 1 element and
* {@link JpaExecutor#expectSingleResult} is <code>true</code>, then a
* {@link MessagingException} is thrown.
*
* If set to <code>false</code>, the complete result list is returned as the
* payload.
*
*/
public void setExpectSingleResult(boolean expectSingleResult) {
this.expectSingleResult = expectSingleResult;
}
//Exposing getters for the unit test cases
/**
* Returns the JPA Query that would be executed using the executor
* Set the max number of results to retrieve from the database. Defaults to
* 0, which means that all possible objects shall be retrieved.
*
* @param maxNumberOfResults Must not be negative.
*
* @see Query#setMaxResults(int)
*/
public String getJpaQuery() {
return jpaQuery;
public void setMaxNumberOfResults(int maxNumberOfResults) {
Assert.isTrue(maxNumberOfResults >= 0, "maxNumberOfResults must not be negative.");
this.maxNumberOfResults = maxNumberOfResults;
}
/**
* Returns the Native Query that would be executed using the executor
*/
public String getNativeQuery() {
return nativeQuery;
}
/**
* Returns the Named Query that would be executed using the executor
*/
public String getNamedQuery() {
return namedQuery;
}
}

View File

@@ -50,60 +50,7 @@
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="expect-single-result" default="false">
<xsd:annotation>
<xsd:documentation><![CDATA[
This parameter indicates that only one result object shall be
returned as a result from the executed JPA operation.
If set to 'true' and the result list from the JPA operations
contains only 1 element, then that 1 element is extracted
and returned as payload.
If the result map contains more than 1 element and
'expect-single-result' is true, then a 'MessagingException'
is thrown.
If set to 'false', the complete result list is returned
as the payload.
]]>
</xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:union memberTypes="xsd:boolean xsd:string" />
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="delete-after-poll" default="false" use="optional">
<xsd:annotation>
<xsd:documentation>
Once entities have been retrieved from the database, shall
they be removed from the database?
If instead of deleting the retrieved entities, you would
rather like to updated them, e.g. setting a flag in a
column marking the record as retrieved, please consider
using a subsequent Outbound Gateway (coupled with a payload enricher).
</xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:union memberTypes="xsd:boolean xsd:string" />
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="delete-per-row" default="false" use="optional">
<xsd:annotation>
<xsd:documentation>
If you want to automatically remove retrieved entities from
the database you can also specify using the 'delete-per-row'
attribute, whether the list of retrieved objects shall be
deleted on a 'per-object-basis (true) or whether the objects
shall be removed using a batch operation (false). The
attribute defaults to 'false'.
</xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:union memberTypes="xsd:boolean xsd:string" />
</xsd:simpleType>
</xsd:attribute>
<xsd:attributeGroup ref="commonRetrievingJpaAttributes" />
<xsd:attribute name="send-timeout" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
@@ -151,84 +98,72 @@
</xsd:element>
</xsd:sequence>
<xsd:attributeGroup ref="coreJpaComponentAttributes"/>
<xsd:attribute name="persist-mode" use="optional" default="MERGE">
<xsd:attributeGroup ref="commonUpdatingJpaAttributes"/>
<xsd:attribute name="use-payload-as-parameter-source">
<xsd:annotation>
<xsd:documentation><![CDATA[
Defines the persistence mode which is used when soley using
the entity-class. This attribute instructs to either Merge
entities, to Persist them. Furthermore and entity can also
be deleted. By Default 'MERGE' is used.
]]></xsd:documentation>
<xsd:documentation>
<![CDATA[
If set to 'true', the payload of the Message will be used
as a source for providing parameters. If false the entire
Message will be available as a source for parameters.
If no JPA Parameters are passed in, this property
will default to 'true'. This means that using a default
BeanPropertyParameterSourceFactory, the bean properties
of the payload will be used as a source for parameter
values for the to-be-executed JPA query.
However, if JPA Parameters are passed in, then this
property will by default evaluate to 'false'. JPA Parameters
allow for SpEL Expressions to be provided and therefore
it is highly beneficial to have access to the entire Message.
]]>
</xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:union memberTypes="persistMode xsd:string"/>
<xsd:union memberTypes="xsd:boolean xsd:string" />
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="use-payload-as-parameter-source">
<xsd:annotation>
<xsd:attribute name="parameter-source-factory"
type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<xsd:documentation>
<![CDATA[
If set to 'true', the payload of the Message will be used
as a source for providing parameters. If false the entire
Message will be available as a source for parameters.
If no JPA Parameters are passed in, this property
will default to 'true'. This means that using a default
BeanPropertyParameterSourceFactory, the bean properties
of the payload will be used as a source for parameter
values for the to-be-executed JPA query.
However, if JPA Parameters are passed in, then this
property will by default evaluate to 'false'. JPA Parameters
allow for SpEL Expressions to be provided and therefore
it is highly beneficial to have access to the entire Message.
]]>
Reference to a ParameterSourceFactory.
</xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:union memberTypes="xsd:boolean xsd:string" />
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="parameter-source-factory"
type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<xsd:documentation>
Reference to a ParameterSourceFactory.
</xsd:documentation>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.jpa.support.parametersource.ParameterSourceFactory" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<xsd:documentation>
Channel from which messages will be output.
When a message is sent to this channel it will
cause the query
to be executed.
</xsd:documentation>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.MessageChannel" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="order">
<xsd:annotation>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.jpa.support.parametersource.ParameterSourceFactory" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<xsd:documentation>
Specifies the order for invocation when this endpoint is connected as a
subscriber to a SubscribableChannel.
Channel from which messages will be output.
When a message is sent to this channel it will
cause the query
to be executed.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.MessageChannel" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="order">
<xsd:annotation>
<xsd:documentation>
Specifies the order for invocation when this endpoint is connected as a
subscriber to a SubscribableChannel.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:element name="outbound-gateway">
<xsd:element name="updating-outbound-gateway">
<xsd:annotation>
<xsd:documentation>
Defines the Spring Integration JPA Outbound Gateway
@@ -254,151 +189,39 @@
</xsd:element>
</xsd:sequence>
<xsd:attributeGroup ref="coreJpaComponentAttributes" />
<xsd:attribute name="gateway-type" use="optional" default="UPDATING">
<xsd:annotation>
<xsd:documentation><![CDATA[
The outbound JPA gateway basically wraps the functionality
of the 'outbound-channel-adapter' and the 'inbound-channel-adapter'.
By specifying the gateway-type, you bascially specify which
set of functionlity you would like to use. The attribute defaults
to 'UPDATING'.
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:union memberTypes="gatewayType xsd:string"/>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="persist-mode" use="optional" default="MERGE">
<xsd:annotation>
<xsd:documentation><![CDATA[
Defines the persistence mode which is used when soley using
the entity-class. This attribute instructs to either Merge
entities, to Persist them. Furthermore and entity can also
be deleted. By Default 'MERGE' is used.
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:union memberTypes="persistMode xsd:string"/>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="use-payload-as-parameter-source">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
If set to 'true', the payload of the Message will be used
as a source for providing parameters. If false the entire
Message will be available as a source for parameters.
<xsd:attributeGroup ref="commonUpdatingJpaAttributes"/>
<xsd:attributeGroup ref="commonJpaOutboundGatewayAttributes"/>
</xsd:complexType>
</xsd:element>
If no JPA Parameters are passed in, this property
will default to 'true'. This means that using a default
BeanPropertyParameterSourceFactory, the bean properties
of the payload will be used as a source for parameter
values for the to-be-executed JPA query.
<xsd:element name="retrieving-outbound-gateway">
<xsd:annotation>
<xsd:documentation>
Defines a Spring Integration JPA Outbound Gateway
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="integration:poller" minOccurs="0" maxOccurs="1" />
<xsd:element name="transactional" type="integration:transactionalType" minOccurs="0" maxOccurs="1" />
<xsd:element name="parameter" minOccurs="0" maxOccurs="unbounded" type="parameterSubElementType">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
Provides a mechanism to provide
parameters for the queries that are either based
on the Java Persistence Query Language (JPQL) or
native SQL queries.
However, if JPA Parameters are passed in, then this
property will by default evaluate to 'false'. JPA Parameters
allow for SpEL Expressions to be provided and therefore
it is highly beneficial to have access to the entire Message.
]]>
</xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:union memberTypes="xsd:boolean xsd:string" />
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="request-channel" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation>
The receiving Message Channel of this endpoint.
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.core.MessageChannel" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="reply-channel" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Message Channel to which replies should be
sent, after receiving the database response.
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.core.MessageChannel" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="reply-timeout" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
Allows you to specify how long this gateway will wait for
the reply message to be sent successfully before throwing
an exception. Keep in mind that when sending to a
DirectChannel, the invocation will occur in the sender's thread
so the failing of the send operation may be caused by other
components further downstream. By default the Gateway will
wait indefinitely. The value is specified in milliseconds.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="parameter-source-factory" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.jpa.JPAQLParameterSourceFactory" />
</tool:annotation>
</xsd:appinfo>
<xsd:documentation>
The parameter source factory that would be used for evaluating the
parameters of the response JPA QL that would be evaluated
JPA outbound gateway
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="order">
<xsd:annotation>
<xsd:documentation>
Specifies the order for invocation when this endpoint is connected as a
subscriber to a SubscribableChannel.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="max-number-of-results">
<xsd:annotation>
<xsd:documentation>
Specifies the maximum number of entities that shall be returned
by a JPA Operation. Using this attribute you basically set
the 'maxResults' property of the JPA Query object.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="expect-single-result" default="false">
<xsd:annotation>
<xsd:documentation><![CDATA[
This parameter indicates that only one result object shall be
returned as a result from the executed JPA operation.
If set to 'true' and the result list from the JPA operations
contains only 1 element, then that 1 element is extracted
and returned as payload.
If the result map contains more than 1 element and
'expect-single-result' is true, then a 'MessagingException'
is thrown.
If set to 'false', the complete result list is returned
as the payload.
]]>
</xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:union memberTypes="xsd:boolean xsd:string" />
</xsd:simpleType>
</xsd:attribute>
Parameters can also be provided for Named Queries.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
<xsd:attributeGroup ref="coreJpaComponentAttributes" />
<xsd:attributeGroup ref="commonJpaOutboundGatewayAttributes"/>
<xsd:attributeGroup ref="commonRetrievingJpaAttributes" />
</xsd:complexType>
</xsd:element>
@@ -441,6 +264,177 @@
</xsd:attribute>
</xsd:complexType>
<xsd:attributeGroup name="commonJpaOutboundGatewayAttributes">
<xsd:attribute name="use-payload-as-parameter-source">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
If set to 'true', the payload of the Message will be used
as a source for providing parameters. If false the entire
Message will be available as a source for parameters.
If no JPA Parameters are passed in, this property
will default to 'true'. This means that using a default
BeanPropertyParameterSourceFactory, the bean properties
of the payload will be used as a source for parameter
values for the to-be-executed JPA query.
However, if JPA Parameters are passed in, then this
property will by default evaluate to 'false'. JPA Parameters
allow for SpEL Expressions to be provided and therefore
it is highly beneficial to have access to the entire Message.
]]>
</xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:union memberTypes="xsd:boolean xsd:string" />
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="request-channel" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation>
The receiving Message Channel of this endpoint.
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.core.MessageChannel" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="reply-channel" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Message Channel to which replies should be
sent, after receiving the database response.
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.core.MessageChannel" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="reply-timeout" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
Allows you to specify how long this gateway will wait for
the reply message to be sent successfully before throwing
an exception. Keep in mind that when sending to a
DirectChannel, the invocation will occur in the sender's thread
so the failing of the send operation may be caused by other
components further downstream. By default the Gateway will
wait indefinitely. The value is specified in milliseconds.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="parameter-source-factory" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.jpa.JPAQLParameterSourceFactory" />
</tool:annotation>
</xsd:appinfo>
<xsd:documentation>
The parameter source factory that would be used for evaluating the
parameters of the response JPA QL that would be evaluated
JPA outbound gateway
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="order">
<xsd:annotation>
<xsd:documentation>
Specifies the order for invocation when this endpoint is connected as a
subscriber to a SubscribableChannel.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:attributeGroup>
<xsd:attributeGroup name="commonUpdatingJpaAttributes">
<xsd:attribute name="persist-mode" use="optional" default="MERGE">
<xsd:annotation>
<xsd:documentation><![CDATA[
Defines the persistence mode which is used when soley using
the entity-class. This attribute instructs to either Merge
entities, to Persist them. Furthermore and entity can also
be deleted. By Default 'MERGE' is used.
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:union memberTypes="persistMode xsd:string"/>
</xsd:simpleType>
</xsd:attribute>
</xsd:attributeGroup>
<xsd:attributeGroup name="commonRetrievingJpaAttributes">
<xsd:attribute name="max-number-of-results">
<xsd:annotation>
<xsd:documentation>
Specifies the maximum number of entities that shall be returned
by a JPA Operation. Using this attribute you basically set
the 'maxResults' property of the JPA Query object.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="expect-single-result" default="false">
<xsd:annotation>
<xsd:documentation><![CDATA[
This parameter indicates that only one result object shall be
returned as a result from the executed JPA operation.
If set to 'true' and the result list from the JPA operations
contains only 1 element, then that 1 element is extracted
and returned as payload.
If the result map contains more than 1 element and
'expect-single-result' is true, then a 'MessagingException'
is thrown.
If set to 'false', the complete result list is returned
as the payload.
]]>
</xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:union memberTypes="xsd:boolean xsd:string" />
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="delete-after-poll" default="false" use="optional">
<xsd:annotation>
<xsd:documentation>
Once entities have been retrieved from the database, shall
they be removed from the database?
If instead of deleting the retrieved entities, you would
rather like to updated them, e.g. setting a flag in a
column marking the record as retrieved, please consider
using a subsequent Outbound Gateway (coupled with a payload enricher).
</xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:union memberTypes="xsd:boolean xsd:string" />
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="delete-in-batch" default="false" use="optional">
<xsd:annotation>
<xsd:documentation>
If you want to automatically remove retrieved entities from
the database you can also specify using the 'delete-in-batch'
attribute, whether the list of retrieved objects shall be
deleted on a 'per-object-basis (false) or whether the objects
shall be removed using a batch operation (true). The
attribute defaults to 'false'.
</xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:union memberTypes="xsd:boolean xsd:string" />
</xsd:simpleType>
</xsd:attribute>
</xsd:attributeGroup>
<xsd:attributeGroup name="coreJpaComponentAttributes">
<xsd:attribute name="id" type="xsd:string" use="optional">
<xsd:annotation>
@@ -569,10 +563,5 @@
<xsd:enumeration value="PERSIST"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="gatewayType">
<xsd:restriction base="xsd:token">
<xsd:enumeration value="UPDATING"/>
<xsd:enumeration value="RETRIEVING"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:schema>

View File

@@ -41,7 +41,7 @@ public class JpaInboundChannelAdapterParserTests {
@Test
public void testJpaInboundChannelAdapterParser() throws Exception {
setUp("JpaInboundChannelAdapterParserTests.xml", getClass());
setUp("JpaInboundChannelAdapterParserTests.xml", getClass(), "jpaInboundChannelAdapter1");
final AbstractMessageChannel outputChannel = TestUtils.getPropertyValue(this.consumer, "outputChannel", AbstractMessageChannel.class);
@@ -61,7 +61,42 @@ public class JpaInboundChannelAdapterParserTests {
assertTrue(TestUtils.getPropertyValue(jpaExecutor, "expectSingleResult", Boolean.class));
}
}
@Test
public void testJpaInboundChannelAdapterParserWithMaxResults() throws Exception {
setUp("JpaInboundChannelAdapterParserTests.xml", getClass(), "jpaInboundChannelAdapter2");
final AbstractMessageChannel outputChannel = TestUtils.getPropertyValue(this.consumer, "outputChannel", AbstractMessageChannel.class);
assertEquals("out", outputChannel.getComponentName());
final JpaExecutor jpaExecutor = TestUtils.getPropertyValue(this.consumer, "source.jpaExecutor", JpaExecutor.class);
assertNotNull(jpaExecutor);
final Class<?> entityClass = TestUtils.getPropertyValue(jpaExecutor, "entityClass", Class.class);
assertEquals("org.springframework.integration.jpa.test.entity.StudentDomain", entityClass.getName());
final JpaOperations jpaOperations = TestUtils.getPropertyValue(jpaExecutor, "jpaOperations", JpaOperations.class);
assertNotNull(jpaOperations);
assertEquals(Integer.valueOf(13), TestUtils.getPropertyValue(jpaExecutor, "maxNumberOfResults", Integer.class));
}
@Test
public void testJpaExecutorBeanIdNaming() throws Exception {
this.context = new ClassPathXmlApplicationContext("JpaInboundChannelAdapterParserTests.xml", getClass());
assertNotNull(context.getBean("jpaInboundChannelAdapter1.jpaExecutor", JpaExecutor.class));
assertNotNull(context.getBean("jpaInboundChannelAdapter2.jpaExecutor", JpaExecutor.class));
}
@After
public void tearDown(){
@@ -70,9 +105,9 @@ public class JpaInboundChannelAdapterParserTests {
}
}
public void setUp(String name, Class<?> cls){
public void setUp(String name, Class<?> cls, String consumerId){
context = new ClassPathXmlApplicationContext(name, cls);
consumer = this.context.getBean("jpaInboundChannelAdapter", SourcePollingChannelAdapter.class);
consumer = this.context.getBean(consumerId, SourcePollingChannelAdapter.class);
}
}

View File

@@ -13,7 +13,7 @@
<int:channel id="out"/>
<int-jpa:inbound-channel-adapter id="jpaInboundChannelAdapter"
<int-jpa:inbound-channel-adapter id="jpaInboundChannelAdapter1"
entity-manager-factory="entityManagerFactory"
entity-class="org.springframework.integration.jpa.test.entity.StudentDomain"
expect-single-result="true"
@@ -21,4 +21,12 @@
<int:poller fixed-rate="5000"/>
</int-jpa:inbound-channel-adapter>
<int-jpa:inbound-channel-adapter id="jpaInboundChannelAdapter2"
entity-manager-factory="entityManagerFactory"
entity-class="org.springframework.integration.jpa.test.entity.StudentDomain"
max-number-of-results="13"
channel="out">
<int:poller fixed-rate="5000"/>
</int-jpa:inbound-channel-adapter>
</beans>

View File

@@ -148,6 +148,15 @@ public class JpaMessageHandlerParserTests {
}
@Test
public void testJpaExecutorBeanIdNaming() throws Exception {
setUp("JpaMessageHandlerParserTestsWithEmFactory.xml", getClass());
assertNotNull(context.getBean("jpaOutboundChannelAdapter.jpaExecutor", JpaExecutor.class));
}
@After
public void tearDown(){
if(context != null){

View File

@@ -42,8 +42,8 @@ public class JpaOutboundGatewayParserTests {
private EventDrivenConsumer consumer;
@Test
public void testJpaOutboundGatewayParser() throws Exception {
setUp("JpaOutboundGatewayParserTests.xml", getClass());
public void testRetrievingJpaOutboundGatewayParser() throws Exception {
setUp("JpaOutboundGatewayParserTests.xml", getClass(), "retrievingJpaOutboundGateway");
final AbstractMessageChannel inputChannel = TestUtils.getPropertyValue(this.consumer, "inputChannel", AbstractMessageChannel.class);
@@ -73,11 +73,66 @@ public class JpaOutboundGatewayParserTests {
assertNotNull(jpaOperations);
assertTrue(TestUtils.getPropertyValue(jpaExecutor, "expectSingleResult", Boolean.class));
final Integer maxNumberOfResults = TestUtils.getPropertyValue(jpaExecutor, "maxNumberOfResults", Integer.class);
assertEquals(Integer.valueOf(55), maxNumberOfResults);
}
@Test
public void testUpdatingJpaOutboundGatewayParser() throws Exception {
setUp("JpaOutboundGatewayParserTests.xml", getClass(), "updatingJpaOutboundGateway");
final AbstractMessageChannel inputChannel = TestUtils.getPropertyValue(this.consumer, "inputChannel", AbstractMessageChannel.class);
assertEquals("in", inputChannel.getComponentName());
final JpaOutboundGateway jpaOutboundGateway = TestUtils.getPropertyValue(this.consumer, "handler", JpaOutboundGateway.class);
final OutboundGatewayType gatewayType = TestUtils.getPropertyValue(jpaOutboundGateway, "gatewayType", OutboundGatewayType.class);
assertEquals(OutboundGatewayType.UPDATING, gatewayType);
long sendTimeout = TestUtils.getPropertyValue(jpaOutboundGateway, "messagingTemplate.sendTimeout", Long.class);
assertEquals(100, sendTimeout);
final JpaExecutor jpaExecutor = TestUtils.getPropertyValue(this.consumer, "handler.jpaExecutor", JpaExecutor.class);
assertNotNull(jpaExecutor);
final Class<?> entityClass = TestUtils.getPropertyValue(jpaExecutor, "entityClass", Class.class);
assertEquals("org.springframework.integration.jpa.test.entity.StudentDomain", entityClass.getName());
final JpaOperations jpaOperations = TestUtils.getPropertyValue(jpaExecutor, "jpaOperations", JpaOperations.class);
assertNotNull(jpaOperations);
final Boolean usePayloadAsParameterSource = TestUtils.getPropertyValue(jpaExecutor, "usePayloadAsParameterSource", Boolean.class);
assertTrue(usePayloadAsParameterSource);
final Integer order = TestUtils.getPropertyValue(jpaOutboundGateway, "order", Integer.class);
assertEquals(Integer.valueOf(2), order);
final PersistMode persistMode = TestUtils.getPropertyValue(jpaExecutor, "persistMode", PersistMode.class);
assertEquals(PersistMode.PERSIST, persistMode);
assertTrue(TestUtils.getPropertyValue(jpaExecutor, "expectSingleResult", Boolean.class));
}
@Test
public void testJpaExecutorBeanIdNaming() throws Exception {
this.context = new ClassPathXmlApplicationContext("JpaOutboundGatewayParserTests.xml", getClass());
assertNotNull(context.getBean("retrievingJpaOutboundGateway.jpaExecutor", JpaExecutor.class));
assertNotNull(context.getBean("updatingJpaOutboundGateway.jpaExecutor", JpaExecutor.class));
}
@@ -88,9 +143,9 @@ public class JpaOutboundGatewayParserTests {
}
}
public void setUp(String name, Class<?> cls) {
public void setUp(String name, Class<?> cls, String gatewayId) {
context = new ClassPathXmlApplicationContext(name, cls);
consumer = this.context.getBean("jpaOutboundGateway", EventDrivenConsumer.class);
consumer = this.context.getBean(gatewayId, EventDrivenConsumer.class);
}
}

View File

@@ -14,14 +14,24 @@
<int:channel id="in"/>
<int:channel id="out"/>
<int-jpa:outbound-gateway id="jpaOutboundGateway"
<int-jpa:retrieving-outbound-gateway id="retrievingJpaOutboundGateway"
entity-manager-factory="entityManagerFactory"
auto-startup="true"
entity-class="org.springframework.integration.jpa.test.entity.StudentDomain"
expect-single-result="true"
persist-mode="PERSIST"
gateway-type="RETRIEVING"
order="1"
max-number-of-results="55"
request-channel="in"
reply-channel="out"
reply-timeout="100"/>
<int-jpa:updating-outbound-gateway id="updatingJpaOutboundGateway"
entity-manager-factory="entityManagerFactory"
auto-startup="false"
entity-class="org.springframework.integration.jpa.test.entity.StudentDomain"
persist-mode="PERSIST"
use-payload-as-parameter-source="true"
order="2"
request-channel="in"
reply-channel="out"
reply-timeout="100"/>

View File

@@ -29,6 +29,7 @@ import org.springframework.integration.jpa.support.JpaParameter;
import org.springframework.integration.jpa.support.parametersource.ExpressionEvaluatingParameterSourceFactory;
import org.springframework.integration.jpa.test.entity.StudentDomain;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
@@ -92,7 +93,7 @@ public class JpaExecutorTests {
public void testSetMultipleQueryTypes() {
JpaExecutor executor = new JpaExecutor(mock(EntityManager.class));
executor.setJpaQuery("select s from Student s");
Assert.assertNotNull(executor.getJpaQuery());
Assert.assertNotNull(TestUtils.getPropertyValue(executor, "jpaQuery", String.class));
try {
executor.setNamedQuery("NamedQuery");
@@ -100,7 +101,8 @@ public class JpaExecutorTests {
Assert.assertEquals("You can define only one of the "
+ "properties 'jpaQuery', 'nativeQuery', 'namedQuery'", e.getMessage());
}
Assert.assertNull(executor.getNamedQuery());
Assert.assertNull(TestUtils.getPropertyValue(executor, "namedQuery"));
try {
executor.setNativeQuery("select * from Student");
@@ -108,11 +110,11 @@ public class JpaExecutorTests {
Assert.assertEquals("You can define only one of the "
+ "properties 'jpaQuery', 'nativeQuery', 'namedQuery'", e.getMessage());
}
Assert.assertNull(executor.getNativeQuery());
Assert.assertNull(TestUtils.getPropertyValue(executor, "nativeQuery"));
executor = new JpaExecutor(mock(EntityManager.class));
executor.setNamedQuery("NamedQuery");
Assert.assertNotNull(executor.getNamedQuery());
Assert.assertNotNull(TestUtils.getPropertyValue(executor, "namedQuery", String.class));
try {
executor.setJpaQuery("select s from Student s");
@@ -120,7 +122,7 @@ public class JpaExecutorTests {
Assert.assertEquals("You can define only one of the "
+ "properties 'jpaQuery', 'nativeQuery', 'namedQuery'", e.getMessage());
}
Assert.assertNull(executor.getJpaQuery());
Assert.assertNull(TestUtils.getPropertyValue(executor, "jpaQuery"));
}
@@ -204,6 +206,20 @@ public class JpaExecutorTests {
return executor;
}
@Test
public void testNegativeMaxNumberOfResults() throws Exception {
final JpaExecutor jpaExecutor = new JpaExecutor(mock(EntityManager.class));
try {
jpaExecutor.setMaxNumberOfResults(-10);
} catch (IllegalArgumentException e) {
Assert.assertEquals("maxNumberOfResults must not be negative.", e.getMessage());
return;
}
Assert.fail("Was expecting an IllegalStateException to be thrown.");
}
}

View File

@@ -178,7 +178,7 @@ public class JpaPollingChannelAdapterTests {
final JpaExecutor jpaExecutor = new JpaExecutor(entityManager);
jpaExecutor.setJpaQuery("from Student");
jpaExecutor.setMaxRows(1);
jpaExecutor.setMaxNumberOfResults(1);
final JpaPollingChannelAdapter jpaPollingChannelAdapter = new JpaPollingChannelAdapter(jpaExecutor);
@@ -350,7 +350,7 @@ public class JpaPollingChannelAdapterTests {
final JpaExecutor jpaExecutor = new JpaExecutor(jpaOperations);
jpaExecutor.setJpaQuery("from Student s");
jpaExecutor.setDeleteAfterPoll(true);
jpaExecutor.setDeletePerRow(true);
jpaExecutor.setDeleteInBatch(false);
final JpaPollingChannelAdapter jpaPollingChannelAdapter = new JpaPollingChannelAdapter(jpaExecutor);

View File

@@ -19,7 +19,7 @@ import junit.framework.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.MessageHandlingException;
import org.springframework.integration.MessagingException;
import org.springframework.integration.jpa.test.JpaTestUtils;
import org.springframework.integration.jpa.test.entity.StudentDomain;
import org.springframework.test.annotation.DirtiesContext;
@@ -71,7 +71,7 @@ public class JpaOutboundGatewayTests {
public void getStudentWithException() {
try {
studentService.getStudentWithException(1001L);
} catch (MessageHandlingException e) {
} catch (MessagingException e) {
Assert.assertEquals("The Jpa operation returned more than 1 result object but expectSingleResult was 'true'.",
e.getMessage());