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 3f55c689ee..5adbeba380 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
@@ -125,7 +125,18 @@ public class ExpressionEvaluatingSqlParameterSourceFactory extends AbstractExpre
@Override
public SqlParameterSource createParameterSource(final Object input) {
- return new ExpressionEvaluatingSqlParameterSource(input, this.staticParameters, this.parameterExpressions);
+ return new ExpressionEvaluatingSqlParameterSource(input, this.staticParameters, this.parameterExpressions, true);
+ }
+
+ /**
+ * Create an expression evaluating {@link SqlParameterSource} that does not cache it's results. Useful for cases
+ * where the source is used multiple times, for example in a {@code } for the
+ * {@code select-sql-parameter-source} attribute.
+ * @param input The root object for the evaluation.
+ * @return The parameter source.
+ */
+ public SqlParameterSource createParameterSourceNoCache(final Object input) {
+ return new ExpressionEvaluatingSqlParameterSource(input, this.staticParameters, this.parameterExpressions, false);
}
@Override
@@ -138,21 +149,32 @@ public class ExpressionEvaluatingSqlParameterSourceFactory extends AbstractExpre
private final Object input;
- private volatile Map values = new HashMap();
+ private final Map values = new HashMap();
private final Map parameterExpressions;
+ private final boolean cache;
+
private ExpressionEvaluatingSqlParameterSource(Object input, Map staticParameters,
- Map parameterExpressions) {
+ Map parameterExpressions, boolean cache) {
this.input = input;
this.parameterExpressions = parameterExpressions;
this.values.putAll(staticParameters);
+ this.cache = cache;
}
@Override
public Object getValue(String paramName) throws IllegalArgumentException {
+ return this.doGetValue(paramName, false);
+ }
+
+ public Object doGetValue(String paramName, boolean calledFromHasValue) throws IllegalArgumentException {
if (values.containsKey(paramName)) {
- return values.get(paramName);
+ Object cachedByHasValue = values.get(paramName);
+ if (!this.cache) {
+ values.remove(paramName);
+ }
+ return cachedByHasValue;
}
if (!parameterExpressions.containsKey(paramName)) {
@@ -174,7 +196,9 @@ public class ExpressionEvaluatingSqlParameterSourceFactory extends AbstractExpre
}
Object value = evaluateExpression(expression, input);
- values.put(paramName, value);
+ if (this.cache || calledFromHasValue) {
+ values.put(paramName, value);
+ }
if (logger.isDebugEnabled()) {
logger.debug("Resolved expression " + expression + " to " + value);
}
@@ -184,7 +208,7 @@ public class ExpressionEvaluatingSqlParameterSourceFactory extends AbstractExpre
@Override
public boolean hasValue(String paramName) {
try {
- Object value = getValue(paramName);
+ Object value = doGetValue(paramName, true);
if (value == ERROR) {
return false;
}
@@ -193,7 +217,9 @@ public class ExpressionEvaluatingSqlParameterSourceFactory extends AbstractExpre
if (logger.isDebugEnabled()) {
logger.debug("Could not evaluate expression", e);
}
- values.put(paramName, ERROR);
+ if (this.cache) {
+ values.put(paramName, ERROR);
+ }
return false;
}
return true;
diff --git a/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/config/spring-integration-jdbc-4.0.xsd b/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/config/spring-integration-jdbc-4.0.xsd
index 6fa18ccb42..719fcfab03 100644
--- a/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/config/spring-integration-jdbc-4.0.xsd
+++ b/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/config/spring-integration-jdbc-4.0.xsd
@@ -202,6 +202,10 @@
that query has
placeholders (e.g. "SELECT * from FOO where KEY=:key") they
will be bound from this source by name.
+ Note: if you use the framework's 'ExpressionEvaluatingSqlParameterSourceFactory'
+ to create a SpEL-based parameter source, be sure to use the 'createParameterSourceNoCache'
+ method so that the expression will be re-evaluated on each poll. See the reference
+ documentation for more information.
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 6aabbc5fd2..0c1c649bba 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
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2014 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.
@@ -129,6 +129,20 @@ public class JdbcPollingChannelAdapterParserTests {
assertEquals("BAR", list.get(0).get("NAME"));
}
+ @Test
+ public void testSelectParameterSourceFactoryInboundChannelAdapter() {
+ setUp("pollingWithSelectParameterSourceJdbcInboundChannelAdapterTest.xml", getClass());
+ this.jdbcTemplate.update("insert into item values(1,'',42)");
+ Message> message = messagingTemplate.receive();
+ assertNotNull(message);
+ assertEquals(42, ((Map,?>) ((List> )message.getPayload()).get(0)).get("STATUS"));
+ this.jdbcTemplate.update("insert into item values(2,'',84)");
+ this.appCtx.getBean(Status.class).which = 84;
+ message = messagingTemplate.receive();
+ assertNotNull(message);
+ assertEquals(84, ((Map,?>) ((List> )message.getPayload()).get(0)).get("STATUS"));
+ }
+
@Test
public void testParameterSourceInboundChannelAdapter() {
setUp("pollingWithParametersForMapJdbcInboundChannelAdapterTest.xml", getClass());
@@ -141,6 +155,7 @@ public class JdbcPollingChannelAdapterParserTests {
public void testMaxRowsInboundChannelAdapter() {
setUp("pollingWithMaxRowsJdbcInboundChannelAdapterTest.xml", getClass());
new TransactionTemplate(transactionManager).execute(new TransactionCallback() {
+ @Override
public Void doInTransaction(TransactionStatus status) {
jdbcTemplate.update("insert into item values(1,'',2)");
jdbcTemplate.update("insert into item values(2,'',2)");
@@ -200,14 +215,26 @@ public class JdbcPollingChannelAdapterParserTests {
public static class TestSqlParameterSource extends AbstractSqlParameterSource {
+ @Override
public Object getValue(String paramName) throws IllegalArgumentException {
return 2;
}
+ @Override
public boolean hasValue(String paramName) {
return true;
}
}
+ public static class Status {
+
+ private int which = 42;
+
+ public int which() {
+ return this.which;
+ }
+
+ }
+
}
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
new file mode 100644
index 0000000000..c750aadd67
--- /dev/null
+++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/pollingWithSelectParameterSourceJdbcInboundChannelAdapterTest.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/reference/docbook/jdbc.xml b/src/reference/docbook/jdbc.xml
index 7c067aa6e9..04ffb9f3fe 100644
--- a/src/reference/docbook/jdbc.xml
+++ b/src/reference/docbook/jdbc.xml
@@ -74,12 +74,65 @@
channel="target" data-source="dataSource"
update="update item set status=10 where id in (:id)" />]]>
- The parameters in the update query are specified with a colon (:) prefix to the name of a parameter (which in this case is an expression to be applied to each of the rows in the polled result set). This is a standard feature of the named parameter JDBC support in Spring JDBC combined with a convention (projection onto the polled result list) adopted in Spring Integration. The underlying Spring JDBC features limit the available expressions (e.g. most special characters other than period are disallowed), but since the target is usually a list of or an individual object addressable by simple bean paths this isn't unduly restrictive.
- To change the parameter generation strategy you can inject a
+ The parameters in the update query are specified with a colon (:)
+ prefix to the name of a parameter (which in this case is an expression
+ to be applied to each of the rows in the polled result set).
+ This is a standard feature of the named parameter JDBC support in Spring JDBC
+ combined with a convention (projection onto the polled result list)
+ adopted in Spring Integration.
+ The underlying Spring JDBC features limit the available expressions
+ (e.g. most special characters other than period are disallowed),
+ but since the target is usually a list of or an individual object addressable by simple bean paths this isn't unduly restrictive.
+
+ To change the parameter generation strategy you can inject a
SqlParameterSourceFactory into the adapter to
override the default behavior (the adapter has a
- sql-parameter-source-factory attribute).
+ sql-parameter-source-factory attribute). Spring Integration
+ provides a ExpressionEvaluatingSqlParameterSourceFactory which
+ will create a SpEL-based parameter source, with the results of the query as the
+ #root object. (If update-per-row is true, the root object
+ is the row). If the same parameter name appears multiple times in the update query, it
+ is evaluated only one time, and its result is cached.
+
+
+ You can also use a parameter source for the select query. In this case, since there is no "result"
+ object to evaluate against, a single parameter source is used each time (rather than using a
+ parameter source factory). Starting with version 4.0, you can use Spring
+ to create a SpEL based parameter source as follows:
+
+
+
+
+
+
+
+
+
+
+
+
+
+]]>
+
+
+ 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).
+
+
+ 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.
+
Polling and Transactions
@@ -355,7 +408,7 @@
For more information, please see:
-
+
Also important, please ensure that you use an up-to-date version