From 854dba1366b098b2db60f6d323dbbaa62c2c37a7 Mon Sep 17 00:00:00 2001 From: Meherzad Lahewala Date: Sat, 4 Nov 2017 19:32:44 +0530 Subject: [PATCH] INT-4291 Add ExprEvalSqlPSF.setSqlParameterTypes JIRA: https://jira.spring.io/browse/INT-4291 * Fix checkstyle trailing space *Use correct way to constants in spring xml and fix test case * Polishing - Update javadoc and rename method name to relate more to the behavior --- ...onEvaluatingSqlParameterSourceFactory.java | 32 ++++++++++++-- ...luatingSqlParameterSourceFactoryTests.java | 42 +++++++++++++++++-- ...terSourceJdbcInboundChannelAdapterTest.xml | 9 +++- src/reference/asciidoc/jdbc.adoc | 27 ++++++++++++ src/reference/asciidoc/whats-new.adoc | 7 +++- 5 files changed, 107 insertions(+), 10 deletions(-) diff --git a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/ExpressionEvaluatingSqlParameterSourceFactory.java b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/ExpressionEvaluatingSqlParameterSourceFactory.java index 92cca7fde3..f56cd5d072 100644 --- a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/ExpressionEvaluatingSqlParameterSourceFactory.java +++ b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/ExpressionEvaluatingSqlParameterSourceFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 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. @@ -40,6 +40,7 @@ import org.springframework.jdbc.core.namedparam.SqlParameterSource; * @author Oleg Zhurakousky * @author Gary Russell * @author Artem Bilan + * @author Meherzad Lahewala * @since 2.0 */ public class ExpressionEvaluatingSqlParameterSourceFactory extends AbstractExpressionEvaluator implements @@ -53,6 +54,8 @@ public class ExpressionEvaluatingSqlParameterSourceFactory extends AbstractExpre private volatile Map staticParameters; + private volatile Map sqlParametersTypes; + /** * The {@link Map} of parameters with expressions. * {@code key} - parameter name; {@code value} - array of two {@link Expression}s: @@ -126,9 +129,23 @@ public class ExpressionEvaluatingSqlParameterSourceFactory extends AbstractExpre this.parameterExpressions = paramExpressions; } + /** + * Optionally set parameter sql types for the used parameters. Use + * {@link java.sql.Types} to get the parameter type value. + * @param sqlParametersTypes the parameter type to use + * @since 5.0 + * @see java.sql.Types + */ + public void setSqlParameterTypes(Map sqlParametersTypes) { + this.sqlParametersTypes = sqlParametersTypes; + } + @Override public SqlParameterSource createParameterSource(final Object input) { - return new ExpressionEvaluatingSqlParameterSource(input, this.staticParameters, this.parameterExpressions, true); + AbstractSqlParameterSource sqlParameterSource = new ExpressionEvaluatingSqlParameterSource(input, + this.staticParameters, this.parameterExpressions, true); + registerSqlTypes(sqlParameterSource); + return sqlParameterSource; } /** @@ -139,7 +156,10 @@ public class ExpressionEvaluatingSqlParameterSourceFactory extends AbstractExpre * @return The parameter source. */ public SqlParameterSource createParameterSourceNoCache(final Object input) { - return new ExpressionEvaluatingSqlParameterSource(input, this.staticParameters, this.parameterExpressions, false); + AbstractSqlParameterSource sqlParameterSource = new ExpressionEvaluatingSqlParameterSource(input, + this.staticParameters, this.parameterExpressions, false); + registerSqlTypes(sqlParameterSource); + return sqlParameterSource; } @Override @@ -148,6 +168,12 @@ public class ExpressionEvaluatingSqlParameterSourceFactory extends AbstractExpre this.getEvaluationContext().setVariable("staticParameters", this.staticParameters); } + private void registerSqlTypes(AbstractSqlParameterSource sqlParameterSource) { + if (this.sqlParametersTypes != null) { + this.sqlParametersTypes.forEach(sqlParameterSource::registerSqlType); + } + } + private final class ExpressionEvaluatingSqlParameterSource extends AbstractSqlParameterSource { private final Object input; diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/ExpressionEvaluatingSqlParameterSourceFactoryTests.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/ExpressionEvaluatingSqlParameterSourceFactoryTests.java index e1f81a985b..9833ebdb02 100644 --- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/ExpressionEvaluatingSqlParameterSourceFactoryTests.java +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/ExpressionEvaluatingSqlParameterSourceFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 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. @@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; +import java.sql.Types; import java.util.Arrays; import java.util.Collections; @@ -27,10 +28,11 @@ import org.junit.Test; import org.springframework.beans.factory.BeanFactory; import org.springframework.jdbc.core.namedparam.SqlParameterSource; +import org.springframework.jdbc.support.JdbcUtils; /** * @author Dave Syer - * + * @author Meherzad Lahewala */ public class ExpressionEvaluatingSqlParameterSourceFactoryTests { @@ -44,6 +46,7 @@ public class ExpressionEvaluatingSqlParameterSourceFactoryTests { SqlParameterSource source = factory.createParameterSource(null); assertTrue(source.hasValue("foo")); assertEquals("bar", source.getValue("foo")); + assertEquals(JdbcUtils.TYPE_UNKNOWN, source.getSqlType("foo")); } @Test @@ -53,18 +56,19 @@ public class ExpressionEvaluatingSqlParameterSourceFactoryTests { SqlParameterSource source = factory.createParameterSource(Collections.singletonMap("foo", "bar")); assertTrue(source.hasValue("foo")); assertEquals("bar", source.getValue("foo")); + assertEquals(JdbcUtils.TYPE_UNKNOWN, source.getSqlType("foo")); } @Test public void testListOfMapsInput() throws Exception { factory.setBeanFactory(mock(BeanFactory.class)); factory.afterPropertiesSet(); - @SuppressWarnings("unchecked") SqlParameterSource source = factory.createParameterSource(Arrays.asList(Collections.singletonMap("foo", "bar"), Collections.singletonMap("foo", "bucket"))); String expression = "foo"; assertTrue(source.hasValue(expression)); assertEquals("[bar, bucket]", source.getValue(expression).toString()); + assertEquals(JdbcUtils.TYPE_UNKNOWN, source.getSqlType(expression)); } @Test @@ -75,6 +79,7 @@ public class ExpressionEvaluatingSqlParameterSourceFactoryTests { // This is an illegal parameter name in Spring JDBC so we'd never get this as input assertTrue(source.hasValue("foo.toUpperCase()")); assertEquals("BAR", source.getValue("foo.toUpperCase()")); + assertEquals(JdbcUtils.TYPE_UNKNOWN, source.getSqlType("food")); } @Test @@ -85,6 +90,7 @@ public class ExpressionEvaluatingSqlParameterSourceFactoryTests { SqlParameterSource source = factory.createParameterSource(Collections.singletonMap("foo", "bar")); assertTrue(source.hasValue("spam")); assertEquals("BAR", source.getValue("spam")); + assertEquals(JdbcUtils.TYPE_UNKNOWN, source.getSqlType("spam")); } @Test @@ -96,6 +102,7 @@ public class ExpressionEvaluatingSqlParameterSourceFactoryTests { SqlParameterSource source = factory.createParameterSource(Collections.singletonMap("crap", "bucket")); assertTrue(source.hasValue("spam")); assertEquals("BAR", source.getValue("spam")); + assertEquals(JdbcUtils.TYPE_UNKNOWN, source.getSqlType("spam")); } @Test @@ -103,12 +110,39 @@ public class ExpressionEvaluatingSqlParameterSourceFactoryTests { factory.setParameterExpressions(Collections.singletonMap("spam", "foo.toUpperCase()")); factory.setBeanFactory(mock(BeanFactory.class)); factory.afterPropertiesSet(); - @SuppressWarnings("unchecked") SqlParameterSource source = factory.createParameterSource(Arrays.asList(Collections.singletonMap("foo", "bar"), Collections.singletonMap("foo", "bucket"))); String expression = "spam"; assertTrue(source.hasValue(expression)); assertEquals("[BAR, BUCKET]", source.getValue(expression).toString()); + assertEquals(JdbcUtils.TYPE_UNKNOWN, source.getSqlType("foo")); } + @Test + public void testListOfMapsInputWithExpressionAndTypes() throws Exception { + factory.setParameterExpressions(Collections.singletonMap("spam", "foo.toUpperCase()")); + factory.setBeanFactory(mock(BeanFactory.class)); + factory.setSqlParameterTypes(Collections.singletonMap("spam", Types.SQLXML)); + factory.afterPropertiesSet(); + SqlParameterSource source = factory.createParameterSource(Arrays.asList(Collections.singletonMap("foo", "bar"), + Collections.singletonMap("foo", "bucket"))); + String expression = "spam"; + assertTrue(source.hasValue(expression)); + assertEquals("[BAR, BUCKET]", source.getValue(expression).toString()); + assertEquals(Types.SQLXML, source.getSqlType("spam")); + } + + @Test + public void testListOfMapsInputWithExpressionAndEmptyTypes() throws Exception { + factory.setParameterExpressions(Collections.singletonMap("spam", "foo.toUpperCase()")); + factory.setBeanFactory(mock(BeanFactory.class)); + factory.setSqlParameterTypes(Collections.emptyMap()); + factory.afterPropertiesSet(); + SqlParameterSource source = factory.createParameterSource(Arrays.asList(Collections.singletonMap("foo", "bar"), + Collections.singletonMap("foo", "bucket"))); + String expression = "spam"; + assertTrue(source.hasValue(expression)); + assertEquals("[BAR, BUCKET]", source.getValue(expression).toString()); + assertEquals(JdbcUtils.TYPE_UNKNOWN, source.getSqlType("spam")); + } } diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/pollingWithSelectParameterSourceJdbcInboundChannelAdapterTest.xml b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/pollingWithSelectParameterSourceJdbcInboundChannelAdapterTest.xml index c750aadd67..57e9245c71 100644 --- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/pollingWithSelectParameterSourceJdbcInboundChannelAdapterTest.xml +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/pollingWithSelectParameterSourceJdbcInboundChannelAdapterTest.xml @@ -15,17 +15,22 @@ update="delete from item" /> - + - + + + + + + diff --git a/src/reference/asciidoc/jdbc.adoc b/src/reference/asciidoc/jdbc.adoc index 5c4226272a..2f07e5160e 100644 --- a/src/reference/asciidoc/jdbc.adoc +++ b/src/reference/asciidoc/jdbc.adoc @@ -81,6 +81,33 @@ The `value` in each parameter expression can be any valid SpEL expression. The `#root` object for the expression evaluation is the constructor argument defined on the `parameterSource` bean. It is static for all evaluations (in this case, an empty String). +Starting with _version 5.0_, you can use pass parameter types using `setSqlParameterTypes` method in +the `ExpressionEvaluatingSqlParameterSourceFactory` can be supplied with the sqlParameterTypes to specify the target SQL type for the particular parameter. + +Below example provides sql type for the parameters being used in the query. + +[source,xml] +---- + + + + + + + + + + + + + + +---- + IMPORTANT: Use the `createParameterSourceNoCache` factory method; otherwise the parameter source will cache the result of the evaluation. Also note that, because caching is disabled, if the same parameter name appears in the select query multiple times, it will be re-evaluated for each occurrence. diff --git a/src/reference/asciidoc/whats-new.adoc b/src/reference/asciidoc/whats-new.adoc index 36f7efc554..3aa7155ca9 100644 --- a/src/reference/asciidoc/whats-new.adoc +++ b/src/reference/asciidoc/whats-new.adoc @@ -292,8 +292,13 @@ The `GemfireMetadataStore` now implements `ListenableMetadataStore`, allowing us See <> for more information. -==== Jdbc Message Channel Store Changes +==== Jdbc Changes The `JdbcMessageChannelStore` now provides setter for the `ChannelMessageStorePreparedStatementSetter` allowing users to customize a message insertion in the store. See <> for more information. + +The `ExpressionEvaluatingSqlParameterSourceFactory` now provides setter for the sqlParameterTypes allowing users +to customize sql types of the parameters. + +See <> for more information. \ No newline at end of file