INT-3322 JDBC i-c-a SpEL Select ParameterSource

JIRA: https://jira.spring.io/browse/INT-3322

Support the use of an ExpressionEvaluatingSqlParameterSource
from the ExpressionEvaluatingSqlParameterSourceFactory as the
`select-sql-parameter-source` for an inbound channel adapter.

Add documentation showing how to construct the bean.

Add a mechanism to disable caching so that the expression is
re-evaluated each time. However, the value should still be
cached between the `hasValue()` and `getValue()` calls.

INT-3322 Polishing; PR Comments
This commit is contained in:
Gary russell
2014-03-13 16:09:19 +02:00
committed by Artem Bilan
parent 98b46c61f3
commit c73cc40e23
5 changed files with 155 additions and 12 deletions

View File

@@ -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 <int-jdbc:inbound-channel-adapter/>} 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<String, Object> values = new HashMap<String, Object>();
private final Map<String, Object> values = new HashMap<String, Object>();
private final Map<String, Expression[]> parameterExpressions;
private final boolean cache;
private ExpressionEvaluatingSqlParameterSource(Object input, Map<String, ?> staticParameters,
Map<String, Expression[]> parameterExpressions) {
Map<String, Expression[]> 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;

View File

@@ -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.
</xsd:documentation>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.jdbc.core.namedparam.SqlParameterSource" />

View File

@@ -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<Void>() {
@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;
}
}
}

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration/jdbc"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:si="http://www.springframework.org/schema/integration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.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">
<inbound-channel-adapter query="select * from item where status=:status" channel="target"
data-source="dataSource" select-sql-parameter-source="parameterSource"
update="delete from item" />
<beans:import resource="jdbcInboundChannelAdapterCommonConfig.xml" />
<beans:bean id="parameterSource" factory-bean="parameterSourceFactory" factory-method="createParameterSourceNoCache">
<beans:constructor-arg value="" />
</beans:bean>
<beans:bean id="parameterSourceFactory" class="org.springframework.integration.jdbc.ExpressionEvaluatingSqlParameterSourceFactory">
<beans:property name="parameterExpressions">
<beans:map>
<beans:entry key="status" value="@statusBean.which()" />
</beans:map>
</beans:property>
</beans:bean>
<beans:bean id="statusBean" class="org.springframework.integration.jdbc.config.JdbcPollingChannelAdapterParserTests$Status" />
</beans:beans>