From 6b50c49efe5c5f38cbb54b765d577e4d4c8adfd3 Mon Sep 17 00:00:00 2001 From: Artem Bilan Date: Tue, 19 Nov 2013 17:34:53 +0200 Subject: [PATCH] INT-3210: JDBC & JPA: Optimize SpEL Parsing JIRA: https://jira.springsource.org/browse/INT-3210 * Parse `Expression` parameters only once at creation phase * There is still tiny case, when configured parameters don't have provided parameter, in this case an `input` determines as JavaBean and the `name` will be populated as bean property via SpEL. INT-3210: Add params to maps and collections * If configured parameters don't contain provide param, parse his expressions and add to parameters of current `ParameterSource` and enclosing `ParameterSourceFactory` * Move JPA `ExpressionEvaluatingParameterSource` to inner class of `ExpressionEvaluatingParameterSourceFactory`, similar to `ExpressionEvaluatingSqlParameterSourceFactory` --- ...onEvaluatingSqlParameterSourceFactory.java | 56 ++++++-- .../integration/jpa/support/JpaParameter.java | 86 ++++++++---- .../ExpressionEvaluatingParameterSource.java | 131 ------------------ ...ssionEvaluatingParameterSourceFactory.java | 130 ++++++++++++++++- ...ressionEvaluatingParameterSourceUtils.java | 32 +---- 5 files changed, 234 insertions(+), 201 deletions(-) delete mode 100644 spring-integration-jpa/src/main/java/org/springframework/integration/jpa/support/parametersource/ExpressionEvaluatingParameterSource.java 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 5f4fb4c314..494aac5a8a 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 @@ -21,7 +21,10 @@ import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.expression.Expression; import org.springframework.expression.ExpressionException; +import org.springframework.expression.ExpressionParser; +import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.integration.util.AbstractExpressionEvaluator; import org.springframework.jdbc.core.namedparam.AbstractSqlParameterSource; import org.springframework.jdbc.core.namedparam.SqlParameterSource; @@ -33,6 +36,7 @@ import org.springframework.jdbc.core.namedparam.SqlParameterSource; * @author Dave Syer * @author Oleg Zhurakousky * @author Gary Russell + * @author Artem Bilan * @since 2.0 */ public class ExpressionEvaluatingSqlParameterSourceFactory extends AbstractExpressionEvaluator implements @@ -40,15 +44,23 @@ public class ExpressionEvaluatingSqlParameterSourceFactory extends AbstractExpre private final static Log logger = LogFactory.getLog(ExpressionEvaluatingSqlParameterSourceFactory.class); + private static final ExpressionParser PARSER = new SpelExpressionParser(); + private static final Object ERROR = new Object(); private volatile Map staticParameters; - private volatile Map parameterExpressions; + /** + * The {@link Map} of parameters with expressions. + * {@code key} - parameter name; {@code value} - array of two {@link Expression}s: + * first element - direct {@link Expression}, second - collection projection {@link Expression}. + * Used in case of root object of evaluation is {@link Collection}. + */ + private volatile Map parameterExpressions; public ExpressionEvaluatingSqlParameterSourceFactory() { this.staticParameters = Collections.unmodifiableMap(new HashMap()); - this.parameterExpressions = Collections.unmodifiableMap(new HashMap()); + this.parameterExpressions = new HashMap(); } /** @@ -98,13 +110,21 @@ public class ExpressionEvaluatingSqlParameterSourceFactory extends AbstractExpre * @param parameterExpressions the parameter expressions to set */ public void setParameterExpressions(Map parameterExpressions) { - this.parameterExpressions = parameterExpressions; + Map paramExpressions = new HashMap(parameterExpressions.size()); + for (Map.Entry entry : parameterExpressions.entrySet()) { + String key = entry.getKey(); + String expression = entry.getValue(); + Expression[] expressions = new Expression[] { + PARSER.parseExpression(expression), + PARSER.parseExpression("#root.![" + expression + "]") + }; + paramExpressions.put(key, expressions); + } + this.parameterExpressions = paramExpressions; } public SqlParameterSource createParameterSource(final Object input) { - SqlParameterSource toReturn = new ExpressionEvaluatingSqlParameterSource(input, staticParameters, - parameterExpressions); - return toReturn; + return new ExpressionEvaluatingSqlParameterSource(input, this.staticParameters, this.parameterExpressions); } @Override @@ -119,10 +139,10 @@ public class ExpressionEvaluatingSqlParameterSourceFactory extends AbstractExpre private volatile Map values = new HashMap(); - private final Map parameterExpressions; + private final Map parameterExpressions; private ExpressionEvaluatingSqlParameterSource(Object input, Map staticParameters, - Map parameterExpressions) { + Map parameterExpressions) { this.input = input; this.parameterExpressions = parameterExpressions; this.values.putAll(staticParameters); @@ -132,13 +152,25 @@ public class ExpressionEvaluatingSqlParameterSourceFactory extends AbstractExpre if (values.containsKey(paramName)) { return values.get(paramName); } - String expression = paramName; - if (parameterExpressions.containsKey(expression)) { - expression = parameterExpressions.get(expression); + + if (!parameterExpressions.containsKey(paramName)) { + Expression[] expressions = new Expression[] { + PARSER.parseExpression(paramName), + PARSER.parseExpression("#root.![" + paramName + "]") + }; + ExpressionEvaluatingSqlParameterSourceFactory.this.parameterExpressions.put(paramName, expressions); + this.parameterExpressions.put(paramName, expressions); } + + Expression expression = null; + if (input instanceof Collection) { - expression = "#root.![" + expression + "]"; + expression = parameterExpressions.get(paramName)[1]; } + else { + expression = parameterExpressions.get(paramName)[0]; + } + Object value = evaluateExpression(expression, input); values.put(paramName, value); if (logger.isDebugEnabled()) { diff --git a/spring-integration-jpa/src/main/java/org/springframework/integration/jpa/support/JpaParameter.java b/spring-integration-jpa/src/main/java/org/springframework/integration/jpa/support/JpaParameter.java index 4cdd9739ca..c65a77036f 100644 --- a/spring-integration-jpa/src/main/java/org/springframework/integration/jpa/support/JpaParameter.java +++ b/spring-integration-jpa/src/main/java/org/springframework/integration/jpa/support/JpaParameter.java @@ -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. @@ -15,6 +15,9 @@ */ package org.springframework.integration.jpa.support; +import org.springframework.expression.Expression; +import org.springframework.expression.ExpressionParser; +import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.util.Assert; /** @@ -24,32 +27,29 @@ import org.springframework.util.Assert; * TODO Should we combine ProcedureParameter class and this class? * * @author Gunnar Hillert + * @author Artem Bilan * @since 2.2 * */ public class JpaParameter { - private String name; - private Object value; - private String expression; + private static final ExpressionParser PARSER = new SpelExpressionParser(); - public String getName() { - return this.name; - } - public void setName(String name) { - this.name = name; - } - public Object getValue() { - return this.value; - } - public void setValue(Object value) { - this.value = value; - } - public String getExpression() { - return this.expression; - } - public void setExpression(String expression) { - this.expression = expression; + private String name; + + private Object value; + + private String expression; + + private Expression spelExpression; + + private Expression projectionExpression; + + /** + * Default constructor. + */ + public JpaParameter() { + super(); } /** @@ -66,7 +66,7 @@ public class JpaParameter { this.name = name; this.value = value; - this.expression = expression; + this.setExpression(expression); } /** @@ -77,16 +77,42 @@ public class JpaParameter { * @param expression If null, the value property must be set */ public JpaParameter(Object value, String expression) { - super(); - this.value = value; - this.expression = expression; + this.value = value; + this.setExpression(expression); } - /** - * Default constructor. - */ - public JpaParameter() { - super(); + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public Object getValue() { + return this.value; + } + + public void setValue(Object value) { + this.value = value; + } + + public String getExpression() { + return this.expression; + } + + public Expression getSpelExpression() { + return this.spelExpression; + } + + public Expression getProjectionExpression() { + return this.projectionExpression; + } + + public void setExpression(String expression) { + this.expression = expression; + this.spelExpression = PARSER.parseExpression(expression); + this.projectionExpression = PARSER.parseExpression("#root.![" + expression + "]"); } @Override diff --git a/spring-integration-jpa/src/main/java/org/springframework/integration/jpa/support/parametersource/ExpressionEvaluatingParameterSource.java b/spring-integration-jpa/src/main/java/org/springframework/integration/jpa/support/parametersource/ExpressionEvaluatingParameterSource.java deleted file mode 100644 index 68fd5851e2..0000000000 --- a/spring-integration-jpa/src/main/java/org/springframework/integration/jpa/support/parametersource/ExpressionEvaluatingParameterSource.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2002-2012 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.integration.jpa.support.parametersource; - -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.expression.ExpressionException; -import org.springframework.integration.jpa.support.JpaParameter; -import org.springframework.integration.jpa.support.parametersource.ExpressionEvaluatingParameterSourceUtils.ParameterExpressionEvaluator; -import org.springframework.util.Assert; - -/** -* -* @author Gunnar Hillert -* @since 2.2 -* -*/ -class ExpressionEvaluatingParameterSource implements PositionSupportingParameterSource { - - private static final Log logger = LogFactory.getLog(ExpressionEvaluatingParameterSource.class); - - private static final Object ERROR = new Object(); - - private final Object input; - - private volatile Map values = new HashMap(); - - private final Map parameterExpressions; - - private final List parameters; - - private final ParameterExpressionEvaluator expressionEvaluator; - - ExpressionEvaluatingParameterSource(Object input, List parameters, ParameterExpressionEvaluator expressionEvaluator) { - - this.input = input; - this.expressionEvaluator = expressionEvaluator; - this.parameters = parameters; - this.parameterExpressions = ExpressionEvaluatingParameterSourceUtils.convertExpressions(parameters); - this.values.putAll(ExpressionEvaluatingParameterSourceUtils.convertStaticParameters(parameters)); - - } - - public Object getValueByPosition(int position) { - - Assert.isTrue(position >= 0, "The position must be be non-negative."); - - if (position <= parameters.size()) { - - final JpaParameter parameter = parameters.get(position); - - if (parameter.getValue() != null) { - return parameter.getValue(); - } - - if (parameter.getExpression() != null) { - String expression = parameter.getExpression(); - - if (input instanceof Collection) { - expression = "#root.![" + expression + "]"; - } - - final Object value = this.expressionEvaluator.evaluateExpression(expression, input); - //FIXME values.put(paramName, value); - if (logger.isDebugEnabled()) { - logger.debug("Resolved expression " + expression + " to " + value); - } - return value; - - } - - } - - return null; - - } - - public Object getValue(String paramName) { - if (values.containsKey(paramName)) { - return values.get(paramName); - } - String expression = paramName; - if (parameterExpressions.containsKey(expression)) { - expression = parameterExpressions.get(expression); - } - if (input instanceof Collection) { - expression = "#root.![" + expression + "]"; - } - final Object value = this.expressionEvaluator.evaluateExpression(expression, input); - values.put(paramName, value); - if (logger.isDebugEnabled()) { - logger.debug("Resolved expression " + expression + " to " + value); - } - return value; - } - - public boolean hasValue(String paramName) { - try { - final Object value = getValue(paramName); - if (value == ERROR) { - return false; - } - } - catch (ExpressionException e) { - if (logger.isDebugEnabled()) { - logger.debug("Could not evaluate expression", e); - } - values.put(paramName, ERROR); - return false; - } - return true; - } -} diff --git a/spring-integration-jpa/src/main/java/org/springframework/integration/jpa/support/parametersource/ExpressionEvaluatingParameterSourceFactory.java b/spring-integration-jpa/src/main/java/org/springframework/integration/jpa/support/parametersource/ExpressionEvaluatingParameterSourceFactory.java index 903a0b8bac..4f6ba04ca0 100644 --- a/spring-integration-jpa/src/main/java/org/springframework/integration/jpa/support/parametersource/ExpressionEvaluatingParameterSourceFactory.java +++ b/spring-integration-jpa/src/main/java/org/springframework/integration/jpa/support/parametersource/ExpressionEvaluatingParameterSourceFactory.java @@ -16,10 +16,17 @@ package org.springframework.integration.jpa.support.parametersource; import java.util.ArrayList; -import java.util.Collections; +import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.BeanFactory; +import org.springframework.expression.Expression; +import org.springframework.expression.ExpressionException; import org.springframework.integration.jpa.support.JpaParameter; import org.springframework.integration.jpa.support.parametersource.ExpressionEvaluatingParameterSourceUtils.ParameterExpressionEvaluator; import org.springframework.util.Assert; @@ -28,11 +35,16 @@ import org.springframework.util.Assert; * * @author Gunnar Hillert * @author Gary Russell + * @author Artem Bilan * @since 2.2 * */ public class ExpressionEvaluatingParameterSourceFactory implements ParameterSourceFactory { + private final static Log logger = LogFactory.getLog(ExpressionEvaluatingParameterSourceFactory.class); + + private static final Object ERROR = new Object(); + private volatile List parameters; private final ParameterExpressionEvaluator expressionEvaluator = new ParameterExpressionEvaluator(); @@ -42,7 +54,7 @@ public class ExpressionEvaluatingParameterSourceFactory implements ParameterSour } public ExpressionEvaluatingParameterSourceFactory(BeanFactory beanFactory) { - this.parameters = Collections.unmodifiableList(new ArrayList()); + this.parameters = new ArrayList(); this.expressionEvaluator.setBeanFactory(beanFactory); } @@ -69,4 +81,118 @@ public class ExpressionEvaluatingParameterSourceFactory implements ParameterSour return new ExpressionEvaluatingParameterSource(input, this.parameters, expressionEvaluator); } + + class ExpressionEvaluatingParameterSource implements PositionSupportingParameterSource { + + private final Object input; + + private volatile Map values = new HashMap(); + + private final List parameters; + + private final Map parametersMap; + + private final ParameterExpressionEvaluator expressionEvaluator; + + ExpressionEvaluatingParameterSource(Object input, List parameters, ParameterExpressionEvaluator expressionEvaluator) { + + this.input = input; + this.expressionEvaluator = expressionEvaluator; + this.parameters = parameters; + this.parametersMap = new HashMap(parameters.size()); + for (JpaParameter parameter : parameters) { + this.parametersMap.put(parameter.getName(), parameter); + } + this.values.putAll(ExpressionEvaluatingParameterSourceUtils.convertStaticParameters(parameters)); + + } + + public Object getValueByPosition(int position) { + + Assert.isTrue(position >= 0, "The position must be be non-negative."); + + if (position <= parameters.size()) { + + final JpaParameter parameter = parameters.get(position); + + if (parameter.getValue() != null) { + return parameter.getValue(); + } + + if (parameter.getExpression() != null) { + Expression expression; + + if (input instanceof Collection) { + expression = parameter.getProjectionExpression(); + } + else { + expression = parameter.getSpelExpression(); + } + + final Object value = this.expressionEvaluator.evaluateExpression(expression, input); + if (parameter.getName() != null) { + values.put(parameter.getName(), value); + } + if (logger.isDebugEnabled()) { + logger.debug("Resolved expression " + expression + " to " + value); + } + return value; + + } + + } + + return null; + + } + + public Object getValue(String paramName) { + if (values.containsKey(paramName)) { + return values.get(paramName); + } + + if (!this.parametersMap.containsKey(paramName)) { + JpaParameter parameter = new JpaParameter(paramName, null, paramName); + ExpressionEvaluatingParameterSourceFactory.this.parameters.add(parameter); + this.parametersMap.put(paramName, parameter); + } + + JpaParameter jpaParameter = this.parametersMap.get(paramName); + + Expression expression = null; + + if (input instanceof Collection) { + expression = jpaParameter.getProjectionExpression(); + } + else { + expression = jpaParameter.getSpelExpression(); + } + + final Object value = this.expressionEvaluator.evaluateExpression(expression, input); + values.put(paramName, value); + if (logger.isDebugEnabled()) { + logger.debug("Resolved expression " + expression + " to " + value); + } + return value; + } + + public boolean hasValue(String paramName) { + try { + final Object value = getValue(paramName); + if (value == ERROR) { + return false; + } + } + catch (ExpressionException e) { + if (logger.isDebugEnabled()) { + logger.debug("Could not evaluate expression", e); + } + values.put(paramName, ERROR); + return false; + } + return true; + } + + } + } diff --git a/spring-integration-jpa/src/main/java/org/springframework/integration/jpa/support/parametersource/ExpressionEvaluatingParameterSourceUtils.java b/spring-integration-jpa/src/main/java/org/springframework/integration/jpa/support/parametersource/ExpressionEvaluatingParameterSourceUtils.java index d4fe006f1f..40a54b85c4 100644 --- a/spring-integration-jpa/src/main/java/org/springframework/integration/jpa/support/parametersource/ExpressionEvaluatingParameterSourceUtils.java +++ b/spring-integration-jpa/src/main/java/org/springframework/integration/jpa/support/parametersource/ExpressionEvaluatingParameterSourceUtils.java @@ -19,6 +19,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.Map; +import org.springframework.expression.Expression; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.integration.jpa.support.JpaParameter; import org.springframework.integration.util.AbstractExpressionEvaluator; @@ -37,31 +38,6 @@ class ExpressionEvaluatingParameterSourceUtils { throw new AssertionError(); } - /** - * Utility method that converts a Collection of {@link JpaParameter} to - * a Map containing only expression parameters. - * - * @param jpaParameters Must not be null. - * @return Map containing only the Expression bound parameters. Will never be null. - */ - public static Map convertExpressions(Collection jpaParameters) { - - Assert.notNull(jpaParameters, "The Collection of jpaParameters must not be null."); - - for (JpaParameter parameter : jpaParameters) { - Assert.notNull(parameter, "'jpaParameters' must not contain null values."); - } - - final Map staticParameters = new HashMap(); - - for (JpaParameter parameter : jpaParameters) { - if (parameter.getExpression() != null) { - staticParameters.put(parameter.getName(), parameter.getExpression()); - } - } - - return staticParameters; - } /** * Utility method that converts a Collection of {@link JpaParameter} to @@ -89,6 +65,10 @@ class ExpressionEvaluatingParameterSourceUtils { return staticParameters; } + /** + * Simple {@link AbstractExpressionEvaluator} implementation + * to increase the visibility of protected methods. + */ public static class ParameterExpressionEvaluator extends AbstractExpressionEvaluator { @Override @@ -97,7 +77,7 @@ class ExpressionEvaluatingParameterSourceUtils { } @Override - public Object evaluateExpression(String expression, Object input) { + public Object evaluateExpression(Expression expression, Object input) { return super.evaluateExpression(expression, input); }