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