Skip to content

Commit 584990a

Browse files
committed
When processing DynamicSqlSource, evaluate param values in scripting phase
- Evaluated param values are stored in `ParameterMapping` and later used in DefaultParameterHandler - There is no change when processing RawSqlSource - Removed unused `injectionFilter` from TextSqlNode (gh-117) This should fix #2754 . This might also fix #206 and #575 , but with this patch, it still is not possible to invoke a method with parameter inside a parameter reference like `#{_parameter.mymethod(_parameter.value)}`. It might be possible to accept OGNL expression in a param reference (e.g. `#{${_parameter.mymethod(_parameter.value)}}`), but I'm not sure if that's a good idea.
1 parent 17a9930 commit 584990a

File tree

15 files changed

+383
-251
lines changed

15 files changed

+383
-251
lines changed
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/*
2+
* Copyright 2009-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.apache.ibatis.builder;
18+
19+
import java.util.List;
20+
import java.util.Map;
21+
22+
import org.apache.ibatis.mapping.ParameterMapping;
23+
import org.apache.ibatis.mapping.ParameterMode;
24+
import org.apache.ibatis.parsing.TokenHandler;
25+
import org.apache.ibatis.reflection.MetaClass;
26+
import org.apache.ibatis.reflection.MetaObject;
27+
import org.apache.ibatis.reflection.property.PropertyTokenizer;
28+
import org.apache.ibatis.session.Configuration;
29+
import org.apache.ibatis.type.JdbcType;
30+
31+
public class ParameterMappingTokenHandler extends BaseBuilder implements TokenHandler {
32+
33+
private static final String PARAMETER_PROPERTIES = "javaType,jdbcType,mode,numericScale,resultMap,typeHandler,jdbcTypeName";
34+
private final List<ParameterMapping> parameterMappings;
35+
private final Class<?> parameterType;
36+
private final MetaObject metaParameters;
37+
private final Object parameterObject;
38+
private final boolean paramExists;
39+
40+
public ParameterMappingTokenHandler(List<ParameterMapping> parameterMappings, Configuration configuration,
41+
Object parameterObject, Class<?> parameterType, Map<String, Object> additionalParameters, boolean paramExists) {
42+
super(configuration);
43+
this.parameterType = parameterObject == null
44+
? (parameterType == null ? Object.class : parameterType)
45+
: parameterObject.getClass();
46+
this.metaParameters = configuration.newMetaObject(additionalParameters);
47+
this.parameterObject = parameterObject;
48+
this.paramExists = paramExists;
49+
this.parameterMappings = parameterMappings;
50+
}
51+
52+
public ParameterMappingTokenHandler(List<ParameterMapping> parameterMappings, Configuration configuration,
53+
Class<?> parameterType,
54+
Map<String, Object> additionalParameters) {
55+
super(configuration);
56+
this.parameterType = parameterType;
57+
this.metaParameters = configuration.newMetaObject(additionalParameters);
58+
this.parameterObject = null;
59+
this.paramExists = false;
60+
this.parameterMappings = parameterMappings;
61+
}
62+
63+
public List<ParameterMapping> getParameterMappings() {
64+
return parameterMappings;
65+
}
66+
67+
@Override
68+
public String handleToken(String content) {
69+
parameterMappings.add(buildParameterMapping(content));
70+
return "?";
71+
}
72+
73+
private ParameterMapping buildParameterMapping(String content) {
74+
Map<String, String> propertiesMap = parseParameterMapping(content);
75+
String property = propertiesMap.get("property");
76+
PropertyTokenizer propertyTokenizer = new PropertyTokenizer(property);
77+
Class<?> propertyType;
78+
if (metaParameters.hasGetter(propertyTokenizer.getName())) { // issue #448 get type from additional params
79+
propertyType = metaParameters.getGetterType(property);
80+
} else if (typeHandlerRegistry.hasTypeHandler(parameterType)) {
81+
propertyType = parameterType;
82+
} else if (JdbcType.CURSOR.name().equals(propertiesMap.get("jdbcType"))) {
83+
propertyType = java.sql.ResultSet.class;
84+
} else if (property == null || Map.class.isAssignableFrom(parameterType)) {
85+
propertyType = Object.class;
86+
} else {
87+
MetaClass metaClass = MetaClass.forClass(parameterType, configuration.getReflectorFactory());
88+
if (metaClass.hasGetter(property)) {
89+
propertyType = metaClass.getGetterType(property);
90+
} else {
91+
propertyType = Object.class;
92+
}
93+
}
94+
ParameterMapping.Builder builder = new ParameterMapping.Builder(configuration, property, propertyType);
95+
Class<?> javaType = propertyType;
96+
String typeHandlerAlias = null;
97+
ParameterMode mode = null;
98+
for (Map.Entry<String, String> entry : propertiesMap.entrySet()) {
99+
String name = entry.getKey();
100+
String value = entry.getValue();
101+
if ("javaType".equals(name)) {
102+
javaType = resolveClass(value);
103+
builder.javaType(javaType);
104+
} else if ("jdbcType".equals(name)) {
105+
builder.jdbcType(resolveJdbcType(value));
106+
} else if ("mode".equals(name)) {
107+
mode = resolveParameterMode(value);
108+
builder.mode(mode);
109+
} else if ("numericScale".equals(name)) {
110+
builder.numericScale(Integer.valueOf(value));
111+
} else if ("resultMap".equals(name)) {
112+
builder.resultMapId(value);
113+
} else if ("typeHandler".equals(name)) {
114+
typeHandlerAlias = value;
115+
} else if ("jdbcTypeName".equals(name)) {
116+
builder.jdbcTypeName(value);
117+
} else if ("property".equals(name)) {
118+
// Do Nothing
119+
} else if ("expression".equals(name)) {
120+
throw new BuilderException("Expression based parameters are not supported yet");
121+
} else {
122+
throw new BuilderException("An invalid property '" + name + "' was found in mapping #{" + content
123+
+ "}. Valid properties are " + PARAMETER_PROPERTIES);
124+
}
125+
}
126+
if (typeHandlerAlias != null) {
127+
builder.typeHandler(resolveTypeHandler(javaType, typeHandlerAlias));
128+
}
129+
if (!ParameterMode.OUT.equals(mode) && paramExists) {
130+
if (metaParameters.hasGetter(propertyTokenizer.getName())) {
131+
builder.value(metaParameters.getValue(property));
132+
} else if (parameterObject == null) {
133+
builder.value(null);
134+
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
135+
builder.value(parameterObject);
136+
} else {
137+
MetaObject metaObject = configuration.newMetaObject(parameterObject);
138+
builder.value(metaObject.getValue(property));
139+
}
140+
}
141+
return builder.build();
142+
}
143+
144+
private Map<String, String> parseParameterMapping(String content) {
145+
try {
146+
return new ParameterExpression(content);
147+
} catch (BuilderException ex) {
148+
throw ex;
149+
} catch (Exception ex) {
150+
throw new BuilderException("Parsing error was found in mapping #{" + content
151+
+ "}. Check syntax #{property|(expression), var1=value1, var2=value2, ...} ", ex);
152+
}
153+
}
154+
}

src/main/java/org/apache/ibatis/builder/SqlSourceBuilder.java

Lines changed: 8 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -15,41 +15,27 @@
1515
*/
1616
package org.apache.ibatis.builder;
1717

18-
import java.util.ArrayList;
1918
import java.util.List;
20-
import java.util.Map;
2119
import java.util.StringTokenizer;
2220

2321
import org.apache.ibatis.mapping.ParameterMapping;
2422
import org.apache.ibatis.mapping.SqlSource;
25-
import org.apache.ibatis.parsing.GenericTokenParser;
26-
import org.apache.ibatis.parsing.TokenHandler;
27-
import org.apache.ibatis.reflection.MetaClass;
28-
import org.apache.ibatis.reflection.MetaObject;
2923
import org.apache.ibatis.session.Configuration;
30-
import org.apache.ibatis.type.JdbcType;
3124

3225
/**
3326
* @author Clinton Begin
3427
*/
35-
public class SqlSourceBuilder extends BaseBuilder {
28+
public class SqlSourceBuilder {
3629

37-
private static final String PARAMETER_PROPERTIES = "javaType,jdbcType,mode,numericScale,resultMap,typeHandler,jdbcTypeName";
38-
39-
public SqlSourceBuilder(Configuration configuration) {
40-
super(configuration);
30+
private SqlSourceBuilder() {
31+
super();
4132
}
4233

43-
public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
44-
ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
45-
GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
46-
String sql;
47-
if (configuration.isShrinkWhitespacesInSql()) {
48-
sql = parser.parse(removeExtraWhitespaces(originalSql));
49-
} else {
50-
sql = parser.parse(originalSql);
51-
}
52-
return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
34+
public static SqlSource buildSqlSource(Configuration configuration, String sql,
35+
List<ParameterMapping> parameterMappings) {
36+
return new StaticSqlSource(configuration,
37+
configuration.isShrinkWhitespacesInSql() ? SqlSourceBuilder.removeExtraWhitespaces(sql) : sql,
38+
parameterMappings);
5339
}
5440

5541
public static String removeExtraWhitespaces(String original) {
@@ -66,92 +52,4 @@ public static String removeExtraWhitespaces(String original) {
6652
return builder.toString();
6753
}
6854

69-
private static class ParameterMappingTokenHandler extends BaseBuilder implements TokenHandler {
70-
71-
private final List<ParameterMapping> parameterMappings = new ArrayList<>();
72-
private final Class<?> parameterType;
73-
private final MetaObject metaParameters;
74-
75-
public ParameterMappingTokenHandler(Configuration configuration, Class<?> parameterType, Map<String, Object> additionalParameters) {
76-
super(configuration);
77-
this.parameterType = parameterType;
78-
this.metaParameters = configuration.newMetaObject(additionalParameters);
79-
}
80-
81-
public List<ParameterMapping> getParameterMappings() {
82-
return parameterMappings;
83-
}
84-
85-
@Override
86-
public String handleToken(String content) {
87-
parameterMappings.add(buildParameterMapping(content));
88-
return "?";
89-
}
90-
91-
private ParameterMapping buildParameterMapping(String content) {
92-
Map<String, String> propertiesMap = parseParameterMapping(content);
93-
String property = propertiesMap.get("property");
94-
Class<?> propertyType;
95-
if (metaParameters.hasGetter(property)) { // issue #448 get type from additional params
96-
propertyType = metaParameters.getGetterType(property);
97-
} else if (typeHandlerRegistry.hasTypeHandler(parameterType)) {
98-
propertyType = parameterType;
99-
} else if (JdbcType.CURSOR.name().equals(propertiesMap.get("jdbcType"))) {
100-
propertyType = java.sql.ResultSet.class;
101-
} else if (property == null || Map.class.isAssignableFrom(parameterType)) {
102-
propertyType = Object.class;
103-
} else {
104-
MetaClass metaClass = MetaClass.forClass(parameterType, configuration.getReflectorFactory());
105-
if (metaClass.hasGetter(property)) {
106-
propertyType = metaClass.getGetterType(property);
107-
} else {
108-
propertyType = Object.class;
109-
}
110-
}
111-
ParameterMapping.Builder builder = new ParameterMapping.Builder(configuration, property, propertyType);
112-
Class<?> javaType = propertyType;
113-
String typeHandlerAlias = null;
114-
for (Map.Entry<String, String> entry : propertiesMap.entrySet()) {
115-
String name = entry.getKey();
116-
String value = entry.getValue();
117-
if ("javaType".equals(name)) {
118-
javaType = resolveClass(value);
119-
builder.javaType(javaType);
120-
} else if ("jdbcType".equals(name)) {
121-
builder.jdbcType(resolveJdbcType(value));
122-
} else if ("mode".equals(name)) {
123-
builder.mode(resolveParameterMode(value));
124-
} else if ("numericScale".equals(name)) {
125-
builder.numericScale(Integer.valueOf(value));
126-
} else if ("resultMap".equals(name)) {
127-
builder.resultMapId(value);
128-
} else if ("typeHandler".equals(name)) {
129-
typeHandlerAlias = value;
130-
} else if ("jdbcTypeName".equals(name)) {
131-
builder.jdbcTypeName(value);
132-
} else if ("property".equals(name)) {
133-
// Do Nothing
134-
} else if ("expression".equals(name)) {
135-
throw new BuilderException("Expression based parameters are not supported yet");
136-
} else {
137-
throw new BuilderException("An invalid property '" + name + "' was found in mapping #{" + content + "}. Valid properties are " + PARAMETER_PROPERTIES);
138-
}
139-
}
140-
if (typeHandlerAlias != null) {
141-
builder.typeHandler(resolveTypeHandler(javaType, typeHandlerAlias));
142-
}
143-
return builder.build();
144-
}
145-
146-
private Map<String, String> parseParameterMapping(String content) {
147-
try {
148-
return new ParameterExpression(content);
149-
} catch (BuilderException ex) {
150-
throw ex;
151-
} catch (Exception ex) {
152-
throw new BuilderException("Parsing error was found in mapping #{" + content + "}. Check syntax #{property|(expression), var1=value1, var2=value2, ...} ", ex);
153-
}
154-
}
155-
}
156-
15755
}

src/main/java/org/apache/ibatis/executor/BaseExecutor.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,9 @@ public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBo
208208
if (parameterMapping.getMode() != ParameterMode.OUT) {
209209
Object value;
210210
String propertyName = parameterMapping.getProperty();
211-
if (boundSql.hasAdditionalParameter(propertyName)) {
211+
if (parameterMapping.hasValue()) {
212+
value = parameterMapping.getValue();
213+
} else if (boundSql.hasAdditionalParameter(propertyName)) {
212214
value = boundSql.getAdditionalParameter(propertyName);
213215
} else if (parameterObject == null) {
214216
value = null;

src/main/java/org/apache/ibatis/mapping/ParameterMapping.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ public class ParameterMapping {
3838
private String resultMapId;
3939
private String jdbcTypeName;
4040
private String expression;
41+
private Object value;
42+
private boolean hasValue;
4143

4244
private ParameterMapping() {
4345
}
@@ -99,6 +101,12 @@ public Builder expression(String expression) {
99101
return this;
100102
}
101103

104+
public Builder value(Object value) {
105+
parameterMapping.value = value;
106+
parameterMapping.hasValue = true;
107+
return this;
108+
}
109+
102110
public ParameterMapping build() {
103111
resolveTypeHandler();
104112
validate();
@@ -207,6 +215,14 @@ public String getExpression() {
207215
return expression;
208216
}
209217

218+
public Object getValue() {
219+
return value;
220+
}
221+
222+
public boolean hasValue() {
223+
return hasValue;
224+
}
225+
210226
@Override
211227
public String toString() {
212228
final StringBuilder sb = new StringBuilder("ParameterMapping{");
@@ -220,6 +236,7 @@ public String toString() {
220236
sb.append(", resultMapId='").append(resultMapId).append('\'');
221237
sb.append(", jdbcTypeName='").append(jdbcTypeName).append('\'');
222238
sb.append(", expression='").append(expression).append('\'');
239+
sb.append(", value='").append(value).append('\'');
223240
sb.append('}');
224241
return sb.toString();
225242
}

src/main/java/org/apache/ibatis/scripting/defaults/DefaultParameterHandler.java

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -66,18 +66,7 @@ public void setParameters(PreparedStatement ps) {
6666
for (int i = 0; i < parameterMappings.size(); i++) {
6767
ParameterMapping parameterMapping = parameterMappings.get(i);
6868
if (parameterMapping.getMode() != ParameterMode.OUT) {
69-
Object value;
70-
String propertyName = parameterMapping.getProperty();
71-
if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
72-
value = boundSql.getAdditionalParameter(propertyName);
73-
} else if (parameterObject == null) {
74-
value = null;
75-
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
76-
value = parameterObject;
77-
} else {
78-
MetaObject metaObject = configuration.newMetaObject(parameterObject);
79-
value = metaObject.getValue(propertyName);
80-
}
69+
Object value = getValue(parameterMapping);
8170
TypeHandler typeHandler = parameterMapping.getTypeHandler();
8271
JdbcType jdbcType = parameterMapping.getJdbcType();
8372
if (value == null && jdbcType == null) {
@@ -93,4 +82,21 @@ public void setParameters(PreparedStatement ps) {
9382
}
9483
}
9584

85+
private Object getValue(ParameterMapping parameterMapping) {
86+
if (parameterMapping.hasValue()) {
87+
return parameterMapping.getValue();
88+
}
89+
String propertyName = parameterMapping.getProperty();
90+
if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
91+
return boundSql.getAdditionalParameter(propertyName);
92+
} else if (parameterObject == null) {
93+
return null;
94+
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
95+
return parameterObject;
96+
} else {
97+
MetaObject metaObject = configuration.newMetaObject(parameterObject);
98+
return metaObject.getValue(propertyName);
99+
}
100+
}
101+
96102
}

0 commit comments

Comments
 (0)