diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/IntegrationNamespaceUtils.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/IntegrationNamespaceUtils.java
index 9d322df0b2..51d7ae7543 100644
--- a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/IntegrationNamespaceUtils.java
+++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/IntegrationNamespaceUtils.java
@@ -46,22 +46,25 @@ public abstract class IntegrationNamespaceUtils {
static final String REF_ATTRIBUTE = "ref";
static final String METHOD_ATTRIBUTE = "method";
static final String ORDER = "order";
-
-
/**
- * Configures the provided bean definition builder with a property
- * value corresponding to the attribute whose name is provided if
- * that attribute is defined in the given element.
+ * Configures the provided bean definition builder with a property value
+ * corresponding to the attribute whose name is provided if that attribute
+ * is defined in the given element.
*
- * @param builder the bean definition builder to be configured
- * @param element the XML element where the attribute should be defined
- * @param attributeName the name of the attribute whose value will be
- * used to populate the property
- * @param propertyName the name of the property to be populated
+ * @param builder
+ * the bean definition builder to be configured
+ * @param element
+ * the XML element where the attribute should be defined
+ * @param attributeName
+ * the name of the attribute whose value will be used to populate
+ * the property
+ * @param propertyName
+ * the name of the property to be populated
*/
- public static void setValueIfAttributeDefined(BeanDefinitionBuilder builder,
- Element element, String attributeName, String propertyName) {
+ public static void setValueIfAttributeDefined(
+ BeanDefinitionBuilder builder, Element element,
+ String attributeName, String propertyName) {
String attributeValue = element.getAttribute(attributeName);
if (StringUtils.hasText(attributeValue)) {
builder.addPropertyValue(propertyName, attributeValue);
@@ -69,41 +72,50 @@ public abstract class IntegrationNamespaceUtils {
}
/**
- * Configures the provided bean definition builder with a property
- * value corresponding to the attribute whose name is provided if
- * that attribute is defined in the given element.
+ * Configures the provided bean definition builder with a property value
+ * corresponding to the attribute whose name is provided if that attribute
+ * is defined in the given element.
*
- *
The property name will be the camel-case equivalent of the lower
- * case hyphen separated attribute (e.g. the "foo-bar" attribute would
- * match the "fooBar" property).
+ *
+ * The property name will be the camel-case equivalent of the lower case
+ * hyphen separated attribute (e.g. the "foo-bar" attribute would match the
+ * "fooBar" property).
*
* @see Conventions#attributeNameToPropertyName(String)
*
- * @param builder the bean definition builder to be configured
- * @param element - the XML element where the attribute should be defined
- * @param attributeName - the name of the attribute whose value will be set
- * on the property
+ * @param builder
+ * the bean definition builder to be configured
+ * @param element
+ * - the XML element where the attribute should be defined
+ * @param attributeName
+ * - the name of the attribute whose value will be set on the
+ * property
*/
- public static void setValueIfAttributeDefined(BeanDefinitionBuilder builder,
- Element element, String attributeName) {
- setValueIfAttributeDefined(builder, element, attributeName,
- Conventions.attributeNameToPropertyName(attributeName));
+ public static void setValueIfAttributeDefined(
+ BeanDefinitionBuilder builder, Element element, String attributeName) {
+ setValueIfAttributeDefined(builder, element, attributeName, Conventions
+ .attributeNameToPropertyName(attributeName));
}
/**
- * Configures the provided bean definition builder with a property
- * reference to a bean. The bean reference is identified by the value
- * from the attribute whose name is provided if that attribute is
- * defined in the given element.
+ * Configures the provided bean definition builder with a property reference
+ * to a bean. The bean reference is identified by the value from the
+ * attribute whose name is provided if that attribute is defined in the
+ * given element.
*
- * @param builder the bean definition builder to be configured
- * @param element the XML element where the attribute should be defined
- * @param attributeName the name of the attribute whose value will be
- * used as a bean reference to populate the property
- * @param propertyName the name of the property to be populated
+ * @param builder
+ * the bean definition builder to be configured
+ * @param element
+ * the XML element where the attribute should be defined
+ * @param attributeName
+ * the name of the attribute whose value will be used as a bean
+ * reference to populate the property
+ * @param propertyName
+ * the name of the property to be populated
*/
- public static void setReferenceIfAttributeDefined(BeanDefinitionBuilder builder,
- Element element, String attributeName, String propertyName) {
+ public static void setReferenceIfAttributeDefined(
+ BeanDefinitionBuilder builder, Element element,
+ String attributeName, String propertyName) {
String attributeValue = element.getAttribute(attributeName);
if (StringUtils.hasText(attributeValue)) {
builder.addPropertyReference(propertyName, attributeValue);
@@ -111,34 +123,38 @@ public abstract class IntegrationNamespaceUtils {
}
/**
- * Configures the provided bean definition builder with a property
- * reference to a bean. The bean reference is identified by the value
- * from the attribute whose name is provided if that attribute is
- * defined in the given element.
+ * Configures the provided bean definition builder with a property reference
+ * to a bean. The bean reference is identified by the value from the
+ * attribute whose name is provided if that attribute is defined in the
+ * given element.
*
- *
The property name will be the camel-case equivalent of the lower
- * case hyphen separated attribute (e.g. the "foo-bar" attribute would
- * match the "fooBar" property).
+ *
+ * The property name will be the camel-case equivalent of the lower case
+ * hyphen separated attribute (e.g. the "foo-bar" attribute would match the
+ * "fooBar" property).
*
* @see Conventions#attributeNameToPropertyName(String)
*
- * @param builder the bean definition builder to be configured
- * @param element - the XML element where the attribute should be defined
- * @param attributeName - the name of the attribute whose value will be
- * used as a bean reference to populate the property
+ * @param builder
+ * the bean definition builder to be configured
+ * @param element
+ * - the XML element where the attribute should be defined
+ * @param attributeName
+ * - the name of the attribute whose value will be used as a bean
+ * reference to populate the property
*
* @see Conventions#attributeNameToPropertyName(String)
*/
- public static void setReferenceIfAttributeDefined(BeanDefinitionBuilder builder,
- Element element, String attributeName) {
+ public static void setReferenceIfAttributeDefined(
+ BeanDefinitionBuilder builder, Element element, String attributeName) {
setReferenceIfAttributeDefined(builder, element, attributeName,
Conventions.attributeNameToPropertyName(attributeName));
}
/**
- * Provides a user friendly description of an element based on its node
- * name and, if available, its "id" attribute value. This is useful for
- * creating error messages from within bean definition parsers.
+ * Provides a user friendly description of an element based on its node name
+ * and, if available, its "id" attribute value. This is useful for creating
+ * error messages from within bean definition parsers.
*/
public static String createElementDescription(Element element) {
String elementId = "'" + element.getNodeName() + "'";
@@ -155,53 +171,106 @@ public abstract class IntegrationNamespaceUtils {
* attribute, this will create and register a PollerMetadata instance and
* then add it as a property reference of the target builder.
*
- * @param pollerElement the "poller" element to parse
- * @param targetBuilder the builder that expects the "trigger" property
- * @param parserContext the parserContext for the target builder
+ * @param pollerElement
+ * the "poller" element to parse
+ * @param targetBuilder
+ * the builder that expects the "trigger" property
+ * @param parserContext
+ * the parserContext for the target builder
*/
- public static void configurePollerMetadata(Element pollerElement, BeanDefinitionBuilder targetBuilder, ParserContext parserContext) {
+ public static void configurePollerMetadata(Element pollerElement,
+ BeanDefinitionBuilder targetBuilder, ParserContext parserContext) {
if (pollerElement.hasAttribute("ref")) {
if (pollerElement.getAttributes().getLength() != 1) {
- parserContext.getReaderContext().error(
- "A 'poller' element that provides a 'ref' must have no other attributes.", pollerElement);
+ parserContext
+ .getReaderContext()
+ .error(
+ "A 'poller' element that provides a 'ref' must have no other attributes.",
+ pollerElement);
}
if (pollerElement.getChildNodes().getLength() != 0) {
- parserContext.getReaderContext().error(
- "A 'poller' element that provides a 'ref' must have no child elements.", pollerElement);
+ parserContext
+ .getReaderContext()
+ .error(
+ "A 'poller' element that provides a 'ref' must have no child elements.",
+ pollerElement);
}
- targetBuilder.addPropertyReference("pollerMetadata", pollerElement.getAttribute("ref"));
- }
- else {
- BeanDefinition beanDefinition = parserContext.getDelegate().parseCustomElement(
- pollerElement, targetBuilder.getBeanDefinition());
+ targetBuilder.addPropertyReference("pollerMetadata", pollerElement
+ .getAttribute("ref"));
+ } else {
+ BeanDefinition beanDefinition = parserContext.getDelegate()
+ .parseCustomElement(pollerElement,
+ targetBuilder.getBeanDefinition());
if (beanDefinition == null) {
- parserContext.getReaderContext().error("BeanDefinition must not be null", pollerElement);
+ parserContext.getReaderContext().error(
+ "BeanDefinition must not be null", pollerElement);
}
targetBuilder.addPropertyValue("pollerMetadata", beanDefinition);
}
}
- public static BeanComponentDefinition parseInnerHandlerDefinition(Element element, ParserContext parserContext){
- // parses out inner bean definition for concrete implementation if defined
- List childElements = DomUtils.getChildElementsByTagName(element, "bean");
+ /**
+ * Get a text value from a named attribute if it exists, otherwise check for
+ * a nested element of the same name. If both are specified it is an error,
+ * but if neither is specified, just returns null.
+ *
+ * @param element
+ * a DOM node
+ * @param name
+ * the name of the property (attribute or child element)
+ * @param parserContext
+ * the current context
+ * @return the text from the attribite or element or null
+ */
+ public static String getTextFromAttributeOrNestedElement(Element element,
+ String name, ParserContext parserContext) {
+ String attr = element.getAttribute(name);
+ Element childElement = DomUtils.getChildElementByTagName(element, name);
+ if (StringUtils.hasText(attr) && childElement != null) {
+ parserContext.getReaderContext().error(
+ "Either an attribute or a child element can be specified for "
+ + name + " but not both", element);
+ return null;
+ }
+ if (!StringUtils.hasText(attr) && childElement == null) {
+ return null;
+ }
+ return StringUtils.hasText(attr) ? attr : childElement.getTextContent();
+ }
+
+ public static BeanComponentDefinition parseInnerHandlerDefinition(
+ Element element, ParserContext parserContext) {
+ // parses out inner bean definition for concrete implementation if
+ // defined
+ List childElements = DomUtils.getChildElementsByTagName(
+ element, "bean");
BeanComponentDefinition innerComponentDefinition = null;
- if (childElements != null && childElements.size() == 1){
+ if (childElements != null && childElements.size() == 1) {
Element beanElement = childElements.get(0);
BeanDefinitionParserDelegate delegate = parserContext.getDelegate();
- BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(beanElement);
- bdHolder = delegate.decorateBeanDefinitionIfRequired(beanElement, bdHolder);
+ BeanDefinitionHolder bdHolder = delegate
+ .parseBeanDefinitionElement(beanElement);
+ bdHolder = delegate.decorateBeanDefinitionIfRequired(beanElement,
+ bdHolder);
BeanDefinition inDef = bdHolder.getBeanDefinition();
- String beanName = BeanDefinitionReaderUtils.generateBeanName(inDef, parserContext.getRegistry());
- innerComponentDefinition = new BeanComponentDefinition(inDef, beanName);
+ String beanName = BeanDefinitionReaderUtils.generateBeanName(inDef,
+ parserContext.getRegistry());
+ innerComponentDefinition = new BeanComponentDefinition(inDef,
+ beanName);
parserContext.registerBeanComponent(innerComponentDefinition);
}
-
+
String ref = element.getAttribute(REF_ATTRIBUTE);
- Assert.isTrue(!(StringUtils.hasText(ref) && innerComponentDefinition != null), "Ambiguous definition. Inner bean " +
- (innerComponentDefinition == null
- ? innerComponentDefinition
- : innerComponentDefinition.getBeanDefinition().getBeanClassName()) + " declaration and \"ref\" " + ref +
- " are not allowed together.");
+ Assert
+ .isTrue(
+ !(StringUtils.hasText(ref) && innerComponentDefinition != null),
+ "Ambiguous definition. Inner bean "
+ + (innerComponentDefinition == null ? innerComponentDefinition
+ : innerComponentDefinition
+ .getBeanDefinition()
+ .getBeanClassName())
+ + " declaration and \"ref\" " + ref
+ + " are not allowed together.");
return innerComponentDefinition;
}
diff --git a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcMessageHandlerParser.java b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcMessageHandlerParser.java
index 3de068f3b9..ecd81ba4db 100644
--- a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcMessageHandlerParser.java
+++ b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcMessageHandlerParser.java
@@ -52,7 +52,10 @@ public class JdbcMessageHandlerParser extends AbstractOutboundChannelAdapterPars
"Exactly one of the attributes data-source or "
+ "simple-jdbc-operations should be set for the JDBC outbound-channel-adapter", source);
}
- String query = element.getAttribute("query");
+ String query = IntegrationNamespaceUtils.getTextFromAttributeOrNestedElement(element, "query", parserContext);
+ if (!StringUtils.hasText(query)) {
+ throw new BeanCreationException("The query attrbitue is required");
+ }
if (!StringUtils.hasText(query)) {
throw new BeanCreationException("The query attrbitue is required");
}
diff --git a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcPollingChannelAdapterParser.java b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcPollingChannelAdapterParser.java
index 5d2d044c06..6b397f5a27 100644
--- a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcPollingChannelAdapterParser.java
+++ b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcPollingChannelAdapterParser.java
@@ -55,10 +55,11 @@ public class JdbcPollingChannelAdapterParser extends AbstractPollingInboundChann
parserContext.getReaderContext().error("Exactly one of the attributes data-source or " +
"simple-jdbc-operations should be set for the JDBC inbound-channel-adapter", source);
}
- String query = element.getAttribute("query");
+ String query = IntegrationNamespaceUtils.getTextFromAttributeOrNestedElement(element, "query", parserContext);
if (!StringUtils.hasText(query)) {
throw new BeanCreationException("The query attrbitue is required");
}
+ String update = IntegrationNamespaceUtils.getTextFromAttributeOrNestedElement(element, "update", parserContext);
if (refToDataSourceSet) {
builder.addConstructorArgReference(dataSourceRef);
}
@@ -68,7 +69,9 @@ public class JdbcPollingChannelAdapterParser extends AbstractPollingInboundChann
builder.addConstructorArgValue(query);
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "row-mapper");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "sql-parameter-source-factory");
- IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "update", "updateSql");
+ if (update!=null) {
+ builder.addPropertyValue("updateSql", update);
+ }
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "update-per-row");
return BeanDefinitionReaderUtils.registerWithGeneratedName(
builder.getBeanDefinition(), parserContext.getRegistry());
diff --git a/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/config/spring-integration-jdbc-2.0.xsd b/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/config/spring-integration-jdbc-2.0.xsd
index 80f1533637..2b90fd7366 100644
--- a/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/config/spring-integration-jdbc-2.0.xsd
+++ b/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/config/spring-integration-jdbc-2.0.xsd
@@ -1,12 +1,15 @@
-
-
+
-
+
@@ -85,7 +89,8 @@
]]>
-
+
@@ -106,14 +111,45 @@
-
+
+
+
+
+ A select query to execute when a message is
+ polled. In general
+ the query can return multiple
+ rows, because
+ the result will be a List (of type determined by the
+ row
+ mapper).
+
+
+
+
+
+
+
+
+ An update query to execute when a message is
+ polled. If the poll is in a transaction then the
+ update will
+ roll back if the transaction does.
+
+
+
+
+
-
+
- A select query to execute when a message is polled. In general the query can return multiple
- rows, because the result will be a List (of type determined by the row mapper).
+ A select query to execute when a message is
+ polled. In general the query can return multiple
+ rows, because
+ the result will be a List (of type determined by the row
+ mapper).
@@ -122,13 +158,17 @@
- Reference to a row mapper to use to convert JDBC result set rows to message payloads.
+ Reference to a row mapper to use to convert
+ JDBC result set rows to message payloads.
Optional
- with default that maps
- result set row to a map (column name to column value). Other simple
+ with default
+ that maps
+ result set row to a map (column name to column value).
+ Other simple
use cases can
be handled
- with out-of-the box implementations from Spring JDBC. Others require a custom row
+ with out-of-the box
+ implementations from Spring JDBC. Others require a custom row
mapper.
@@ -141,18 +181,23 @@
- An update query to execute when a message is polled. If the poll is in a transaction then the
- update will roll back if the transaction does.
+ An update query to execute when a message is
+ polled. If the poll is in a transaction then the
+ update will
+ roll back if the transaction does.
-
+
- Flag to indicate whether the update query should be executed per message, or per row (in the
- case that a message contains multiple rows).
+ Flag to indicate whether the update query
+ should be executed per message, or per row (in the
+ case that a
+ message contains multiple rows).
@@ -161,10 +206,12 @@
- Channel to which polled messages will be sent.
+ Channel to which polled messages will be
+ sent.
-
+
@@ -184,15 +231,36 @@
-
+
+
+
+
+
+ An SQL update query to execute (INSERT,
+ UPDATE
+ or DELETE). Bean properties of the outgoing
+ message can be
+ referenced in named parameters, e.g. "INSERT into FOOS (ID,
+ NAME) values (:headers[business.key],
+ :payload)". More complex
+ requirements can be implemented by
+
+
+
+
+
+
- An SQL update query to execute (INSERT, UPDATE
+ An SQL update query to execute (INSERT,
+ UPDATE
or DELETE). Bean properties of the outgoing
message can be
- referenced in named parameters, e.g. "INSERT into FOOS (ID, NAME) values (:headers[business.key],
- :payload)". More complex requirements can be implemented by
+ referenced in named parameters, e.g. "INSERT into FOOS (ID,
+ NAME) values (:headers[business.key],
+ :payload)". More complex
+ requirements can be implemented by
@@ -201,11 +269,14 @@
- Channel from which messages will be output. When a message is sent to this channel it will
- cause the query to be executed.
+ Channel from which messages will be output.
+ When a message is sent to this channel it will
+ cause the query
+ to be executed.
-
+
@@ -224,7 +295,8 @@
database. Either this or the
simple-jdbc-operations
must be
- specified (but not both).
+ specified
+ (but not both).
@@ -239,7 +311,8 @@
Reference to a JdbcOperations. Either
this or
- the data-source must be
+ the
+ data-source must be
specified (but not both).
@@ -252,14 +325,21 @@
- Reference to a SqlParameterSourceFactory. For an inbound adapter the input is the result of the
- query, and for an outbound adapter the input is the whole outgoing message. The default factory creates a bean
- property parameter source for a generic input (like a Message), and treats a List in a special way: the List is
- assumed to contain entities with a field called "id" and these are collected and copied to a field in the
+ Reference to a SqlParameterSourceFactory. For an
+ inbound adapter the input is the result of the
+ query, and for an
+ outbound adapter the input is the whole outgoing message. The
+ default factory creates a bean
+ property parameter source for a
+ generic input (like a Message), and treats a List in a special
+ way: the List is
+ assumed to contain entities with a field called
+ "id" and these are collected and copied to a field in the
parameter source called "idList".
-
+
diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcMessageHandlerParserTests.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcMessageHandlerParserTests.java
index 862e13bc92..bed3629513 100644
--- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcMessageHandlerParserTests.java
+++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcMessageHandlerParserTests.java
@@ -54,6 +54,16 @@ public class JdbcMessageHandlerParserTests {
assertEquals("Wrong name", "bar", map.get("name"));
}
+ @Test
+ public void testMapPayloadNestedQueryOutboundChannelAdapter(){
+ setUp("handlingMapPayloadNestedQueryJdbcOutboundChannelAdapterTest.xml", getClass());
+ Message> message = MessageBuilder.withPayload(Collections.singletonMap("foo", "bar")).build();
+ channel.send(message);
+ Map map = this.jdbcTemplate.queryForMap("SELECT * from FOOS");
+ assertEquals("Wrong id", message.getHeaders().getId().toString(), map.get("ID"));
+ assertEquals("Wrong name", "bar", map.get("name"));
+ }
+
@Test
public void testParameterSourceOutboundChannelAdapter(){
setUp("handlingParameterSourceJdbcOutboundChannelAdapterTest.xml", getClass());
diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcPollingChannelAdapterParserTests.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcPollingChannelAdapterParserTests.java
index dc48c23dda..82657b7078 100644
--- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcPollingChannelAdapterParserTests.java
+++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcPollingChannelAdapterParserTests.java
@@ -52,6 +52,16 @@ public class JdbcPollingChannelAdapterParserTests {
assertNull(channelTemplate.receive());
}
+ @Test
+ public void testSimpleInboundChannelAdapterWithNestedUpdate(){
+ setUp("pollingForMapJdbcInboundChannelAdapterWithNestedUpdateTest.xml", getClass());
+ this.jdbcTemplate.update("insert into item values(1,'',2)");
+ Message> message = channelTemplate.receive();
+ assertNotNull(message);
+ message = channelTemplate.receive();
+ assertNull(channelTemplate.receive());
+ }
+
@Test
public void testExtendedInboundChannelAdapter(){
setUp("pollingWithJdbcOperationsJdbcInboundChannelAdapterTest.xml", getClass());
diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/handlingMapPayloadNestedQueryJdbcOutboundChannelAdapterTest.xml b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/handlingMapPayloadNestedQueryJdbcOutboundChannelAdapterTest.xml
new file mode 100644
index 0000000000..c122dfaa5d
--- /dev/null
+++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/handlingMapPayloadNestedQueryJdbcOutboundChannelAdapterTest.xml
@@ -0,0 +1,18 @@
+
+
+
+
+ insert into foos (id, status, name) values (:headers[$id], 0, :payload[foo])
+
+
+
+
+
diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/pollingForMapJdbcInboundChannelAdapterWithNestedUpdateTest.xml b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/pollingForMapJdbcInboundChannelAdapterWithNestedUpdateTest.xml
new file mode 100644
index 0000000000..c91e34ca0d
--- /dev/null
+++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/pollingForMapJdbcInboundChannelAdapterWithNestedUpdateTest.xml
@@ -0,0 +1,20 @@
+
+
+
+
+ select * from item where status=2
+ update item set status=10 where id in (:idList)
+
+
+
+
+
diff --git a/src/docbkx/jdbc.xml b/src/docbkx/jdbc.xml
new file mode 100644
index 0000000000..1652fa8236
--- /dev/null
+++ b/src/docbkx/jdbc.xml
@@ -0,0 +1,113 @@
+
+
+
+ JDBC Support
+
+ Spring Integration provides Channel Adapters for receiving and sending
+ messages via database queries.
+
+
+ Inbound Channel Adapter
+
+ The main function of an inbound Channel Adapter is to execute a SQL
+ SELECT query and turn the result set into a message. The
+ message payload is the whole result set, expressed as a
+ List, and the types of the items in the list
+ depends on the row-mapping strategy that is used. The default strategy is
+ a generic mapper that just returns a Map for each
+ row i nthe query. Optionally this can be changed by adding a reference to
+ requires a reference to a RowMapper instance (see
+ the Spring
+ JDBC documentation for more detailed information about row
+ mapping).
+ If you want to convert rows in the SELECT query result to
+ individual messages you can use a downstream splitter.
+
+
+ The inbound adapter also requires a reference to either
+ JdbcTemplate instance or
+ DataSource. The following example defines
+ an inbound Channel Adapter with a DataSource
+ reference. ]]>
+
+ The parameters in the update query are specified with a colon (:) prefix to the name of a map key. This is a standard feature of the named parameter JDBC support in Spring JDBC.
+
+
+ As well as the SELECT statement to generate the
+ messages, the adapter above also has an UPDATE statement that
+ is being used to mark the records as processed, so they don't show up in
+ the next poll. The update is parameterised by the list of ids from the
+ original select. This is done through a naming convention by default (a
+ column in the input result set called "id" is translated into a list in
+ the parameter map for the update called "idList"). To change the parameter
+ generation strategy you can inject a
+ SqlParameterSourceFactory into the adapter to
+ override the default behaviour (the adapter has a
+ sql-parameter-source-factory attribute).
+
+
+ Polling and Transactions
+
+ The inbound adapter accepts a regular Spring Integration poller as
+ a sub element, so for instance the frequency of the polling can be
+ controlled. A very important feature of the poller for JDBC usage is the
+ option to wrap the poll operation in a transaction, for example:
+
+
+
+
+
+
+]]>
+
+ In this example the database is polled every 1000 milliseconds,
+ and the update and select queries are both executed in the same
+ transaction. The transaction manager configuration is not shown, but as
+ long as it is aware of the data source then the poll is transactional. A
+ common use case is for the downstream channels to be direct channels
+ (the default), so that the endpoints are invoked in the same thread, and
+ hence the same transaction. then if any of them fails, the transaction
+ rolls back and the input data are reverted to their original
+ state.
+
+
+
+
+ Outbound Channel Adapter
+
+ The outbound Channel Adapter is the inverse of the inbound: its role
+ is to handle a message and use it to execute a SQL query. The message
+ payload and headers are available by default as input parameters to the
+ query, for instance: ]]> In the
+ example above, messages arriving on the channel "input" have a payload of
+ a map with key "foo", so the [] operator dereferences that
+ value from the map. The headers are also accessed as a map.
+ The parameters in the query above are bean paths in the incoming message (they are not Spring EL expressions). This behaviour is part of the
+
+ MapSqlParameterSource
+
+ in Spring JDBC, which is the default source created by the outbound adapter. Other behaviour is possible in the adapter, and only requires the user to inject a different
+
+ SqlParameterSourceFactory
+
+ .
+
+
+ The outbound adapter requires a reference to either a DataSource or
+ a JdbcTemplate. It can also have a
+ SqlParameterSourceFactory injected to control the
+ binding of incoming message to the query.
+
+ If the input channel is a direct channel then the outbound adapter
+ runs its query in the same thread, and therefor ethe same transaction (if
+ there is one) as the sender of the message.
+
+