View Javadoc
1   /**
2    *    Copyright 2009-2019 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    *       http://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  package org.apache.ibatis.builder;
17  
18  import java.util.ArrayList;
19  import java.util.List;
20  import java.util.Map;
21  
22  import org.apache.ibatis.mapping.ParameterMapping;
23  import org.apache.ibatis.mapping.SqlSource;
24  import org.apache.ibatis.parsing.GenericTokenParser;
25  import org.apache.ibatis.parsing.TokenHandler;
26  import org.apache.ibatis.reflection.MetaClass;
27  import org.apache.ibatis.reflection.MetaObject;
28  import org.apache.ibatis.session.Configuration;
29  import org.apache.ibatis.type.JdbcType;
30  
31  /**
32   * @author Clinton Begin
33   */
34  public class SqlSourceBuilder extends BaseBuilder {
35  
36    private static final String PARAMETER_PROPERTIES = "javaType,jdbcType,mode,numericScale,resultMap,typeHandler,jdbcTypeName";
37  
38    public SqlSourceBuilder(Configuration configuration) {
39      super(configuration);
40    }
41  
42    public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
43      ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
44      GenericTokenParserrser.html#GenericTokenParser">GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
45      String sql = parser.parse(originalSql);
46      return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
47    }
48  
49    private static class ParameterMappingTokenHandler extends BaseBuilder implements TokenHandler {
50  
51      private List<ParameterMapping> parameterMappings = new ArrayList<>();
52      private Class<?> parameterType;
53      private MetaObject metaParameters;
54  
55      public ParameterMappingTokenHandler(Configuration configuration, Class<?> parameterType, Map<String, Object> additionalParameters) {
56        super(configuration);
57        this.parameterType = parameterType;
58        this.metaParameters = configuration.newMetaObject(additionalParameters);
59      }
60  
61      public List<ParameterMapping> getParameterMappings() {
62        return parameterMappings;
63      }
64  
65      @Override
66      public String handleToken(String content) {
67        parameterMappings.add(buildParameterMapping(content));
68        return "?";
69      }
70  
71      private ParameterMapping buildParameterMapping(String content) {
72        Map<String, String> propertiesMap = parseParameterMapping(content);
73        String property = propertiesMap.get("property");
74        Class<?> propertyType;
75        if (metaParameters.hasGetter(property)) { // issue #448 get type from additional params
76          propertyType = metaParameters.getGetterType(property);
77        } else if (typeHandlerRegistry.hasTypeHandler(parameterType)) {
78          propertyType = parameterType;
79        } else if (JdbcType.CURSOR.name().equals(propertiesMap.get("jdbcType"))) {
80          propertyType = java.sql.ResultSet.class;
81        } else if (property == null || Map.class.isAssignableFrom(parameterType)) {
82          propertyType = Object.class;
83        } else {
84          MetaClass metaClass = MetaClass.forClass(parameterType, configuration.getReflectorFactory());
85          if (metaClass.hasGetter(property)) {
86            propertyType = metaClass.getGetterType(property);
87          } else {
88            propertyType = Object.class;
89          }
90        }
91        ParameterMapping.Builder builder = new ParameterMapping.Builder(configuration, property, propertyType);
92        Class<?> javaType = propertyType;
93        String typeHandlerAlias = null;
94        for (Map.Entry<String, String> entry : propertiesMap.entrySet()) {
95          String name = entry.getKey();
96          String value = entry.getValue();
97          if ("javaType".equals(name)) {
98            javaType = resolveClass(value);
99            builder.javaType(javaType);
100         } else if ("jdbcType".equals(name)) {
101           builder.jdbcType(resolveJdbcType(value));
102         } else if ("mode".equals(name)) {
103           builder.mode(resolveParameterMode(value));
104         } else if ("numericScale".equals(name)) {
105           builder.numericScale(Integer.valueOf(value));
106         } else if ("resultMap".equals(name)) {
107           builder.resultMapId(value);
108         } else if ("typeHandler".equals(name)) {
109           typeHandlerAlias = value;
110         } else if ("jdbcTypeName".equals(name)) {
111           builder.jdbcTypeName(value);
112         } else if ("property".equals(name)) {
113           // Do Nothing
114         } else if ("expression".equals(name)) {
115           throw new BuilderException("Expression based parameters are not supported yet");
116         } else {
117           throw new BuilderException("An invalid property '" + name + "' was found in mapping #{" + content + "}.  Valid properties are " + PARAMETER_PROPERTIES);
118         }
119       }
120       if (typeHandlerAlias != null) {
121         builder.typeHandler(resolveTypeHandler(javaType, typeHandlerAlias));
122       }
123       return builder.build();
124     }
125 
126     private Map<String, String> parseParameterMapping(String content) {
127       try {
128         return new ParameterExpression(content);
129       } catch (BuilderException ex) {
130         throw ex;
131       } catch (Exception ex) {
132         throw new BuilderException("Parsing error was found in mapping #{" + content + "}.  Check syntax #{property|(expression), var1=value1, var2=value2, ...} ", ex);
133       }
134     }
135   }
136 
137 }