From c3cd0885adf73cd146eb808e18fc2789828d0ece Mon Sep 17 00:00:00 2001 From: datagear Date: Tue, 27 Nov 2018 20:15:22 +0800 Subject: [PATCH] =?UTF-8?q?[persistence,web]persistence=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8F=98=E9=87=8F=E8=A1=A8=E8=BE=BE=E5=BC=8F?= =?UTF-8?q?=E6=94=AF=E6=8C=81=EF=BC=8C=E4=B8=BA=E6=89=B9=E9=87=8F=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E6=93=8D=E4=BD=9C=E6=97=B6=E7=9A=84=E7=B1=BB=E4=BC=BC?= =?UTF-8?q?=E2=80=9C#{index*3+1}=E2=80=9D=EF=BC=88index=E4=B8=BA=E6=89=B9?= =?UTF-8?q?=E9=87=8F=E7=B4=A2=E5=BC=95=EF=BC=89=E7=9A=84=E8=A1=A8=E8=BE=BE?= =?UTF-8?q?=E5=BC=8F=E6=8F=90=E4=BE=9B=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- datagear-persistence/pom.xml | 5 + ...ctExpressionModelPersistenceOperation.java | 341 ++++++++++++++++++ .../AbstractModelPersistenceOperation.java | 104 ------ .../support/DefaultPersistenceManager.java | 42 ++- .../support/ExpressionErrorException.java | 38 ++ .../support/ExpressionEvaluationContext.java | 147 ++++++++ .../support/ExpressionResolver.java | 102 ++++-- .../support/InsertPersistenceOperation.java | 185 +++++----- .../support/SqlExpressionErrorException.java | 13 +- .../support/SqlExpressionResolver.java | 43 +++ .../support/UpdatePersistenceOperation.java | 161 +++++---- .../support/VariableExpressionBean.java | 37 ++ .../VariableExpressionErrorException.java | 23 ++ .../support/VariableExpressionResolver.java | 43 +++ ...ressionModelPersistenceOperationTest.java} | 4 +- .../support/ExpressionResolverTest.java | 15 + .../support/SpelExpressionParserTest.java | 65 ++++ .../web/controller/ControllerAdvice.java | 91 +++-- .../web/convert/AbstractDataConverter.java | 31 +- .../resources/datagear-applicationContext.xml | 10 + .../resources/locales/datagear.properties | 3 +- .../webapp/static/script/datagear-util.js | 15 +- .../src/main/webapp/static/theme/common.css | 17 + 23 files changed, 1155 insertions(+), 380 deletions(-) create mode 100644 datagear-persistence/src/main/java/org/datagear/persistence/support/AbstractExpressionModelPersistenceOperation.java create mode 100644 datagear-persistence/src/main/java/org/datagear/persistence/support/ExpressionErrorException.java create mode 100644 datagear-persistence/src/main/java/org/datagear/persistence/support/ExpressionEvaluationContext.java create mode 100644 datagear-persistence/src/main/java/org/datagear/persistence/support/SqlExpressionResolver.java create mode 100644 datagear-persistence/src/main/java/org/datagear/persistence/support/VariableExpressionBean.java create mode 100644 datagear-persistence/src/main/java/org/datagear/persistence/support/VariableExpressionErrorException.java create mode 100644 datagear-persistence/src/main/java/org/datagear/persistence/support/VariableExpressionResolver.java rename datagear-persistence/src/test/java/org/datagear/persistence/support/{AbstractModelPersistenceOperationTest.java => AbstractExpressionModelPersistenceOperationTest.java} (79%) create mode 100644 datagear-persistence/src/test/java/org/datagear/persistence/support/SpelExpressionParserTest.java diff --git a/datagear-persistence/pom.xml b/datagear-persistence/pom.xml index 49bd8897..02e85af5 100644 --- a/datagear-persistence/pom.xml +++ b/datagear-persistence/pom.xml @@ -28,6 +28,11 @@ datagear-dbinfo ${project.version} + + org.springframework + spring-expression + ${spring.version} + org.springframework spring-jdbc diff --git a/datagear-persistence/src/main/java/org/datagear/persistence/support/AbstractExpressionModelPersistenceOperation.java b/datagear-persistence/src/main/java/org/datagear/persistence/support/AbstractExpressionModelPersistenceOperation.java new file mode 100644 index 00000000..050c9904 --- /dev/null +++ b/datagear-persistence/src/main/java/org/datagear/persistence/support/AbstractExpressionModelPersistenceOperation.java @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2018 datagear.org. All Rights Reserved. + */ + +package org.datagear.persistence.support; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + +import org.datagear.connection.JdbcUtil; +import org.datagear.model.Model; +import org.datagear.model.Property; +import org.datagear.model.support.MU; +import org.datagear.persistence.UnsupportedModelCharacterException; +import org.datagear.persistence.support.ExpressionResolver.Expression; +import org.springframework.core.convert.ConversionService; +import org.springframework.expression.spel.standard.SpelExpressionParser; + +/** + * 抽象支持表达式的持久化操作类。 + * + * @author datagear@163.com + * + */ +public abstract class AbstractExpressionModelPersistenceOperation extends AbstractModelPersistenceOperation +{ + private ConversionService conversionService; + + private ExpressionResolver variableExpressionResolver; + + private ExpressionResolver sqlExpressionResolver; + + private SpelExpressionParser spelExpressionParser = new SpelExpressionParser(); + + public AbstractExpressionModelPersistenceOperation() + { + super(); + } + + public AbstractExpressionModelPersistenceOperation(ConversionService conversionService, + ExpressionResolver variableExpressionResolver, ExpressionResolver sqlExpressionResolver) + { + super(); + this.conversionService = conversionService; + this.variableExpressionResolver = variableExpressionResolver; + this.sqlExpressionResolver = sqlExpressionResolver; + } + + public ConversionService getConversionService() + { + return conversionService; + } + + public void setConversionService(ConversionService conversionService) + { + this.conversionService = conversionService; + } + + public ExpressionResolver getVariableExpressionResolver() + { + return variableExpressionResolver; + } + + public void setVariableExpressionResolver(ExpressionResolver variableExpressionResolver) + { + this.variableExpressionResolver = variableExpressionResolver; + } + + public ExpressionResolver getSqlExpressionResolver() + { + return sqlExpressionResolver; + } + + public void setSqlExpressionResolver(ExpressionResolver sqlExpressionResolver) + { + this.sqlExpressionResolver = sqlExpressionResolver; + } + + public SpelExpressionParser getSpelExpressionParser() + { + return spelExpressionParser; + } + + public void setSpelExpressionParser(SpelExpressionParser spelExpressionParser) + { + this.spelExpressionParser = spelExpressionParser; + } + + /** + * 解析表达式属性值。 + *

+ * 如果包含表达式,返回计算结果;否则,直接返回{@code propValue}。 + *

+ * + * @param cn + * @param model + * @param property + * @param expressionPropValue + * @param expressionEvaluationContext + * @return + */ + protected Object evaluatePropertyValueIfExpression(Connection cn, Model model, Property property, Object propValue, + ExpressionEvaluationContext expressionEvaluationContext) + { + List variableExpressions = this.variableExpressionResolver.resolve(propValue); + + Object evaluatedPropValue = propValue; + + if (variableExpressions != null && !variableExpressions.isEmpty()) + { + String strPropValue = (String) propValue; + + checkValidExpressionProperty(model, property, strPropValue); + + evaluatedPropValue = evaluateVariableExpressions(strPropValue, variableExpressions, + expressionEvaluationContext); + } + + List sqlExpressions = this.sqlExpressionResolver.resolve(evaluatedPropValue); + + if (sqlExpressions != null && !sqlExpressions.isEmpty()) + { + String strPropValue = (String) evaluatedPropValue; + + checkValidExpressionProperty(model, property, strPropValue); + + evaluatedPropValue = evaluateSqlExpressions(cn, strPropValue, sqlExpressions, expressionEvaluationContext); + } + + if (evaluatedPropValue != propValue) + { + Class propertyType = property.getModel().getType(); + propValue = this.conversionService.convert(evaluatedPropValue, propertyType); + } + + return propValue; + } + + /** + * 计算给定变量表达式的值。 + * + * @param source + * 变量表达式字符串 + * @param expressions + * @param expressionEvaluationContext + * @return + * @throws VariableExpressionErrorException + */ + protected Object evaluateVariableExpressions(String source, List expressions, + ExpressionEvaluationContext expressionEvaluationContext) throws VariableExpressionErrorException + { + List expressionValues = new ArrayList(); + + for (int i = 0, len = expressions.size(); i < len; i++) + { + Expression expression = expressions.get(i); + + if (expressionEvaluationContext.containsCachedValue(expression)) + { + Object value = expressionEvaluationContext.getCachedValue(expression); + expressionValues.add(value); + } + else + { + evaluateAsVariableExpression(expression, expressionEvaluationContext, expressionValues); + } + } + + String evaluated = this.variableExpressionResolver.evaluate(source, expressions, expressionValues, ""); + + return evaluated; + } + + /** + * 计算给定SQL表达式的值。 + *

+ * 如果表达式并不符合{@linkplain #isSelectSql(String)},那么此方法则会将它作为变量表达式求值,这使得在不需要变量表达式内嵌SQL表达式的情况下,可以仅使用SQL表达式的语法,简化使用难度。 + *

+ * + * @param cn + * @param source + * SQL表达式字符串 + * @param expressions + * @param expressionEvaluationContext + * @return + * @throws SqlExpressionErrorException + * @throws VariableExpressionErrorException + */ + protected Object evaluateSqlExpressions(Connection cn, String source, List expressions, + ExpressionEvaluationContext expressionEvaluationContext) + throws SqlExpressionErrorException, VariableExpressionErrorException + { + List expressionValues = new ArrayList(); + + for (int i = 0, len = expressions.size(); i < len; i++) + { + Expression expression = expressions.get(i); + + if (expressionEvaluationContext.containsCachedValue(expression)) + { + Object value = expressionEvaluationContext.getCachedValue(expression); + expressionValues.add(value); + } + else if (isSelectSql(expression.getContent())) + { + evaluateAsSelectSqlExpression(expression, expressionEvaluationContext, expressionValues, cn); + } + else + { + evaluateAsVariableExpression(expression, expressionEvaluationContext, expressionValues); + } + } + + String evaluated = this.sqlExpressionResolver.evaluate(source, expressions, expressionValues, ""); + + return evaluated; + } + + /** + * 作为SQL表达式求值。 + * + * @param expression + * @param expressionEvaluationContext + * @param expressionValues + * @param cn + * @throws SqlExpressionErrorException + */ + protected void evaluateAsSelectSqlExpression(Expression expression, + ExpressionEvaluationContext expressionEvaluationContext, List expressionValues, Connection cn) + throws SqlExpressionErrorException + { + Statement st = null; + ResultSet rs = null; + try + { + st = cn.createStatement(); + rs = st.executeQuery(expression.getContent()); + + Object value = null; + + if (rs.next()) + value = rs.getObject(1); + + expressionValues.add(value); + expressionEvaluationContext.putCachedValue(expression, value); + } + catch (SQLException e) + { + throw new SqlExpressionErrorException(expression, e); + } + finally + { + JdbcUtil.closeResultSet(rs); + JdbcUtil.closeStatement(st); + } + } + + /** + * 作为变量表达式求值。 + * + * @param expression + * @param expressionEvaluationContext + * @param expressionValues + * @throws VariableExpressionErrorException + */ + protected void evaluateAsVariableExpression(Expression expression, + ExpressionEvaluationContext expressionEvaluationContext, List expressionValues) + throws VariableExpressionErrorException + { + try + { + Object value = this.spelExpressionParser.parseExpression(expression.getContent()) + .getValue(expressionEvaluationContext.getVariableExpressionBean()); + + expressionValues.add(value); + expressionEvaluationContext.putCachedValue(expression, value); + } + catch (Exception e) + { + throw new VariableExpressionErrorException(expression, e); + } + } + + /** + * 检查属性是否可使用表达式。 + * + * @param model + * @param property + * @param expressionPropValue + */ + protected void checkValidExpressionProperty(Model model, Property property, String expressionPropValue) + { + if (!isValidExpressionProperty(model, property)) + throw new UnsupportedModelCharacterException("[" + model + "] 's [" + property + "] is expression [" + + expressionPropValue + "], it must be single, concrete and primitive."); + } + + /** + * 判断属性是否可使用表达式值。 + * + * @param model + * @param property + * @return + */ + protected boolean isValidExpressionProperty(Model model, Property property) + { + return (MU.isSingleProperty(property) && MU.isConcretePrimitiveProperty(property)); + } + + /** + * 判断给定对象是否是表达式。 + * + * @param obj + * @return + */ + protected boolean isExpression(Object obj) + { + return this.variableExpressionResolver.isExpression(obj) || this.sqlExpressionResolver.isExpression(obj); + } + + /** + * 判断给定SQL语句是否是“SELECT”语句。 + * + * @param sql + * @return + */ + protected boolean isSelectSql(String sql) + { + if (sql == null || sql.isEmpty()) + return false; + + return Pattern.matches(SELECT_SQL_REGEX, sql); + } + + protected static final String SELECT_SQL_REGEX = "^\\s*((?i)select)\\s+\\S+[\\s\\S]*$"; +} diff --git a/datagear-persistence/src/main/java/org/datagear/persistence/support/AbstractModelPersistenceOperation.java b/datagear-persistence/src/main/java/org/datagear/persistence/support/AbstractModelPersistenceOperation.java index d390bf22..e7d7cafe 100644 --- a/datagear-persistence/src/main/java/org/datagear/persistence/support/AbstractModelPersistenceOperation.java +++ b/datagear-persistence/src/main/java/org/datagear/persistence/support/AbstractModelPersistenceOperation.java @@ -4,23 +4,6 @@ package org.datagear.persistence.support; -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.regex.Pattern; - -import org.datagear.connection.JdbcUtil; -import org.datagear.model.Model; -import org.datagear.model.Property; -import org.datagear.model.support.MU; -import org.datagear.persistence.UnsupportedModelCharacterException; -import org.datagear.persistence.support.ExpressionResolver.Expression; -import org.springframework.core.convert.ConversionService; - /** * 抽象持久化操作类。 *

@@ -42,91 +25,4 @@ public abstract class AbstractModelPersistenceOperation extends AbstractModelDat super(); } - /** - * 计算给定表达式的真正属性值。 - * - * @param cn - * @param model - * @param property - * @param expressionPropValue - * @param expressions - * @param expressionValueCache - * @param conversionService - * @param expressionResolver - * @return - */ - protected Object evaluatePropertyValueForQueryExpressions(Connection cn, Model model, Property property, - String expressionPropValue, List expressions, Map expressionValueCache, - ConversionService conversionService, ExpressionResolver expressionResolver) - { - if (!MU.isSingleProperty(property) || !MU.isConcretePrimitiveProperty(property)) - throw new UnsupportedModelCharacterException("[" + model + "] 's [" + property + "] is sql expression [" - + expressionPropValue + "], it must be single, concrete and primitive."); - - List expressionValues = new ArrayList(); - - for (int i = 0, len = expressions.size(); i < len; i++) - { - Expression expression = expressions.get(i); - - String cacheKey = (expression.hasName() ? expression.getName() : expression.getContent()); - - if (expressionValueCache.containsKey(cacheKey)) - { - Object value = expressionValueCache.get(cacheKey); - expressionValues.add(value); - } - else if (!isSelectSql(expression.getContent())) - { - expressionValues.add(expression.getExpression()); - } - else - { - Statement st = null; - ResultSet rs = null; - try - { - st = cn.createStatement(); - rs = st.executeQuery(expression.getContent()); - - Object value = null; - - if (rs.next()) - value = rs.getObject(1); - - expressionValues.add(value); - expressionValueCache.put(cacheKey, value); - } - catch (SQLException e) - { - throw new SqlExpressionErrorException(expression, e); - } - finally - { - JdbcUtil.closeResultSet(rs); - JdbcUtil.closeStatement(st); - } - } - } - - String evaluated = expressionResolver.evaluate(expressionPropValue, expressions, expressionValues, ""); - - return conversionService.convert(evaluated, property.getModel().getType()); - } - - /** - * 判断给定SQL语句是否是“SELECT”语句。 - * - * @param sql - * @return - */ - protected boolean isSelectSql(String sql) - { - if (sql == null || sql.isEmpty()) - return false; - - return Pattern.matches(SELECT_SQL_REGEX, sql); - } - - protected static final String SELECT_SQL_REGEX = "^\\s*((?i)select)\\s+\\S+[\\s\\S]*$"; } diff --git a/datagear-persistence/src/main/java/org/datagear/persistence/support/DefaultPersistenceManager.java b/datagear-persistence/src/main/java/org/datagear/persistence/support/DefaultPersistenceManager.java index b05dbf40..3e0d3deb 100644 --- a/datagear-persistence/src/main/java/org/datagear/persistence/support/DefaultPersistenceManager.java +++ b/datagear-persistence/src/main/java/org/datagear/persistence/support/DefaultPersistenceManager.java @@ -38,7 +38,9 @@ public class DefaultPersistenceManager extends AbstractModelDataAccessObject imp private ConversionService conversionService; - private ExpressionResolver expressionResolver; + private ExpressionResolver variableExpressionResolver; + + private ExpressionResolver sqlExpressionResolver; private InsertPersistenceOperation insertPersistenceOperation; @@ -59,12 +61,20 @@ public class DefaultPersistenceManager extends AbstractModelDataAccessObject imp this.dialectSource = dialectSource; this.conversionService = conversionService; - this.expressionResolver = new ExpressionResolver(); + + this.variableExpressionResolver = new VariableExpressionResolver(); + + this.sqlExpressionResolver = new SqlExpressionResolver(); + this.insertPersistenceOperation = new InsertPersistenceOperation(this.conversionService, - this.expressionResolver); + this.variableExpressionResolver, this.sqlExpressionResolver); + this.deletePersistenceOperation = new DeletePersistenceOperation(); + this.updatePersistenceOperation = new UpdatePersistenceOperation(this.insertPersistenceOperation, - this.deletePersistenceOperation, this.conversionService, this.expressionResolver); + this.deletePersistenceOperation, this.conversionService, this.variableExpressionResolver, + this.sqlExpressionResolver); + this.selectPersistenceOperation = new SelectPersistenceOperation(); } @@ -90,16 +100,28 @@ public class DefaultPersistenceManager extends AbstractModelDataAccessObject imp this.updatePersistenceOperation.setConversionService(conversionService); } - public ExpressionResolver getExpressionResolver() + public ExpressionResolver getVariableExpressionResolver() { - return expressionResolver; + return variableExpressionResolver; } - public void setExpressionResolver(ExpressionResolver expressionResolver) + public void setVariableExpressionResolver(ExpressionResolver variableExpressionResolver) { - this.expressionResolver = expressionResolver; - this.insertPersistenceOperation.setExpressionResolver(expressionResolver); - this.updatePersistenceOperation.setExpressionResolver(expressionResolver); + this.variableExpressionResolver = variableExpressionResolver; + this.insertPersistenceOperation.setVariableExpressionResolver(variableExpressionResolver); + this.updatePersistenceOperation.setVariableExpressionResolver(variableExpressionResolver); + } + + public ExpressionResolver getSqlExpressionResolver() + { + return sqlExpressionResolver; + } + + public void setSqlExpressionResolver(ExpressionResolver sqlExpressionResolver) + { + this.sqlExpressionResolver = sqlExpressionResolver; + this.insertPersistenceOperation.setSqlExpressionResolver(sqlExpressionResolver); + this.updatePersistenceOperation.setSqlExpressionResolver(sqlExpressionResolver); } public InsertPersistenceOperation getInsertPersistenceOperation() diff --git a/datagear-persistence/src/main/java/org/datagear/persistence/support/ExpressionErrorException.java b/datagear-persistence/src/main/java/org/datagear/persistence/support/ExpressionErrorException.java new file mode 100644 index 00000000..50aa4096 --- /dev/null +++ b/datagear-persistence/src/main/java/org/datagear/persistence/support/ExpressionErrorException.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018 datagear.org. All Rights Reserved. + */ + +package org.datagear.persistence.support; + +import org.datagear.persistence.PersistenceException; +import org.datagear.persistence.support.ExpressionResolver.Expression; + +/** + * {@linkplain Expression}执行出错异常。 + * + * @author datagear@163.com + * + */ +public class ExpressionErrorException extends PersistenceException +{ + private static final long serialVersionUID = 1L; + + private Expression expression; + + public ExpressionErrorException(Expression expression) + { + super(); + this.expression = expression; + } + + public ExpressionErrorException(Expression expression, Throwable cause) + { + super("Evaluate expression [" + expression.getContent() + "] error", cause); + this.expression = expression; + } + + public Expression getExpression() + { + return expression; + } +} diff --git a/datagear-persistence/src/main/java/org/datagear/persistence/support/ExpressionEvaluationContext.java b/datagear-persistence/src/main/java/org/datagear/persistence/support/ExpressionEvaluationContext.java new file mode 100644 index 00000000..ad2c322b --- /dev/null +++ b/datagear-persistence/src/main/java/org/datagear/persistence/support/ExpressionEvaluationContext.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2018 datagear.org. All Rights Reserved. + */ + +package org.datagear.persistence.support; + +import java.util.HashMap; +import java.util.Map; + +import org.datagear.persistence.support.ExpressionResolver.Expression; + +/** + * {@linkplain Expression}计算上下文。 + *

+ * 为了方便支持批量添加操作,此类的实例默认会添加{@linkplain #VARIABLE_INDEX}变量并且初值为{@code 0}。 + *

+ * + * @author datagear@163.com + * + */ +public class ExpressionEvaluationContext +{ + /** 变量名:index */ + public static final String VARIABLE_INDEX = "index"; + + /** 值缓存 */ + private Map valueCache = new HashMap(); + + /** 变量表达式求值Bean */ + private VariableExpressionBean variableExpressionBean = new VariableExpressionBean(); + + public ExpressionEvaluationContext() + { + super(); + this.variableExpressionBean.setIndex(0); + } + + public Map getValueCache() + { + return valueCache; + } + + public void setValueCache(Map valueCache) + { + this.valueCache = valueCache; + } + + public VariableExpressionBean getVariableExpressionBean() + { + return variableExpressionBean; + } + + public void setVariableExpressionBean(VariableExpressionBean variableExpressionBean) + { + this.variableExpressionBean = variableExpressionBean; + } + + /** + * 是否包含指定{@linkplain Expression}的缓存值。 + * + * @param expression + * @return + */ + public boolean containsCachedValue(Expression expression) + { + String cacheKey = getCachedValueKey(expression); + + return this.valueCache.containsKey(cacheKey); + } + + /** + * 获取指定{@linkplain Expression}的缓存值。 + * + * @param expression + * @return + */ + public Object getCachedValue(Expression expression) + { + String cacheKey = getCachedValueKey(expression); + + return this.valueCache.get(cacheKey); + } + + /** + * 将指定{@linkplain Expression}的值加入缓存。 + * + * @param expression + * @param value + */ + public void putCachedValue(Expression expression, Object value) + { + String cacheKey = getCachedValueKey(expression); + + this.valueCache.put(cacheKey, value); + } + + /** + * 获取{@linkplain #getVariableExpressionBean()}的{@linkplain VariableExpressionBean#getIndex()}的值。 + * + * @return + */ + public int getVariableIndex() + { + return this.variableExpressionBean.getIndex(); + } + + /** + * 设置{@linkplain #getVariableExpressionBean()}的{@linkplain VariableExpressionBean#getIndex()}的值。 + * + * @param value + */ + public void setVariableIndex(int value) + { + this.variableExpressionBean.setIndex(value); + } + + /** + * 将{@linkplain #getVariableExpressionBean()}的{@linkplain VariableExpressionBean#getIndex()}的值加{@code 1}并返回。 + * + * @return + */ + public int incrementVariableIndex() + { + int v = getVariableIndex() + 1; + setVariableIndex(v); + + return v; + } + + /** + * 获取缓存值关键字。 + *

+ * 为了使{@linkplain ExpressionResolver#DEFAULT_START_IDENTIFIER_DOLLAR}、 + * {@linkplain ExpressionResolver#DEFAULT_START_IDENTIFIER_SHARP}的表达式能使用同一个{@linkplain ExpressionEvaluationContext}, + * 此方法会生成"${...}""#{...}"格式的关键字。 + *

+ * + * @param expression + * + * @return + */ + protected String getCachedValueKey(Expression expression) + { + return expression.getStartIdentifier() + (expression.hasName() ? expression.getName() : expression.getContent()) + + expression.getEndIdentifier(); + } +} diff --git a/datagear-persistence/src/main/java/org/datagear/persistence/support/ExpressionResolver.java b/datagear-persistence/src/main/java/org/datagear/persistence/support/ExpressionResolver.java index 3dc42bcb..24f4a5a9 100644 --- a/datagear-persistence/src/main/java/org/datagear/persistence/support/ExpressionResolver.java +++ b/datagear-persistence/src/main/java/org/datagear/persistence/support/ExpressionResolver.java @@ -20,7 +20,11 @@ import java.util.List; */ public class ExpressionResolver { - public static final String DEFAULT_START_IDENTIFIER = "${"; + /** "${"起始标识符 */ + public static final String DEFAULT_START_IDENTIFIER_DOLLAR = "${"; + + /** "#{"起始标识符 */ + public static final String DEFAULT_START_IDENTIFIER_SHARP = "#{"; public static final String DEFAULT_END_IDENTIFIER = "}"; @@ -28,7 +32,7 @@ public class ExpressionResolver public static final char DEFAULT_ESCAPER = '\\'; - private String startIdentifier = DEFAULT_START_IDENTIFIER; + private String startIdentifier = DEFAULT_START_IDENTIFIER_DOLLAR; private String endIdentifier = DEFAULT_END_IDENTIFIER; @@ -36,7 +40,7 @@ public class ExpressionResolver private char escaper = DEFAULT_ESCAPER; - private transient char[] _startIdentifierChars = DEFAULT_START_IDENTIFIER.toCharArray(); + private transient char[] _startIdentifierChars = DEFAULT_START_IDENTIFIER_DOLLAR.toCharArray(); private transient char[] _endIdentifierChars = DEFAULT_END_IDENTIFIER.toCharArray(); @@ -142,9 +146,27 @@ public class ExpressionResolver */ public List resolve(String source) { - List expressions = new ArrayList(); + if (source == null || source.isEmpty()) + return Collections.emptyList(); - resolve(source, expressions); + List expressions = null; + + char[] cs = source.toCharArray(); + + Expression next = null; + int startIndex = 0; + + while ((next = resolveNextExpression(cs, startIndex)) != null) + { + if (expressions == null) + expressions = new ArrayList(); + + expressions.add(next); + startIndex = next.getEnd(); + } + + if (expressions == null) + expressions = Collections.emptyList(); return expressions; } @@ -192,29 +214,6 @@ public class ExpressionResolver return result.toString(); } - /** - * 解析源字符串中的{@linkplain Expression},并写入给定列表。 - * - * @param source - * @param expressions - */ - protected void resolve(String source, List expressions) - { - if (source == null || source.isEmpty()) - return; - - char[] cs = source.toCharArray(); - - Expression next = null; - int startIndex = 0; - - while ((next = resolveNextExpression(cs, startIndex)) != null) - { - expressions.add(next); - startIndex = next.getEnd(); - } - } - /** * 从给定起始位置解析下一个{@linkplain Expression}。 *

@@ -252,12 +251,12 @@ public class ExpressionResolver if (cj == escaper) { - if (cj < source.length - 1) + if (j < source.length - 1) { if (second != null) - second.append(cj); + second.append(source[j + 1]); else - first.append(cj); + first.append(source[j + 1]); } j += 1; @@ -295,7 +294,8 @@ public class ExpressionResolver content = second.toString().trim(); } - return new Expression(new String(source, i, j + 1 - i), i, j + 1, name, content); + return new Expression(this.getStartIdentifier(), this.endIdentifier, + new String(source, i, j + 1 - i), i, j + 1, name, content); } } } @@ -331,7 +331,7 @@ public class ExpressionResolver /** * 表达式。 *

- * 格式为:#{name:content}、#{content} + * 格式为:${name:content}、${content}、#{name:content}、#{content}。 *

* * @author datagear@163.com @@ -341,6 +341,12 @@ public class ExpressionResolver { private static final long serialVersionUID = 1L; + /** 起始标识符 */ + private String startIdentifier; + + /** 结束标识符 */ + private String endIdentifier; + /** 表达式字符串 */ private String expression; @@ -356,18 +362,24 @@ public class ExpressionResolver /** 表达式内容 */ private String content; - public Expression(String expression, int start, int end, String content) + public Expression(String startIdentifier, String endIdentifier, String expression, int start, int end, + String content) { super(); + this.startIdentifier = startIdentifier; + this.endIdentifier = endIdentifier; this.expression = expression; this.start = start; this.end = end; this.content = content; } - public Expression(String expression, int start, int end, String name, String content) + public Expression(String startIdentifier, String endIdentifier, String expression, int start, int end, + String name, String content) { super(); + this.startIdentifier = startIdentifier; + this.endIdentifier = endIdentifier; this.expression = expression; this.start = start; this.end = end; @@ -375,6 +387,26 @@ public class ExpressionResolver this.content = content; } + public String getStartIdentifier() + { + return startIdentifier; + } + + protected void setStartIdentifier(String startIdentifier) + { + this.startIdentifier = startIdentifier; + } + + public String getEndIdentifier() + { + return endIdentifier; + } + + protected void setEndIdentifier(String endIdentifier) + { + this.endIdentifier = endIdentifier; + } + public String getExpression() { return expression; diff --git a/datagear-persistence/src/main/java/org/datagear/persistence/support/InsertPersistenceOperation.java b/datagear-persistence/src/main/java/org/datagear/persistence/support/InsertPersistenceOperation.java index aba37c7e..6490bff1 100644 --- a/datagear-persistence/src/main/java/org/datagear/persistence/support/InsertPersistenceOperation.java +++ b/datagear-persistence/src/main/java/org/datagear/persistence/support/InsertPersistenceOperation.java @@ -6,9 +6,7 @@ package org.datagear.persistence.support; import java.sql.Connection; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import org.datagear.model.Model; import org.datagear.model.Property; @@ -24,7 +22,6 @@ import org.datagear.persistence.mapper.ModelTableMapper; import org.datagear.persistence.mapper.PropertyModelMapper; import org.datagear.persistence.mapper.PropertyTableMapper; import org.datagear.persistence.mapper.RelationMapper; -import org.datagear.persistence.support.ExpressionResolver.Expression; import org.springframework.core.convert.ConversionService; /** @@ -33,42 +30,17 @@ import org.springframework.core.convert.ConversionService; * @author datagear@163.com * */ -public class InsertPersistenceOperation extends AbstractModelPersistenceOperation +public class InsertPersistenceOperation extends AbstractExpressionModelPersistenceOperation { - private ConversionService conversionService; - - private ExpressionResolver expressionResolver; - public InsertPersistenceOperation() { super(); } - public InsertPersistenceOperation(ConversionService conversionService, ExpressionResolver expressionResolver) + public InsertPersistenceOperation(ConversionService conversionService, + ExpressionResolver variableExpressionResolver, ExpressionResolver sqlExpressionResolver) { - super(); - this.conversionService = conversionService; - this.expressionResolver = expressionResolver; - } - - public ConversionService getConversionService() - { - return conversionService; - } - - public void setConversionService(ConversionService conversionService) - { - this.conversionService = conversionService; - } - - public ExpressionResolver getExpressionResolver() - { - return expressionResolver; - } - - public void setExpressionResolver(ExpressionResolver expressionResolver) - { - this.expressionResolver = expressionResolver; + super(conversionService, variableExpressionResolver, sqlExpressionResolver); } /** @@ -83,7 +55,24 @@ public class InsertPersistenceOperation extends AbstractModelPersistenceOperatio */ public int insert(Connection cn, Dialect dialect, String table, Model model, Object obj) { - return insert(cn, dialect, table, model, obj, null, null, null, new HashMap()); + return insert(cn, dialect, table, model, obj, null, null, null, new ExpressionEvaluationContext()); + } + + /** + * 插入对象到指定表。 + * + * @param cn + * @param dialect + * @param table + * @param model + * @param obj + * @param expressionEvaluationContext + * @return + */ + public int insert(Connection cn, Dialect dialect, String table, Model model, Object obj, + ExpressionEvaluationContext expressionEvaluationContext) + { + return insert(cn, dialect, table, model, obj, null, null, null, expressionEvaluationContext); } /** @@ -97,15 +86,34 @@ public class InsertPersistenceOperation extends AbstractModelPersistenceOperatio * @param property * @param relationMapper * @param propertyValue - * @param expressionValueCache - * 用于缓存SQL表达式求值结果的映射表 * @return */ public int insertPropertyTableData(Connection cn, Dialect dialect, String table, Model model, Object obj, Property property, Object propertyValue) { return insertPropertyTableData(cn, dialect, table, model, obj, property, getRelationMapper(model, property), - propertyValue, new HashMap()); + propertyValue, new ExpressionEvaluationContext()); + } + + /** + * 插入属性表数据。 + * + * @param cn + * @param dialect + * @param table + * @param model + * @param obj + * @param property + * @param relationMapper + * @param propertyValue + * @param expressionEvaluationContext + * @return + */ + public int insertPropertyTableData(Connection cn, Dialect dialect, String table, Model model, Object obj, + Property property, Object propertyValue, ExpressionEvaluationContext expressionEvaluationContext) + { + return insertPropertyTableData(cn, dialect, table, model, obj, property, getRelationMapper(model, property), + propertyValue, expressionEvaluationContext); } /** @@ -122,18 +130,17 @@ public class InsertPersistenceOperation extends AbstractModelPersistenceOperatio * 附加列值,允许为{@code null} * @param ignorePropertyName * 忽略的属性名称,用于处理双向关联时,允许为{@code null} - * @param expressionValueCache - * 用于缓存SQL表达式求值结果的映射表 + * @param expressionEvaluationContext * */ protected int insert(Connection cn, Dialect dialect, String table, Model model, Object obj, String[] extraColumnNames, Object[] extraColumnValues, String ignorePropertyName, - Map expressionValueCache) + ExpressionEvaluationContext expressionEvaluationContext) { int count = insertModelTableData(cn, dialect, table, model, obj, extraColumnNames, extraColumnValues, - ignorePropertyName, expressionValueCache); + ignorePropertyName, expressionEvaluationContext); - insertPropertyTableData(cn, dialect, table, model, obj, ignorePropertyName, expressionValueCache); + insertPropertyTableData(cn, dialect, table, model, obj, ignorePropertyName, expressionEvaluationContext); return count; } @@ -155,13 +162,12 @@ public class InsertPersistenceOperation extends AbstractModelPersistenceOperatio * 附加列值,允许为{@code null} * @param ignorePropertyName * 忽略的属性名称,允许为{@code null} - * @param expressionValueCache - * 用于缓存SQL表达式求值结果的映射表 + * @param expressionEvaluationContext * */ protected int insertModelTableData(Connection cn, Dialect dialect, String table, Model model, Object obj, String[] extraColumnNames, Object[] extraColumnValues, String ignorePropertyName, - Map expressionValueCache) + ExpressionEvaluationContext expressionEvaluationContext) { Property[] properties = model.getProperties(); Object[] propertyValues = MU.getPropertyValues(model, obj, properties); @@ -179,14 +185,14 @@ public class InsertPersistenceOperation extends AbstractModelPersistenceOperatio Object propertyValue = propertyValues[i]; - List expressions = this.expressionResolver.resolve(propertyValue); - if (expressions != null && !expressions.isEmpty()) - { - propertyValue = evaluatePropertyValueForQueryExpressions(cn, model, property, (String) propertyValue, - expressions, expressionValueCache, this.conversionService, this.expressionResolver); + Object evalPropertyValue = evaluatePropertyValueIfExpression(cn, model, property, propertyValue, + expressionEvaluationContext); - propertyValues[i] = propertyValue; - property.set(obj, propertyValue); + if (evalPropertyValue != propertyValue) + { + propertyValue = evalPropertyValue; + propertyValues[i] = evalPropertyValue; + property.set(obj, evalPropertyValue); } } @@ -275,11 +281,10 @@ public class InsertPersistenceOperation extends AbstractModelPersistenceOperatio * @param obj * @param ignorePropertyName * 忽略的属性名称,用于处理双向关联时,允许为{@code null} - * @param expressionValueCache - * 用于缓存SQL表达式求值结果的映射表 + * @param expressionEvaluationContext */ protected void insertPropertyTableData(Connection cn, Dialect dialect, String table, Model model, Object obj, - String ignorePropertyName, Map expressionValueCache) + String ignorePropertyName, ExpressionEvaluationContext expressionEvaluationContext) { Property[] properties = model.getProperties(); @@ -295,7 +300,7 @@ public class InsertPersistenceOperation extends AbstractModelPersistenceOperatio continue; insertPropertyTableData(cn, dialect, table, model, obj, property, getRelationMapper(model, property), - propertyValue, expressionValueCache); + propertyValue, expressionEvaluationContext); } } @@ -310,17 +315,16 @@ public class InsertPersistenceOperation extends AbstractModelPersistenceOperatio * @param property * @param relationMapper * @param propertyValue - * @param expressionValueCache - * 用于缓存SQL表达式求值结果的映射表 + * @param expressionEvaluationContext * @return */ protected int insertPropertyTableData(Connection cn, Dialect dialect, String table, Model model, Object obj, Property property, RelationMapper relationMapper, Object propertyValue, - Map expressionValueCache) + ExpressionEvaluationContext expressionEvaluationContext) { int count = 0; - if (this.expressionResolver.isExpression(propertyValue)) + if (isExpression(propertyValue)) { return PERSISTENCE_IGNORED; } @@ -330,7 +334,7 @@ public class InsertPersistenceOperation extends AbstractModelPersistenceOperatio propertyValue); return insertPropertyTableData(cn, dialect, table, model, obj, property, propertyModelMapper, - toArray(propertyValue), null, expressionValueCache); + toArray(propertyValue), null, expressionEvaluationContext); } else { @@ -351,7 +355,7 @@ public class InsertPersistenceOperation extends AbstractModelPersistenceOperatio int myCount = insertPropertyTableData(cn, dialect, table, model, obj, property, PropertyModelMapper.valueOf(property, relationMapper, propertyModel), myPropValues, - myPropValueIdexes, expressionValueCache); + myPropValueIdexes, expressionEvaluationContext); if (myCount > 0) count += myCount; @@ -374,12 +378,11 @@ public class InsertPersistenceOperation extends AbstractModelPersistenceOperatio * @param propValues * @param propValueOrders * 允许为{@code null} - * @param expressionValueCache - * 用于缓存SQL表达式求值结果的映射表 + * @param expressionEvaluationContext */ protected int insertPropertyTableData(Connection cn, Dialect dialect, String table, Model model, Object obj, Property property, PropertyModelMapper propertyModelMapper, Object[] propValues, long[] propValueOrders, - Map expressionValueCache) + ExpressionEvaluationContext expressionEvaluationContext) { if (propertyModelMapper.isModelTableMapperInfo()) { @@ -396,21 +399,21 @@ public class InsertPersistenceOperation extends AbstractModelPersistenceOperatio throw new IllegalArgumentException(); return insertPropertyTableDataForCompositeModelTableMapper(cn, dialect, table, model, obj, property, mpmm, - propValues[0], expressionValueCache); + propValues[0], expressionEvaluationContext); } else if (propertyModelMapper.isPropertyTableMapperInfo()) { PropertyModelMapper ppmm = propertyModelMapper.castPropertyTableMapperInfo(); return insertPropertyTableDataForPropertyTableMapper(cn, dialect, table, model, obj, property, ppmm, - propValues, propValueOrders, expressionValueCache); + propValues, propValueOrders, expressionEvaluationContext); } else if (propertyModelMapper.isJoinTableMapperInfo()) { PropertyModelMapper jpmm = propertyModelMapper.castJoinTableMapperInfo(); return insertPropertyTableDataForJoinTableMapper(cn, dialect, table, model, obj, property, jpmm, propValues, - propValueOrders, expressionValueCache); + propValueOrders, expressionEvaluationContext); } else throw new UnsupportedOperationException(); @@ -427,13 +430,12 @@ public class InsertPersistenceOperation extends AbstractModelPersistenceOperatio * @param property * @param propertyModelMapper * @param propValue - * @param expressionValueCache - * 用于缓存SQL表达式求值结果的映射表 + * @param expressionEvaluationContext * */ protected int insertPropertyTableDataForCompositeModelTableMapper(Connection cn, Dialect dialect, String table, Model model, Object obj, Property property, PropertyModelMapper propertyModelMapper, - Object propValue, Map expressionValueCache) + Object propValue, ExpressionEvaluationContext expressionEvaluationContext) { Model propertyModel = propertyModelMapper.getModel(); @@ -441,7 +443,7 @@ public class InsertPersistenceOperation extends AbstractModelPersistenceOperatio return PERSISTENCE_IGNORED; return insert(cn, dialect, getTableName(propertyModel), propertyModel, propValue, null, null, null, - expressionValueCache); + expressionEvaluationContext); } /** @@ -457,25 +459,24 @@ public class InsertPersistenceOperation extends AbstractModelPersistenceOperatio * @param propValues * @param propValueOrders * 允许为{@code null} - * @param expressionValueCache - * 用于缓存SQL表达式求值结果的映射表 + * @param expressionEvaluationContext * @return */ protected int insertPropertyTableDataForPropertyTableMapper(Connection cn, Dialect dialect, String table, Model model, Object obj, Property property, PropertyModelMapper propertyModelMapper, - Object[] propValues, long[] propValueOrders, Map expressionValueCache) + Object[] propValues, long[] propValueOrders, ExpressionEvaluationContext expressionEvaluationContext) { PropertyTableMapper mapper = propertyModelMapper.getMapper(); if (mapper.isPrimitivePropertyMapper()) { return insertPropertyTableDataForPrimitiveValuePropertyTableMapper(cn, dialect, table, model, obj, property, - propertyModelMapper, propValues, propValueOrders, expressionValueCache); + propertyModelMapper, propValues, propValueOrders, expressionEvaluationContext); } else { return insertPropertyTableDataForCompositePropertyTableMapper(cn, dialect, table, model, obj, property, - propertyModelMapper, propValues, propValueOrders, expressionValueCache); + propertyModelMapper, propValues, propValueOrders, expressionEvaluationContext); } } @@ -492,14 +493,13 @@ public class InsertPersistenceOperation extends AbstractModelPersistenceOperatio * @param propValues * @param propValueOrders * 允许为{@code null} - * @param expressionValueCache - * 用于缓存SQL表达式求值结果的映射表 + * @param expressionEvaluationContext * */ protected int insertPropertyTableDataForPrimitiveValuePropertyTableMapper(Connection cn, Dialect dialect, String table, Model model, Object obj, Property property, PropertyModelMapper propertyModelMapper, Object[] propValues, long[] propValueOrders, - Map expressionValueCache) + ExpressionEvaluationContext expressionEvaluationContext) { int count = 0; @@ -531,11 +531,13 @@ public class InsertPersistenceOperation extends AbstractModelPersistenceOperatio { Object propertyValue = propValues[i]; - List expressions = this.expressionResolver.resolve(propertyValue); - if (expressions != null && !expressions.isEmpty()) + Object evalPropertyValue = evaluatePropertyValueIfExpression(cn, model, property, propertyValue, + expressionEvaluationContext); + + if (evalPropertyValue != propertyValue) { - propertyValue = evaluatePropertyValueForQueryExpressions(cn, model, property, (String) propertyValue, - expressions, expressionValueCache, this.conversionService, this.expressionResolver); + propertyValue = evalPropertyValue; + propValues[i] = evalPropertyValue; } Object columnValue = getColumnValue(cn, model, property, propertyModelMapper, propertyValue); @@ -569,13 +571,12 @@ public class InsertPersistenceOperation extends AbstractModelPersistenceOperatio * @param propValues * @param propValueOrders * 允许为{@code null} - * @param expressionValueCache - * 用于缓存SQL表达式求值结果的映射表 + * @param expressionEvaluationContext * @return */ protected int insertPropertyTableDataForCompositePropertyTableMapper(Connection cn, Dialect dialect, String table, Model model, Object obj, Property property, PropertyModelMapper propertyModelMapper, - Object[] propValues, long[] propValueOrders, Map expressionValueCache) + Object[] propValues, long[] propValueOrders, ExpressionEvaluationContext expressionEvaluationContext) { int count = 0; @@ -596,7 +597,7 @@ public class InsertPersistenceOperation extends AbstractModelPersistenceOperatio (propValueOrders == null ? null : propValueOrders[i])); int myCount = insert(cn, dialect, ptable, propertyModel, propValues[i], allMapperColumNames, - allMapperColumnValues, getMappedByWith(mapper), expressionValueCache); + allMapperColumnValues, getMappedByWith(mapper), expressionEvaluationContext); if (myCount > 0) count += myCount; @@ -693,13 +694,12 @@ public class InsertPersistenceOperation extends AbstractModelPersistenceOperatio * @param propValues * @param propValueOrders * 允许为{@code null} - * @param expressionValueCache - * 用于缓存SQL表达式求值结果的映射表 + * @param expressionEvaluationContext * */ protected int insertPropertyTableDataForJoinTableMapper(Connection cn, Dialect dialect, String table, Model model, Object obj, Property property, PropertyModelMapper propertyModelMapper, - Object[] propValues, long[] propValueOrders, Map expressionValueCache) + Object[] propValues, long[] propValueOrders, ExpressionEvaluationContext expressionEvaluationContext) { Model propertyModel = propertyModelMapper.getModel(); @@ -709,7 +709,8 @@ public class InsertPersistenceOperation extends AbstractModelPersistenceOperatio { if (PMU.isPrivate(model, property, propertyModel)) { - insert(cn, dialect, ptable, propertyModel, propValues[i], null, null, null, expressionValueCache); + insert(cn, dialect, ptable, propertyModel, propValues[i], null, null, null, + expressionEvaluationContext); } } @@ -844,7 +845,7 @@ public class InsertPersistenceOperation extends AbstractModelPersistenceOperatio if (valueGenerator != null) propValue = valueGenerator.generate(model, property, obj); - if (propValue != null && this.expressionResolver.isExpression(propValue)) + if (propValue != null && isExpression(propValue)) property.set(obj, propValue); } } diff --git a/datagear-persistence/src/main/java/org/datagear/persistence/support/SqlExpressionErrorException.java b/datagear-persistence/src/main/java/org/datagear/persistence/support/SqlExpressionErrorException.java index f97d4eca..76e9ab22 100644 --- a/datagear-persistence/src/main/java/org/datagear/persistence/support/SqlExpressionErrorException.java +++ b/datagear-persistence/src/main/java/org/datagear/persistence/support/SqlExpressionErrorException.java @@ -6,7 +6,6 @@ package org.datagear.persistence.support; import java.sql.SQLException; -import org.datagear.persistence.PersistenceException; import org.datagear.persistence.support.ExpressionResolver.Expression; /** @@ -15,20 +14,12 @@ import org.datagear.persistence.support.ExpressionResolver.Expression; * @author datagear@163.com * */ -public class SqlExpressionErrorException extends PersistenceException +public class SqlExpressionErrorException extends ExpressionErrorException { private static final long serialVersionUID = 1L; - private Expression expression; - public SqlExpressionErrorException(Expression expression, SQLException cause) { - super(expression.getContent(), cause); - this.expression = expression; - } - - public Expression getExpression() - { - return expression; + super(expression, cause); } } diff --git a/datagear-persistence/src/main/java/org/datagear/persistence/support/SqlExpressionResolver.java b/datagear-persistence/src/main/java/org/datagear/persistence/support/SqlExpressionResolver.java new file mode 100644 index 00000000..36d7533a --- /dev/null +++ b/datagear-persistence/src/main/java/org/datagear/persistence/support/SqlExpressionResolver.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 datagear.org. All Rights Reserved. + */ + +package org.datagear.persistence.support; + +/** + * SQL表达式解析器。 + *

+ * 此类将表达式格式固化为${name:value}、${value},用于解析SQL表达式。 + *

+ * + * @author datagear@163.com + * + */ +public class SqlExpressionResolver extends ExpressionResolver +{ + public SqlExpressionResolver() + { + super(); + super.setStartIdentifier(DEFAULT_START_IDENTIFIER_DOLLAR); + super.setSeparator(DEFAULT_SEPARATOR); + super.setEndIdentifier(DEFAULT_END_IDENTIFIER); + } + + @Override + public void setStartIdentifier(String startIdentifier) + { + throw new UnsupportedOperationException(); + } + + @Override + public void setEndIdentifier(String endIdentifier) + { + throw new UnsupportedOperationException(); + } + + @Override + public void setSeparator(String separator) + { + throw new UnsupportedOperationException(); + } +} diff --git a/datagear-persistence/src/main/java/org/datagear/persistence/support/UpdatePersistenceOperation.java b/datagear-persistence/src/main/java/org/datagear/persistence/support/UpdatePersistenceOperation.java index b6904fd0..680dcd10 100644 --- a/datagear-persistence/src/main/java/org/datagear/persistence/support/UpdatePersistenceOperation.java +++ b/datagear-persistence/src/main/java/org/datagear/persistence/support/UpdatePersistenceOperation.java @@ -6,9 +6,7 @@ package org.datagear.persistence.support; import java.sql.Connection; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import org.datagear.model.Model; import org.datagear.model.Property; @@ -24,7 +22,6 @@ import org.datagear.persistence.mapper.ModelTableMapper; import org.datagear.persistence.mapper.PropertyModelMapper; import org.datagear.persistence.mapper.PropertyTableMapper; import org.datagear.persistence.mapper.RelationMapper; -import org.datagear.persistence.support.ExpressionResolver.Expression; import org.springframework.core.convert.ConversionService; /** @@ -33,7 +30,7 @@ import org.springframework.core.convert.ConversionService; * @author datagear@163.com * */ -public class UpdatePersistenceOperation extends AbstractModelPersistenceOperation +public class UpdatePersistenceOperation extends AbstractExpressionModelPersistenceOperation { /** 当记录未做修改时,返回此标识 */ public static final int UNCHANGED = PERSISTENCE_IGNORED - 1; @@ -45,10 +42,6 @@ public class UpdatePersistenceOperation extends AbstractModelPersistenceOperatio private DeletePersistenceOperation deletePersistenceOperation; - private ConversionService conversionService; - - private ExpressionResolver expressionResolver; - public UpdatePersistenceOperation() { super(); @@ -56,13 +49,11 @@ public class UpdatePersistenceOperation extends AbstractModelPersistenceOperatio public UpdatePersistenceOperation(InsertPersistenceOperation insertPersistenceOperation, DeletePersistenceOperation deletePersistenceOperation, ConversionService conversionService, - ExpressionResolver expressionResolver) + ExpressionResolver variableExpressionResolver, ExpressionResolver sqlExpressionResolver) { - super(); + super(conversionService, variableExpressionResolver, sqlExpressionResolver); this.insertPersistenceOperation = insertPersistenceOperation; this.deletePersistenceOperation = deletePersistenceOperation; - this.conversionService = conversionService; - this.expressionResolver = expressionResolver; } public boolean isHandleMultipleProperty() @@ -95,26 +86,6 @@ public class UpdatePersistenceOperation extends AbstractModelPersistenceOperatio this.deletePersistenceOperation = deletePersistenceOperation; } - public ConversionService getConversionService() - { - return conversionService; - } - - public void setConversionService(ConversionService conversionService) - { - this.conversionService = conversionService; - } - - public ExpressionResolver getExpressionResolver() - { - return expressionResolver; - } - - public void setExpressionResolver(ExpressionResolver expressionResolver) - { - this.expressionResolver = expressionResolver; - } - /** * 更新。 * @@ -122,8 +93,6 @@ public class UpdatePersistenceOperation extends AbstractModelPersistenceOperatio * @param dialect * @param table * @param model - * @param originalCondition - * 用于确定原始数据记录的模型表条件 * @param originalObj * 原始数据 * @param updateObj @@ -135,7 +104,30 @@ public class UpdatePersistenceOperation extends AbstractModelPersistenceOperatio SqlBuilder originalCondition = buildRecordCondition(cn, dialect, model, originalObj, null); return update(cn, dialect, table, model, originalCondition, originalObj, updateObj, null, null, null, - new HashMap()); + new ExpressionEvaluationContext()); + } + + /** + * 更新。 + * + * @param cn + * @param dialect + * @param table + * @param model + * @param originalObj + * 原始数据 + * @param updateObj + * 待更新的数据 + * @param expressionEvaluationContext + * @return + */ + public int update(Connection cn, Dialect dialect, String table, Model model, Object originalObj, Object updateObj, + ExpressionEvaluationContext expressionEvaluationContext) + { + SqlBuilder originalCondition = buildRecordCondition(cn, dialect, model, originalObj, null); + + return update(cn, dialect, table, model, originalCondition, originalObj, updateObj, null, null, null, + expressionEvaluationContext); } /** @@ -159,7 +151,32 @@ public class UpdatePersistenceOperation extends AbstractModelPersistenceOperatio Object updatePropertyValue) { return updatePropertyTableData(cn, dialect, table, model, condition, property, propertyModelMapper, - originalPropertyValue, updatePropertyValue, null, true, new HashMap()); + originalPropertyValue, updatePropertyValue, null, true, new ExpressionEvaluationContext()); + } + + /** + * 更新属性表数据。 + * + * @param cn + * @param dialect + * @param table + * @param model + * @param condition + * @param property + * @param propertyModelMapper + * @param originalPropertyValue + * 原始属性值 + * @param updatePropertyValue + * 待更新的属性值,允许为{@code null} + * @param expressionEvaluationContext + * @return + */ + public int updatePropertyTableData(Connection cn, Dialect dialect, String table, Model model, SqlBuilder condition, + Property property, PropertyModelMapper propertyModelMapper, Object originalPropertyValue, + Object updatePropertyValue, ExpressionEvaluationContext expressionEvaluationContext) + { + return updatePropertyTableData(cn, dialect, table, model, condition, property, propertyModelMapper, + originalPropertyValue, updatePropertyValue, null, true, expressionEvaluationContext); } /** @@ -181,13 +198,12 @@ public class UpdatePersistenceOperation extends AbstractModelPersistenceOperatio * 附加列值,允许为{@code null} * @param ignorePropertyName * 忽略的属性名称,用于处理双向关联时,允许为{@code null} - * @param expressionValueCache - * 用于缓存SQL表达式求值结果的映射表 + * @param expressionEvaluationContext * @return */ protected int update(Connection cn, Dialect dialect, String table, Model model, SqlBuilder originalCondition, Object originalObj, Object updateObj, String[] extraColumnNames, Object[] extraColumnValues, - String ignorePropertyName, Map expressionValueCache) + String ignorePropertyName, ExpressionEvaluationContext expressionEvaluationContext) { int count = 0; @@ -207,14 +223,14 @@ public class UpdatePersistenceOperation extends AbstractModelPersistenceOperatio Object propertyValue = updatePropertyValues[i]; - List expressions = this.expressionResolver.resolve(propertyValue); - if (expressions != null && !expressions.isEmpty()) - { - propertyValue = evaluatePropertyValueForQueryExpressions(cn, model, property, (String) propertyValue, - expressions, expressionValueCache, this.conversionService, this.expressionResolver); + Object evalPropertyValue = evaluatePropertyValueIfExpression(cn, model, property, propertyValue, + expressionEvaluationContext); - updatePropertyValues[i] = propertyValue; - property.set(updateObj, propertyValue); + if (evalPropertyValue != propertyValue) + { + propertyValue = evalPropertyValue; + updatePropertyValues[i] = evalPropertyValue; + property.set(updateObj, evalPropertyValue); } } @@ -268,12 +284,12 @@ public class UpdatePersistenceOperation extends AbstractModelPersistenceOperatio { int myUpdateCount = updatePropertyTableData(cn, dialect, table, model, originalCondition, property, pmm, originalPropertyValue, updatePropertyValue, updateObj, false, - expressionValueCache); + expressionEvaluationContext); if (myUpdateCount == 0) insertPersistenceOperation.insertPropertyTableData(cn, dialect, table, model, updateObj, property, pmm, new Object[] { updatePropertyValue }, null, - expressionValueCache); + expressionEvaluationContext); } else { @@ -307,12 +323,12 @@ public class UpdatePersistenceOperation extends AbstractModelPersistenceOperatio int myUpdateCount = updatePropertyTableData(cn, dialect, table, model, updateCondition, updateInfo.getProperty(), updateInfo.getPropertyModelMapper(), originalPropertyValues[updateInfo.getPropertyIndex()], updatePropertyValue, null, false, - expressionValueCache); + expressionEvaluationContext); if (myUpdateCount == 0) insertPersistenceOperation.insertPropertyTableData(cn, dialect, table, model, updateObj, updateInfo.getProperty(), updateInfo.getPropertyModelMapper(), - new Object[] { updatePropertyValue }, null, expressionValueCache); + new Object[] { updatePropertyValue }, null, expressionEvaluationContext); } } @@ -427,14 +443,13 @@ public class UpdatePersistenceOperation extends AbstractModelPersistenceOperatio * 需要处理外键更新的对象,允许为{@code null} * @param updateModelTable * 是否更新模型表数据 - * @param expressionValueCache - * 用于缓存SQL表达式求值结果的映射表 + * @param expressionEvaluationContext * @return */ protected int updatePropertyTableData(Connection cn, Dialect dialect, String table, Model model, SqlBuilder condition, Property property, PropertyModelMapper propertyModelMapper, Object originalPropertyValue, Object updatePropertyValue, Object keyUpdateObj, boolean updateModelTable, - Map expressionValueCache) + ExpressionEvaluationContext expressionEvaluationContext) { int count = 0; @@ -443,21 +458,21 @@ public class UpdatePersistenceOperation extends AbstractModelPersistenceOperatio PropertyModelMapper mpmm = propertyModelMapper.castModelTableMapperInfo(); count = updatePropertyTableDataForModelTableMapper(cn, dialect, table, model, condition, property, mpmm, - originalPropertyValue, updatePropertyValue, updateModelTable, expressionValueCache); + originalPropertyValue, updatePropertyValue, updateModelTable, expressionEvaluationContext); } else if (propertyModelMapper.isPropertyTableMapperInfo()) { PropertyModelMapper ppmm = propertyModelMapper.castPropertyTableMapperInfo(); count = updatePropertyTableDataForPropertyTableMapper(cn, dialect, table, model, condition, property, ppmm, - originalPropertyValue, updatePropertyValue, keyUpdateObj, expressionValueCache); + originalPropertyValue, updatePropertyValue, keyUpdateObj, expressionEvaluationContext); } else if (propertyModelMapper.isJoinTableMapperInfo()) { PropertyModelMapper jpmm = propertyModelMapper.castJoinTableMapperInfo(); count = updatePropertyTableDataForJoinTableMapper(cn, dialect, table, model, condition, property, jpmm, - originalPropertyValue, updatePropertyValue, keyUpdateObj, expressionValueCache); + originalPropertyValue, updatePropertyValue, keyUpdateObj, expressionEvaluationContext); } else throw new UnsupportedOperationException(); @@ -482,14 +497,13 @@ public class UpdatePersistenceOperation extends AbstractModelPersistenceOperatio * 待更新的属性值,允许为{@code null} * @param updateModelTable * 是否更新模型表数据 - * @param expressionValueCache - * 用于缓存SQL表达式求值结果的映射表 + * @param expressionEvaluationContext * @return */ protected int updatePropertyTableDataForModelTableMapper(Connection cn, Dialect dialect, String table, Model model, SqlBuilder condition, Property property, PropertyModelMapper propertyModelMapper, Object originalPropertyValue, Object updatePropertyValue, boolean updateModelTable, - Map expressionValueCache) + ExpressionEvaluationContext expressionEvaluationContext) { int count = 0; @@ -519,7 +533,7 @@ public class UpdatePersistenceOperation extends AbstractModelPersistenceOperatio { count = update(cn, dialect, table, pmodel, buildRecordCondition(cn, dialect, pmodel, originalPropertyValue, null), originalPropertyValue, - updatePropertyValue, null, null, getMappedByWith(mapper), expressionValueCache); + updatePropertyValue, null, null, getMappedByWith(mapper), expressionEvaluationContext); if (updateModelTable) { @@ -571,14 +585,13 @@ public class UpdatePersistenceOperation extends AbstractModelPersistenceOperatio * 待更新的属性值,允许为{@code null} * @param keyUpdateObj * 需要处理外键更新的对象,允许为{@code null} - * @param expressionValueCache - * 用于缓存SQL表达式求值结果的映射表 + * @param expressionEvaluationContext * @return */ protected int updatePropertyTableDataForPropertyTableMapper(Connection cn, Dialect dialect, String table, Model model, SqlBuilder condition, Property property, PropertyModelMapper propertyModelMapper, Object originalPropertyValue, - Object updatePropertyValue, Object keyUpdateObj, Map expressionValueCache) + Object updatePropertyValue, Object keyUpdateObj, ExpressionEvaluationContext expressionEvaluationContext) { int count = 0; @@ -621,15 +634,14 @@ public class UpdatePersistenceOperation extends AbstractModelPersistenceOperatio { String columnName = toQuoteName(dialect, mapper.getPrimitiveColumnName()); - List expressions = this.expressionResolver.resolve(updatePropertyValue); - if (expressions != null && !expressions.isEmpty()) - { - updatePropertyValue = evaluatePropertyValueForQueryExpressions(cn, model, property, - (String) updatePropertyValue, expressions, expressionValueCache, this.conversionService, - this.expressionResolver); + Object evalUpdatePropertyValue = evaluatePropertyValueIfExpression(cn, model, property, + updatePropertyValue, expressionEvaluationContext); + if (evalUpdatePropertyValue != updatePropertyValue) + { + updatePropertyValue = evalUpdatePropertyValue; Object columnValue = getColumnValue(cn, model, property, propertyModelMapper, - updatePropertyValue); + evalUpdatePropertyValue); sql.sqldSuffix(columnName, "=" + columnValue); } @@ -653,7 +665,7 @@ public class UpdatePersistenceOperation extends AbstractModelPersistenceOperatio { count = update(cn, dialect, getTableName(propertyModel), propertyModel, ptableCondition, originalPropertyValue, updatePropertyValue, mkeyColumnNames, mkeyColumnValues, - getMappedByWith(propertyModelMapper.getMapper()), expressionValueCache); + getMappedByWith(propertyModelMapper.getMapper()), expressionEvaluationContext); } return count; @@ -675,14 +687,13 @@ public class UpdatePersistenceOperation extends AbstractModelPersistenceOperatio * 待更新的属性值,允许为{@code null} * @param keyUpdateObj * 需要处理外键更新的对象,允许为{@code null} - * @param expressionValueCache - * 用于缓存SQL表达式求值结果的映射表 + * @param expressionEvaluationContext * @return */ protected int updatePropertyTableDataForJoinTableMapper(Connection cn, Dialect dialect, String table, Model model, SqlBuilder condition, Property property, PropertyModelMapper propertyModelMapper, Object originalPropertyValue, Object updatePropertyValue, Object keyUpdateObj, - Map expressionValueCache) + ExpressionEvaluationContext expressionEvaluationContext) { int count = 0; @@ -701,7 +712,7 @@ public class UpdatePersistenceOperation extends AbstractModelPersistenceOperatio { count = update(cn, dialect, getTableName(propertyModel), propertyModel, ptableCondition, originalPropertyValue, updatePropertyValue, null, null, - getMappedByWith(propertyModelMapper.getMapper()), expressionValueCache); + getMappedByWith(propertyModelMapper.getMapper()), expressionEvaluationContext); if (keyUpdateObj != null) { diff --git a/datagear-persistence/src/main/java/org/datagear/persistence/support/VariableExpressionBean.java b/datagear-persistence/src/main/java/org/datagear/persistence/support/VariableExpressionBean.java new file mode 100644 index 00000000..6f376e5e --- /dev/null +++ b/datagear-persistence/src/main/java/org/datagear/persistence/support/VariableExpressionBean.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018 datagear.org. All Rights Reserved. + */ + +package org.datagear.persistence.support; + +/** + * 用于变量表达式求值的Bean。 + * + * @author datagear@163.com + * + */ +public class VariableExpressionBean +{ + private int index; + + public VariableExpressionBean() + { + super(); + } + + public VariableExpressionBean(int index) + { + super(); + this.index = index; + } + + public int getIndex() + { + return index; + } + + public void setIndex(int index) + { + this.index = index; + } +} diff --git a/datagear-persistence/src/main/java/org/datagear/persistence/support/VariableExpressionErrorException.java b/datagear-persistence/src/main/java/org/datagear/persistence/support/VariableExpressionErrorException.java new file mode 100644 index 00000000..3e59290b --- /dev/null +++ b/datagear-persistence/src/main/java/org/datagear/persistence/support/VariableExpressionErrorException.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018 datagear.org. All Rights Reserved. + */ + +package org.datagear.persistence.support; + +import org.datagear.persistence.support.ExpressionResolver.Expression; + +/** + * 变量表达式执行出错。 + * + * @author datagear@163.com + * + */ +public class VariableExpressionErrorException extends ExpressionErrorException +{ + private static final long serialVersionUID = 1L; + + public VariableExpressionErrorException(Expression expression, Exception cause) + { + super(expression, cause); + } +} diff --git a/datagear-persistence/src/main/java/org/datagear/persistence/support/VariableExpressionResolver.java b/datagear-persistence/src/main/java/org/datagear/persistence/support/VariableExpressionResolver.java new file mode 100644 index 00000000..5ea17171 --- /dev/null +++ b/datagear-persistence/src/main/java/org/datagear/persistence/support/VariableExpressionResolver.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 datagear.org. All Rights Reserved. + */ + +package org.datagear.persistence.support; + +/** + * 变量表达式解析器。 + *

+ * 此类将表达式格式固化为#{name:value}、#{value},用于解析变量表达式。 + *

+ * + * @author datagear@163.com + * + */ +public class VariableExpressionResolver extends ExpressionResolver +{ + public VariableExpressionResolver() + { + super(); + super.setStartIdentifier(DEFAULT_START_IDENTIFIER_SHARP); + super.setSeparator(DEFAULT_SEPARATOR); + super.setEndIdentifier(DEFAULT_END_IDENTIFIER); + } + + @Override + public void setStartIdentifier(String startIdentifier) + { + throw new UnsupportedOperationException(); + } + + @Override + public void setEndIdentifier(String endIdentifier) + { + throw new UnsupportedOperationException(); + } + + @Override + public void setSeparator(String separator) + { + throw new UnsupportedOperationException(); + } +} diff --git a/datagear-persistence/src/test/java/org/datagear/persistence/support/AbstractModelPersistenceOperationTest.java b/datagear-persistence/src/test/java/org/datagear/persistence/support/AbstractExpressionModelPersistenceOperationTest.java similarity index 79% rename from datagear-persistence/src/test/java/org/datagear/persistence/support/AbstractModelPersistenceOperationTest.java rename to datagear-persistence/src/test/java/org/datagear/persistence/support/AbstractExpressionModelPersistenceOperationTest.java index a700d7e6..94195fa8 100644 --- a/datagear-persistence/src/test/java/org/datagear/persistence/support/AbstractModelPersistenceOperationTest.java +++ b/datagear-persistence/src/test/java/org/datagear/persistence/support/AbstractExpressionModelPersistenceOperationTest.java @@ -13,12 +13,12 @@ import org.junit.Test; * @author datagear@163.com * */ -public class AbstractModelPersistenceOperationTest +public class AbstractExpressionModelPersistenceOperationTest { @Test public void isSelectSqlTest() { - AbstractModelPersistenceOperation persistenceOperation = new AbstractModelPersistenceOperation() + AbstractExpressionModelPersistenceOperation persistenceOperation = new AbstractExpressionModelPersistenceOperation() { }; diff --git a/datagear-persistence/src/test/java/org/datagear/persistence/support/ExpressionResolverTest.java b/datagear-persistence/src/test/java/org/datagear/persistence/support/ExpressionResolverTest.java index 7b81e06c..4ae9626a 100644 --- a/datagear-persistence/src/test/java/org/datagear/persistence/support/ExpressionResolverTest.java +++ b/datagear-persistence/src/test/java/org/datagear/persistence/support/ExpressionResolverTest.java @@ -169,6 +169,21 @@ public class ExpressionResolverTest Assert.assertEquals("content2", e.getContent()); } } + + { + List expressions = expressionResolver.resolve("prefix\\${content0}"); + + Assert.assertEquals(0, expressions.size()); + } + + { + List expressions = expressionResolver.resolve("prefix${cont\\:ent\\}0}"); + + Assert.assertEquals(1, expressions.size()); + + Expression e = expressions.get(0); + Assert.assertEquals("cont:ent}0", e.getContent()); + } } @Test diff --git a/datagear-persistence/src/test/java/org/datagear/persistence/support/SpelExpressionParserTest.java b/datagear-persistence/src/test/java/org/datagear/persistence/support/SpelExpressionParserTest.java new file mode 100644 index 00000000..6542d714 --- /dev/null +++ b/datagear-persistence/src/test/java/org/datagear/persistence/support/SpelExpressionParserTest.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2018 datagear.org. All Rights Reserved. + */ + +package org.datagear.persistence.support; + +import org.junit.Assert; +import org.junit.Test; +import org.springframework.expression.spel.standard.SpelExpressionParser; + +/** + * {@linkplain SpelExpressionParser}测试类。 + * + * @author datagear@163.com + * + */ +public class SpelExpressionParserTest +{ + private SpelExpressionParser spelExpressionParser = new SpelExpressionParser(); + + @Test + public void test() + { + { + int value = (Integer) spelExpressionParser.parseExpression("index").getValue(new TestBean()); + Assert.assertEquals(0, value); + } + + { + int value = (Integer) spelExpressionParser.parseExpression("index").getValue(new TestBean(2)); + Assert.assertEquals(2, value); + } + + { + int value = (Integer) spelExpressionParser.parseExpression("index * 5").getValue(new TestBean(2)); + Assert.assertEquals(10, value); + } + } + + protected static class TestBean + { + private int index; + + public TestBean() + { + super(); + } + + public TestBean(int index) + { + super(); + this.index = index; + } + + public int getIndex() + { + return index; + } + + public void setIndex(int index) + { + this.index = index; + } + } +} diff --git a/datagear-web/src/main/java/org/datagear/web/controller/ControllerAdvice.java b/datagear-web/src/main/java/org/datagear/web/controller/ControllerAdvice.java index ca13944c..f9ffcfbe 100644 --- a/datagear-web/src/main/java/org/datagear/web/controller/ControllerAdvice.java +++ b/datagear-web/src/main/java/org/datagear/web/controller/ControllerAdvice.java @@ -23,6 +23,7 @@ import org.datagear.dbmodel.DatabaseModelResolverException; import org.datagear.persistence.PersistenceException; import org.datagear.persistence.UnsupportedDialectException; import org.datagear.persistence.support.SqlExpressionErrorException; +import org.datagear.persistence.support.VariableExpressionErrorException; import org.datagear.web.convert.IllegalSourceValueException; import org.springframework.http.HttpStatus; import org.springframework.validation.BindException; @@ -50,7 +51,8 @@ public class ControllerAdvice extends AbstractController public String handleControllerMissingServletRequestParameterException(HttpServletRequest request, HttpServletResponse response, MissingServletRequestParameterException exception) { - setOperationMessageForException(request, exception, false); + setOperationMessageForThrowable(request, buildMessageCode(MissingServletRequestParameterException.class), + exception, false); return ERROR_PAGE_URL; } @@ -60,7 +62,7 @@ public class ControllerAdvice extends AbstractController public String handleControllerBindException(HttpServletRequest request, HttpServletResponse response, BindException exception) { - setOperationMessageForException(request, exception, false); + setOperationMessageForThrowable(request, buildMessageCode(BindException.class), exception, false); return ERROR_PAGE_URL; } @@ -70,7 +72,8 @@ public class ControllerAdvice extends AbstractController public String handleControllerMethodArgumentNotValidException(HttpServletRequest request, HttpServletResponse response, MethodArgumentNotValidException exception) { - setOperationMessageForException(request, exception, false); + setOperationMessageForThrowable(request, buildMessageCode(MethodArgumentNotValidException.class), exception, + false); return ERROR_PAGE_URL; } @@ -80,7 +83,8 @@ public class ControllerAdvice extends AbstractController public String handleAuthenticationFailedException(HttpServletRequest request, HttpServletResponse response, AuthenticationFailedException exception) { - setOperationMessageForException(request, exception, true); + setOperationMessageForThrowable(request, buildMessageCode(AuthenticationFailedException.class), exception, + true); return ERROR_PAGE_URL; } @@ -90,7 +94,7 @@ public class ControllerAdvice extends AbstractController public String handleControllerIllegalInputException(HttpServletRequest request, HttpServletResponse response, IllegalInputException exception) { - setOperationMessageForException(request, exception, false); + setOperationMessageForThrowable(request, buildMessageCode(IllegalInputException.class), exception, false); return ERROR_PAGE_URL; } @@ -100,7 +104,7 @@ public class ControllerAdvice extends AbstractController public String handleControllerRecordNotFoundException(HttpServletRequest request, HttpServletResponse response, RecordNotFoundException exception) { - setOperationMessageForException(request, exception, false); + setOperationMessageForThrowable(request, buildMessageCode(RecordNotFoundException.class), exception, false); return ERROR_PAGE_URL; } @@ -110,7 +114,8 @@ public class ControllerAdvice extends AbstractController public String handleControllerRecordNotFoundOrNoPermissionException(HttpServletRequest request, HttpServletResponse response, RecordNotFoundOrPermissionDeniedException exception) { - setOperationMessageForException(request, exception, false); + setOperationMessageForThrowable(request, buildMessageCode(RecordNotFoundOrPermissionDeniedException.class), + exception, false); return ERROR_PAGE_URL; } @@ -120,7 +125,7 @@ public class ControllerAdvice extends AbstractController public String handleControllerSchemaNotFoundException(HttpServletRequest request, HttpServletResponse response, SchemaNotFoundException exception) { - setOperationMessageForException(request, exception, false); + setOperationMessageForThrowable(request, buildMessageCode(SchemaNotFoundException.class), exception, false); return ERROR_PAGE_URL; } @@ -130,8 +135,8 @@ public class ControllerAdvice extends AbstractController public String handleControllerIllegalSourceValueException(HttpServletRequest request, HttpServletResponse response, IllegalSourceValueException exception) { - setOperationMessageForException(request, exception, false, exception.getSourceValue(), - exception.getTargetType().getName()); + setOperationMessageForThrowable(request, buildMessageCode(IllegalSourceValueException.class), exception, false, + exception.getSourceValue(), exception.getTargetType().getName()); return ERROR_PAGE_URL; } @@ -141,7 +146,19 @@ public class ControllerAdvice extends AbstractController public String handleControllerFileNotFoundException(HttpServletRequest request, HttpServletResponse response, FileNotFoundException exception) { - setOperationMessageForException(request, exception, false, exception.getFileName()); + setOperationMessageForThrowable(request, buildMessageCode(FileNotFoundException.class), exception, false, + exception.getFileName()); + + return ERROR_PAGE_URL; + } + + @ExceptionHandler(VariableExpressionErrorException.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public String handlePersistenceVariableExpressionErrorException(HttpServletRequest request, + HttpServletResponse response, VariableExpressionErrorException exception) + { + setOperationMessageForThrowable(request, buildMessageCode(VariableExpressionErrorException.class), + exception.getCause(), false, exception.getExpression().getContent()); return ERROR_PAGE_URL; } @@ -151,7 +168,8 @@ public class ControllerAdvice extends AbstractController public String handlePersistenceSqlExpressionErrorException(HttpServletRequest request, HttpServletResponse response, SqlExpressionErrorException exception) { - setOperationMessageForExceptionCause(request, exception); + setOperationMessageForThrowable(request, buildMessageCode(SqlExpressionErrorException.class), + exception.getCause(), true, exception.getExpression().getContent()); return ERROR_PAGE_URL; } @@ -161,7 +179,7 @@ public class ControllerAdvice extends AbstractController public String handlePersistenceUnsupportedDialectException(HttpServletRequest request, HttpServletResponse response, UnsupportedDialectException exception) { - setOperationMessageForException(request, exception, false); + setOperationMessageForThrowable(request, buildMessageCode(UnsupportedDialectException.class), exception, false); return ERROR_PAGE_URL; } @@ -172,9 +190,10 @@ public class ControllerAdvice extends AbstractController PersistenceException exception) { if (exception.getCause() instanceof SQLException) - setOperationMessageForExceptionCause(request, exception); + setOperationMessageForThrowable(request, buildMessageCode(PersistenceException.class), exception.getCause(), + true); else - setOperationMessageForException(request, exception, true); + setOperationMessageForThrowable(request, buildMessageCode(PersistenceException.class), exception, true); return ERROR_PAGE_URL; } @@ -184,7 +203,8 @@ public class ControllerAdvice extends AbstractController public String handleDbmodelDatabaseInfoResolverException(HttpServletRequest request, HttpServletResponse response, DatabaseInfoResolverException exception) { - setOperationMessageForException(request, exception, true); + setOperationMessageForThrowable(request, buildMessageCode(DatabaseInfoResolverException.class), exception, + true); return ERROR_PAGE_URL; } @@ -194,7 +214,8 @@ public class ControllerAdvice extends AbstractController public String handleDbmodelDatabaseModelResolverException(HttpServletRequest request, HttpServletResponse response, DatabaseModelResolverException exception) { - setOperationMessageForException(request, exception, true); + setOperationMessageForThrowable(request, buildMessageCode(DatabaseModelResolverException.class), exception, + true); return ERROR_PAGE_URL; } @@ -204,7 +225,8 @@ public class ControllerAdvice extends AbstractController public String handleDbmodelTableNotExistsException(HttpServletRequest request, HttpServletResponse response, TableNotExistsException exception) { - setOperationMessageForException(request, exception, false, exception.getTableName()); + setOperationMessageForThrowable(request, buildMessageCode(TableNotExistsException.class), exception, false, + exception.getTableName()); return ERROR_PAGE_URL; } @@ -214,7 +236,7 @@ public class ControllerAdvice extends AbstractController public String handleConnectionConnectionSourceException(HttpServletRequest request, HttpServletResponse response, ConnectionSourceException exception) { - setOperationMessageForException(request, exception, true); + setOperationMessageForThrowable(request, buildMessageCode(ConnectionSourceException.class), exception, true); return ERROR_PAGE_URL; } @@ -224,7 +246,7 @@ public class ControllerAdvice extends AbstractController public String handleConnectionDriverEntityManagerException(HttpServletRequest request, HttpServletResponse response, DriverEntityManagerException exception) { - setOperationMessageForException(request, exception, true); + setOperationMessageForThrowable(request, buildMessageCode(DriverEntityManagerException.class), exception, true); return ERROR_PAGE_URL; } @@ -234,7 +256,8 @@ public class ControllerAdvice extends AbstractController public String handleConnectionDriverNotFoundException(HttpServletRequest request, HttpServletResponse response, DriverNotFoundException exception) { - setOperationMessageForException(request, exception, false, exception.getDriverClassName()); + setOperationMessageForThrowable(request, buildMessageCode(DriverNotFoundException.class), exception, false, + exception.getDriverClassName()); return ERROR_PAGE_URL; } @@ -244,7 +267,8 @@ public class ControllerAdvice extends AbstractController public String handleConnectionDriverClassFormatErrorException(HttpServletRequest request, HttpServletResponse response, DriverClassFormatErrorException exception) { - setOperationMessageForException(request, exception, false); + setOperationMessageForThrowable(request, buildMessageCode(DriverClassFormatErrorException.class), exception, + false); return ERROR_PAGE_URL; } @@ -254,7 +278,7 @@ public class ControllerAdvice extends AbstractController public String handleConnectionURLNotAcceptedException(HttpServletRequest request, HttpServletResponse response, URLNotAcceptedException exception) { - setOperationMessageForException(request, exception, false); + setOperationMessageForThrowable(request, buildMessageCode(URLNotAcceptedException.class), exception, false); return ERROR_PAGE_URL; } @@ -264,7 +288,8 @@ public class ControllerAdvice extends AbstractController public String handleConnectionUnsupportedGetConnectionException(HttpServletRequest request, HttpServletResponse response, UnsupportedGetConnectionException exception) { - setOperationMessageForException(request, exception, false); + setOperationMessageForThrowable(request, buildMessageCode(UnsupportedGetConnectionException.class), exception, + false); return ERROR_PAGE_URL; } @@ -274,25 +299,15 @@ public class ControllerAdvice extends AbstractController public String handleConnectionEstablishConnectionException(HttpServletRequest request, HttpServletResponse response, EstablishConnectionException exception) { - setOperationMessageForExceptionCause(request, exception); + setOperationMessageForThrowable(request, buildMessageCode(EstablishConnectionException.class), + exception.getCause(), true); return ERROR_PAGE_URL; } - protected void setOperationMessageForException(HttpServletRequest request, Exception exception, - boolean traceException, Object... messageArgs) + protected String buildMessageCode(Class clazz) { - String code = buildMessageCode(exception.getClass().getSimpleName()); - - setOperationMessageForThrowable(request, code, exception, traceException, messageArgs); - } - - protected void setOperationMessageForExceptionCause(HttpServletRequest request, Exception exception, - Object... messageArgs) - { - String code = buildMessageCode(exception.getClass().getSimpleName()); - - setOperationMessageForThrowable(request, code, exception.getCause(), true, messageArgs); + return buildMessageCode(clazz.getSimpleName()); } @Override diff --git a/datagear-web/src/main/java/org/datagear/web/convert/AbstractDataConverter.java b/datagear-web/src/main/java/org/datagear/web/convert/AbstractDataConverter.java index 03a478cd..2f23cb23 100644 --- a/datagear-web/src/main/java/org/datagear/web/convert/AbstractDataConverter.java +++ b/datagear-web/src/main/java/org/datagear/web/convert/AbstractDataConverter.java @@ -33,6 +33,8 @@ import org.datagear.model.support.DefaultDynamicBean; import org.datagear.model.support.DynamicBean; import org.datagear.model.support.PropertyPath; import org.datagear.persistence.support.ExpressionResolver; +import org.datagear.persistence.support.SqlExpressionResolver; +import org.datagear.persistence.support.VariableExpressionResolver; import org.springframework.core.convert.ConversionService; /** @@ -64,7 +66,9 @@ public abstract class AbstractDataConverter private ConversionService conversionService; - private ExpressionResolver expressionResolver = new ExpressionResolver(); + private ExpressionResolver variableExpressionResolver = new VariableExpressionResolver(); + + private ExpressionResolver sqlExpressionResolver = new SqlExpressionResolver(); private Map, Class> instanceTypeMap = new HashMap, Class>(); @@ -91,14 +95,24 @@ public abstract class AbstractDataConverter this.conversionService = conversionService; } - public ExpressionResolver getExpressionResolver() + public ExpressionResolver getVariableExpressionResolver() { - return expressionResolver; + return variableExpressionResolver; } - public void setExpressionResolver(ExpressionResolver expressionResolver) + public void setVariableExpressionResolver(ExpressionResolver variableExpressionResolver) { - this.expressionResolver = expressionResolver; + this.variableExpressionResolver = variableExpressionResolver; + } + + public ExpressionResolver getSqlExpressionResolver() + { + return sqlExpressionResolver; + } + + public void setSqlExpressionResolver(ExpressionResolver sqlExpressionResolver) + { + this.sqlExpressionResolver = sqlExpressionResolver; } public Map, Class> getInstanceTypeMap() @@ -167,7 +181,7 @@ public abstract class AbstractDataConverter { String str = (String) obj; - if (this.expressionResolver.isExpression(str)) + if (isExpression(str)) return (T) str; if (str.isEmpty() && !String.class.equals(targetType)) @@ -385,6 +399,11 @@ public abstract class AbstractDataConverter return getInstanceType(instanceType); } + protected boolean isExpression(Object obj) + { + return this.variableExpressionResolver.isExpression(obj) || this.sqlExpressionResolver.isExpression(obj); + } + /** * 比较{@code "[xxx]"}格式字符串的大小。 * diff --git a/datagear-web/src/main/resources/datagear-applicationContext.xml b/datagear-web/src/main/resources/datagear-applicationContext.xml index ac82b139..979325a2 100644 --- a/datagear-web/src/main/resources/datagear-applicationContext.xml +++ b/datagear-web/src/main/resources/datagear-applicationContext.xml @@ -207,9 +207,15 @@ + + + + + + @@ -282,10 +288,14 @@ + + + + diff --git a/datagear-web/src/main/resources/locales/datagear.properties b/datagear-web/src/main/resources/locales/datagear.properties index 3d175d5e..23ebd04c 100644 --- a/datagear-web/src/main/resources/locales/datagear.properties +++ b/datagear-web/src/main/resources/locales/datagear.properties @@ -77,7 +77,8 @@ error.RecordNotFoundOrPermissionDeniedException=\u8BB0\u5F55\u6216\u8BB8\u5DF2\u error.SchemaNotFoundException=\u672A\u627E\u5230\u6B64\u6570\u636E\u5E93 error.IllegalSourceValueException=\u6570\u636E\u8F6C\u6362\u51FA\u9519\uFF1A\u65E0\u6CD5\u5C06[{0}]\u8F6C\u6362\u4E3A[{1}] error.FileNotFoundException=\u672A\u627E\u5230\u6587\u4EF6[{0}] -error.SqlExpressionErrorException=\u6267\u884CSQL\u8868\u8FBE\u5F0F\u51FA\u9519 +error.VariableExpressionErrorException=\u8868\u8FBE\u5F0F\u683C\u5F0F\u6709\u8BEF\uFF1A
{0}
+error.SqlExpressionErrorException=\u6267\u884CSQL\u8BED\u53E5\u51FA\u9519\uFF1A
{0}
error.UnsupportedDialectException=\u6570\u636E\u5E93\u8BBF\u95EE\u51FA\u9519\uFF0C\u7CFB\u7EDF\u4E0D\u652F\u6301\u8BBF\u95EE\u6B64\u6570\u636E\u5E93 error.PersistenceException=\u6570\u636E\u5E93\u8BBF\u95EE\u51FA\u9519 error.NotUniqueRecordException=\u65E0\u6CD5\u552F\u4E00\u786E\u5B9A\u8BB0\u5F55\uFF0C\u6570\u636E\u6709\u91CD\u590D diff --git a/datagear-web/src/main/webapp/static/script/datagear-util.js b/datagear-web/src/main/webapp/static/script/datagear-util.js index fcd037d4..ede7a697 100644 --- a/datagear-web/src/main/webapp/static/script/datagear-util.js +++ b/datagear-web/src/main/webapp/static/script/datagear-util.js @@ -366,7 +366,8 @@ */ tipSuccess : function(content, delayMs) { - content = "" + content; + content = "" + +"
" + content +"
"; return $._tip("ui-state-default", content, (delayMs || 2000)); }, @@ -375,8 +376,9 @@ */ tipError : function(content, delayMs) { - content = "" + content; - return $._tip("ui-state-error", content, (delayMs || 5000)); + content = "" + +"
" + content +"
"; + return $._tip("ui-state-error", content, (delayMs || 3500)); }, /** @@ -384,8 +386,9 @@ */ tipInfo : function(content, delayMs) { - content = "" + content; - return $._tip("ui-state-highlight", content, (delayMs || 5000)); + content = "" + +"
" + content +"
"; + return $._tip("ui-state-highlight", content, (delayMs || 3500)); }, /** @@ -412,7 +415,7 @@ */ _tip : function(tooltipClass, content, delayMs) { - content = "
" + content +"
"; + content = "
" + content +"
"; var tooltip = $(".global-tooltip", document.body); if(tooltip.length > 0) diff --git a/datagear-web/src/main/webapp/static/theme/common.css b/datagear-web/src/main/webapp/static/theme/common.css index 101471d1..0d76773e 100644 --- a/datagear-web/src/main/webapp/static/theme/common.css +++ b/datagear-web/src/main/webapp/static/theme/common.css @@ -336,9 +336,26 @@ table.dataTable tbody tr.selected .checkbox .ui-icon{ margin-left: 0.3em; margin-right: 0.3em; } +.ui-tooltip .ui-tooltip-content .tooltip-icon{ + margin-right: 0.41em; +} .ui-tooltip .ui-tooltip-content .error-detail-icon{ cursor: pointer; } +.ui-tooltip .ui-tooltip-content .content{ + padding-left:0.5em; + padding-right:0.5em; + white-space: nowrap; +} +.ui-tooltip .ui-tooltip-content .content .content-value{ + display: inline-block; +} +.ui-tooltip .ui-tooltip-content .content .content-value .content-value-sub{ + display: inline-block; + max-width: 41em; + vertical-align: bottom; + overflow: hidden; +} /*错误消息对话框*/ .error-dialog{