INT-2739: StoredProc: 'row-mapper' as a Bean Ref

* Convert `row-mapper` attribute of stored procedure components
to bean reference
* Class name is also supported for backward compatibility

JIRA: https://jira.springsource.org/browse/INT-2739
This commit is contained in:
Artem Bilan
2013-09-16 10:53:26 +03:00
committed by Gary Russell
parent f42f6eca57
commit 7f34f8c919
8 changed files with 70 additions and 73 deletions

View File

@@ -13,7 +13,9 @@
package org.springframework.integration.jdbc.config;
import org.springframework.beans.factory.config.BeanDefinition;
import org.w3c.dom.Element;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
@@ -23,7 +25,6 @@ import org.springframework.integration.config.xml.AbstractConsumerEndpointParser
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
import org.springframework.integration.jdbc.StoredProcOutboundGateway;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* @author Gunnar Hillert
@@ -47,7 +48,8 @@ public class StoredProcOutboundGatewayParser extends AbstractConsumerEndpointPar
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(storedProcExecutorBuilder, element, "sql-parameter-source-factory");
IntegrationNamespaceUtils.setValueIfAttributeDefined(storedProcExecutorBuilder, element, "skip-undeclared-results");
final ManagedMap<String, BeanDefinition> returningResultsetMap = StoredProcParserUtils.getReturningResultsetBeanDefinitions(element, parserContext);
final ManagedMap<String, BeanMetadataElement> returningResultsetMap =
StoredProcParserUtils.getReturningResultsetBeanDefinitions(element, parserContext);
if (!returningResultsetMap.isEmpty()) {
storedProcExecutorBuilder.addPropertyValue("returningResultSetRowMappers", returningResultsetMap);

View File

@@ -19,9 +19,14 @@ package org.springframework.integration.jdbc.config;
import java.sql.Types;
import java.util.List;
import org.w3c.dom.Element;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.config.TypedStringValue;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedList;
@@ -36,9 +41,9 @@ import org.springframework.jdbc.core.SqlInOutParameter;
import org.springframework.jdbc.core.SqlOutParameter;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
/**
* @author Gunnar Hillert
@@ -203,21 +208,31 @@ public final class StoredProcParserUtils {
* @param storedProcComponent
* @param parserContext
*/
public static ManagedMap<String, BeanDefinition> getReturningResultsetBeanDefinitions(
public static ManagedMap<String, BeanMetadataElement> getReturningResultsetBeanDefinitions(
Element storedProcComponent, ParserContext parserContext) {
List<Element> returningResultsetChildElements = DomUtils.getChildElementsByTagName(storedProcComponent, "returning-resultset");
ManagedMap<String, BeanDefinition> returningResultsetMap = new ManagedMap<String, BeanDefinition>();
ManagedMap<String, BeanMetadataElement> returningResultsetMap = new ManagedMap<String, BeanMetadataElement>();
for (Element childElement : returningResultsetChildElements) {
String name = childElement.getAttribute("name");
String rowMapperAsString = childElement.getAttribute("row-mapper");
BeanDefinitionBuilder rowMapperBuilder = BeanDefinitionBuilder.genericBeanDefinition(rowMapperAsString);
BeanMetadataElement rowMapperBeanDefinition = null;
returningResultsetMap.put(name, rowMapperBuilder.getBeanDefinition());
try {
// Backward compatibility
ClassUtils.forName(rowMapperAsString, parserContext.getReaderContext().getBeanClassLoader());
rowMapperBeanDefinition = BeanDefinitionBuilder.genericBeanDefinition(rowMapperAsString).getBeanDefinition();
}
catch (ClassNotFoundException e) {
//Ignore it and fallback to bean reference
rowMapperBeanDefinition = new RuntimeBeanReference(rowMapperAsString);
}
returningResultsetMap.put(name, rowMapperBeanDefinition);
}
return returningResultsetMap;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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
@@ -13,8 +13,9 @@
package org.springframework.integration.jdbc.config;
import org.w3c.dom.Element;
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.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
@@ -23,7 +24,6 @@ import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.xml.AbstractPollingInboundChannelAdapterParser;
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
import org.springframework.integration.jdbc.StoredProcPollingChannelAdapter;
import org.w3c.dom.Element;
/**
* @author Gunnar Hillert
@@ -51,7 +51,8 @@ public class StoredProcPollingChannelAdapterParser extends AbstractPollingInboun
IntegrationNamespaceUtils.setValueIfAttributeDefined(storedProcExecutorBuilder, element, "is-function");
IntegrationNamespaceUtils.setValueIfAttributeDefined(storedProcExecutorBuilder, element, "skip-undeclared-results");
final ManagedMap<String, BeanDefinition> returningResultsetMap = StoredProcParserUtils.getReturningResultsetBeanDefinitions(element, parserContext);
final ManagedMap<String, BeanMetadataElement> returningResultsetMap =
StoredProcParserUtils.getReturningResultsetBeanDefinitions(element, parserContext);
if (!returningResultsetMap.isEmpty()) {
storedProcExecutorBuilder.addPropertyValue("returningResultSetRowMappers", returningResultsetMap);

View File

@@ -1274,9 +1274,15 @@
</xsd:attribute>
<xsd:attribute name="row-mapper" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation source="java:java.lang.Class"><![CDATA[
The fully qualified class name of the row mapper.
]]></xsd:documentation>
<xsd:appinfo>
<xsd:documentation>
Reference to a row mapper to use to convert
JDBC result set rows to message payloads.
</xsd:documentation>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.jdbc.core.RowMapper" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>

View File

@@ -1,51 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:int-jdbc="http://www.springframework.org/schema/integration/jdbc"
xmlns:int="http://www.springframework.org/schema/integration"
xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/jdbc http://www.springframework.org/schema/integration/jdbc/spring-integration-jdbc.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<int:channel id="target"/>
<jdbc:embedded-database id="dataSource" type="HSQL"/>
<int-jdbc:stored-proc-inbound-channel-adapter id="storedProcedurePollingChannelAdapter"
data-source="dataSource" channel="target"
stored-procedure-name="GET_PRIME_NUMBERS"
is-function="false">
<int-jdbc:sql-parameter-definition name="username" direction="IN" type="VARCHAR"/>
<int-jdbc:sql-parameter-definition name="password" direction="OUT" />
<int-jdbc:sql-parameter-definition name="age" direction="INOUT" type="INTEGER" scale="5"/>
<int-jdbc:sql-parameter-definition name="description" />
<int-jdbc:parameter name="username" value="kenny" type="java.lang.String"/>
<int-jdbc:parameter name="description" value="Who killed Kenny?"/>
<int-jdbc:parameter name="password" expression="payload.username"/>
<int-jdbc:parameter name="age" value="30" type="java.lang.Integer"/>
<int-jdbc:returning-resultset name="out" row-mapper="org.springframework.integration.jdbc.storedproc.PrimeMapper"/>
</int-jdbc:stored-proc-inbound-channel-adapter>
<int:poller default="true" fixed-rate="10000"/>
<int-jdbc:stored-proc-inbound-channel-adapter id="autoChannel"
data-source="dataSource"
stored-procedure-name="GET_PRIME_NUMBERS"
is-function="false">
<int-jdbc:sql-parameter-definition name="username" direction="IN" type="VARCHAR" />
<int-jdbc:sql-parameter-definition name="password" direction="OUT" />
<int-jdbc:sql-parameter-definition name="age" direction="INOUT" type="INTEGER" scale="5" />
<int-jdbc:sql-parameter-definition name="description" />
<int-jdbc:parameter name="username" value="kenny" type="java.lang.String"/>
<int-jdbc:parameter name="description" value="Who killed Kenny?"/>
<int-jdbc:parameter name="password" expression="payload.username"/>
<int-jdbc:parameter name="age" value="30" type="java.lang.Integer"/>
<int-jdbc:returning-resultset name="out" row-mapper="org.springframework.integration.jdbc.storedproc.PrimeMapper"/>
</int-jdbc:stored-proc-inbound-channel-adapter>
<int:bridge input-channel="autoChannel" output-channel="nullChannel" />
</beans>

View File

@@ -21,12 +21,14 @@ import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import java.sql.Types;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.junit.After;
import org.junit.Test;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
@@ -40,10 +42,13 @@ import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.SqlInOutParameter;
import org.springframework.jdbc.core.SqlOutParameter;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.core.simple.ParameterizedSingleColumnRowMapper;
/**
* @author Gunnar Hillert
* @author Gary Russell
* @author Artem Bilan
*
* @since 2.1
*
*/
@@ -200,12 +205,18 @@ public class StoredProcPollingChannelAdapterParserTests {
Map<String, RowMapper<?>> returningResultSetRowMappersAsMap = (Map<String, RowMapper<?>>) returningResultSetRowMappers;
assertTrue("The rowmapper was not set. Expected returningResultSetRowMappersAsMap.size() == 1", returningResultSetRowMappersAsMap.size() == 1);
assertTrue("The rowmapper was not set. Expected returningResultSetRowMappersAsMap.size() == 2",
returningResultSetRowMappersAsMap.size() == 2);
Entry<String, ?> mapEntry1 = returningResultSetRowMappersAsMap.entrySet().iterator().next();
Iterator<Entry<String,RowMapper<?>>> iterator = returningResultSetRowMappersAsMap.entrySet().iterator();
assertEquals("out", mapEntry1.getKey());
assertTrue(mapEntry1.getValue() instanceof PrimeMapper);
Entry<String, ?> mapEntry = iterator.next();
assertEquals("out", mapEntry.getKey());
assertTrue(mapEntry.getValue() instanceof PrimeMapper);
mapEntry = iterator.next();
assertEquals("out2", mapEntry.getKey());
assertTrue(mapEntry.getValue() instanceof ParameterizedSingleColumnRowMapper);
}

View File

@@ -26,9 +26,14 @@
<int-jdbc:parameter name="password" expression="payload.username"/>
<int-jdbc:parameter name="age" value="30" type="java.lang.Integer"/>
<int-jdbc:returning-resultset name="out" row-mapper="org.springframework.integration.jdbc.storedproc.PrimeMapper"/>
<int-jdbc:returning-resultset name="out2" row-mapper="longSingleColumnRowMapper"/>
</int-jdbc:stored-proc-inbound-channel-adapter>
<bean id="longSingleColumnRowMapper" class="org.springframework.jdbc.core.simple.ParameterizedSingleColumnRowMapper"
factory-method="newInstance">
<constructor-arg value="java.lang.Long"/>
</bean>
<int:poller default="true" fixed-rate="10000"/>
<int-jdbc:stored-proc-inbound-channel-adapter id="autoChannel"

View File

@@ -258,7 +258,7 @@
</para>
</section>
<section id="3.0-stored-proc-sql-return-type">
<title>SqlReturnType support for Stored Procedure components</title>
<title>Stored Procedure Components Improvements</title>
<para>
For more complex database-specific types, not supported by the standard
<code>CallableStatement.getObject</code> method, 2 new additional
@@ -270,7 +270,15 @@
<listitem><emphasis>return-type</emphasis></listitem>
</itemizedlist>
<para>
For more information see <xref linkend="stored-procedures"/>.
<para>
The <code>row-mapper</code> attribute of the Stored Procedure Inbound Channel Adapter
<code>&lt;returning-resultset/&gt;</code> sub-element
now supports a reference to a <interfacename>RowMapper</interfacename> bean
definition. Previously, it contained just a class name (which is still supported).
</para>
<para>
For more information see <xref linkend="stored-procedures"/>.
</para>
</para>
</section>
<section id="3.0-event-for-imap-idle">