View Javadoc
1   /*
2    * Copyright 2004-2026 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  package com.ibatis.sqlmap.engine.config;
17  
18  import com.ibatis.common.beans.Probe;
19  import com.ibatis.common.beans.ProbeFactory;
20  import com.ibatis.common.resources.Resources;
21  import com.ibatis.sqlmap.client.SqlMapException;
22  import com.ibatis.sqlmap.engine.cache.CacheModel;
23  import com.ibatis.sqlmap.engine.impl.SqlMapClientImpl;
24  import com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate;
25  import com.ibatis.sqlmap.engine.mapping.parameter.InlineParameterMapParser;
26  import com.ibatis.sqlmap.engine.mapping.parameter.ParameterMap;
27  import com.ibatis.sqlmap.engine.mapping.result.AutoResultMap;
28  import com.ibatis.sqlmap.engine.mapping.result.ResultMap;
29  import com.ibatis.sqlmap.engine.mapping.sql.Sql;
30  import com.ibatis.sqlmap.engine.mapping.sql.SqlText;
31  import com.ibatis.sqlmap.engine.mapping.sql.dynamic.DynamicSql;
32  import com.ibatis.sqlmap.engine.mapping.sql.simple.SimpleDynamicSql;
33  import com.ibatis.sqlmap.engine.mapping.sql.stat.StaticSql;
34  import com.ibatis.sqlmap.engine.mapping.statement.CachingStatement;
35  import com.ibatis.sqlmap.engine.mapping.statement.InsertStatement;
36  import com.ibatis.sqlmap.engine.mapping.statement.MappedStatement;
37  import com.ibatis.sqlmap.engine.mapping.statement.SelectKeyStatement;
38  import com.ibatis.sqlmap.engine.scope.ErrorContext;
39  import com.ibatis.sqlmap.engine.type.TypeHandlerFactory;
40  
41  import java.sql.ResultSet;
42  import java.util.Arrays;
43  import java.util.List;
44  
45  /**
46   * The Class MappedStatementConfig.
47   */
48  public class MappedStatementConfig {
49  
50    /** The Constant PROBE. */
51    private static final Probe PROBE = ProbeFactory.getProbe();
52  
53    /** The Constant PARAM_PARSER. */
54    private static final InlineParameterMapParser PARAM_PARSER = new InlineParameterMapParser();
55  
56    /** The error context. */
57    private ErrorContext errorContext;
58  
59    /** The client. */
60    private SqlMapClientImpl client;
61  
62    /** The type handler factory. */
63    private TypeHandlerFactory typeHandlerFactory;
64  
65    /** The mapped statement. */
66    private MappedStatement mappedStatement;
67  
68    /** The root statement. */
69    private MappedStatement rootStatement;
70  
71    /**
72     * Instantiates a new mapped statement config.
73     *
74     * @param config
75     *          the config
76     * @param id
77     *          the id
78     * @param statement
79     *          the statement
80     * @param processor
81     *          the processor
82     * @param parameterMapName
83     *          the parameter map name
84     * @param parameterClass
85     *          the parameter class
86     * @param resultMapName
87     *          the result map name
88     * @param additionalResultMapNames
89     *          the additional result map names
90     * @param resultClass
91     *          the result class
92     * @param additionalResultClasses
93     *          the additional result classes
94     * @param cacheModelName
95     *          the cache model name
96     * @param resultSetType
97     *          the result set type
98     * @param fetchSize
99     *          the fetch size
100    * @param allowRemapping
101    *          the allow remapping
102    * @param timeout
103    *          the timeout
104    * @param defaultStatementTimeout
105    *          the default statement timeout
106    * @param xmlResultName
107    *          the xml result name
108    */
109   MappedStatementConfig(SqlMapConfiguration config, String id, MappedStatement statement, SqlSource processor,
110       String parameterMapName, Class parameterClass, String resultMapName, String[] additionalResultMapNames,
111       Class resultClass, Class[] additionalResultClasses, String cacheModelName, String resultSetType,
112       Integer fetchSize, boolean allowRemapping, Integer timeout, Integer defaultStatementTimeout,
113       String xmlResultName) {
114     this.errorContext = config.getErrorContext();
115     this.client = config.getClient();
116     SqlMapExecutorDelegate delegate = client.getDelegate();
117     this.typeHandlerFactory = config.getTypeHandlerFactory();
118     errorContext.setActivity("parsing a mapped statement");
119     errorContext.setObjectId(id + " statement");
120     errorContext.setMoreInfo("Check the result map name.");
121     if (resultMapName != null) {
122       statement.setResultMap(client.getDelegate().getResultMap(resultMapName));
123       if (additionalResultMapNames != null) {
124         for (String additionalResultMapName : additionalResultMapNames) {
125           statement.addResultMap(client.getDelegate().getResultMap(additionalResultMapName));
126         }
127       }
128     }
129     errorContext.setMoreInfo("Check the parameter map name.");
130     if (parameterMapName != null) {
131       statement.setParameterMap(client.getDelegate().getParameterMap(parameterMapName));
132     }
133     statement.setId(id);
134     statement.setResource(errorContext.getResource());
135     if (resultSetType != null) {
136       switch (resultSetType) {
137         case "FORWARD_ONLY":
138           statement.setResultSetType(Integer.valueOf(ResultSet.TYPE_FORWARD_ONLY));
139           break;
140         case "SCROLL_INSENSITIVE":
141           statement.setResultSetType(Integer.valueOf(ResultSet.TYPE_SCROLL_INSENSITIVE));
142           break;
143         case "SCROLL_SENSITIVE":
144           statement.setResultSetType(Integer.valueOf(ResultSet.TYPE_SCROLL_SENSITIVE));
145           break;
146         default:
147           break;
148       }
149     }
150     if (fetchSize != null) {
151       statement.setFetchSize(fetchSize);
152     }
153 
154     // set parameter class either from attribute or from map (make sure to match)
155     ParameterMap parameterMap = statement.getParameterMap();
156     if (parameterMap == null) {
157       statement.setParameterClass(parameterClass);
158     } else {
159       statement.setParameterClass(parameterMap.getParameterClass());
160     }
161 
162     // process SQL statement, including inline parameter maps
163     errorContext.setMoreInfo("Check the SQL statement.");
164     Sql sql = processor.getSql();
165     setSqlForStatement(statement, sql);
166 
167     // set up either null result map or automatic result mapping
168     ResultMap resultMap = statement.getResultMap();
169     if (resultMap == null && resultClass == null) {
170       statement.setResultMap(null);
171     } else if (resultMap == null) {
172       resultMap = buildAutoResultMap(allowRemapping, statement, resultClass, xmlResultName);
173       statement.setResultMap(resultMap);
174       if (additionalResultClasses != null) {
175         for (Class additionalResultClass : additionalResultClasses) {
176           statement.addResultMap(buildAutoResultMap(allowRemapping, statement, additionalResultClass, xmlResultName));
177         }
178       }
179 
180     }
181     statement.setTimeout(defaultStatementTimeout);
182     if (timeout != null) {
183       try {
184         statement.setTimeout(timeout);
185       } catch (NumberFormatException e) {
186         throw new SqlMapException(
187             "Specified timeout value for statement " + statement.getId() + " is not a valid integer");
188       }
189     }
190     errorContext.setMoreInfo(null);
191     errorContext.setObjectId(null);
192     statement.setSqlMapClient(client);
193     if (cacheModelName != null && !cacheModelName.isEmpty() && client.getDelegate().isCacheModelsEnabled()) {
194       CacheModel cacheModel = client.getDelegate().getCacheModel(cacheModelName);
195       mappedStatement = new CachingStatement(statement, cacheModel);
196     } else {
197       mappedStatement = statement;
198     }
199     rootStatement = statement;
200     delegate.addMappedStatement(mappedStatement);
201   }
202 
203   /**
204    * Sets the select key statement.
205    *
206    * @param processor
207    *          the processor
208    * @param resultClassName
209    *          the result class name
210    * @param keyPropName
211    *          the key prop name
212    * @param runAfterSQL
213    *          the run after SQL
214    * @param type
215    *          the type
216    */
217   public void setSelectKeyStatement(SqlSource processor, String resultClassName, String keyPropName,
218       boolean runAfterSQL, String type) {
219     if (!(rootStatement instanceof InsertStatement)) {
220       throw new SqlMapException("You cant set a select key statement on statement named " + rootStatement.getId()
221           + " because it is not an InsertStatement.");
222     }
223     InsertStatement insertStatement = (InsertStatement) rootStatement;
224     Class parameterClass = insertStatement.getParameterClass();
225     errorContext.setActivity("parsing a select key");
226     SelectKeyStatement selectKeyStatement = new SelectKeyStatement();
227     resultClassName = typeHandlerFactory.resolveAlias(resultClassName);
228     Class resultClass = null;
229 
230     // get parameter and result maps
231     selectKeyStatement.setSqlMapClient(client);
232     selectKeyStatement.setId(insertStatement.getId() + "-SelectKey");
233     selectKeyStatement.setResource(errorContext.getResource());
234     selectKeyStatement.setKeyProperty(keyPropName);
235     selectKeyStatement.setRunAfterSQL(runAfterSQL);
236     // process the type (pre or post) attribute
237     if (type != null) {
238       selectKeyStatement.setRunAfterSQL("post".equals(type));
239     }
240     try {
241       if (resultClassName != null) {
242         errorContext.setMoreInfo("Check the select key result class.");
243         resultClass = Resources.classForName(resultClassName);
244       } else if (keyPropName != null && parameterClass != null) {
245         resultClass = PROBE.getPropertyTypeForSetter(parameterClass, selectKeyStatement.getKeyProperty());
246       }
247     } catch (ClassNotFoundException e) {
248       throw new SqlMapException("Error.  Could not set result class.  Cause: " + e, e);
249     }
250     if (resultClass == null) {
251       resultClass = Object.class;
252     }
253 
254     // process SQL statement, including inline parameter maps
255     errorContext.setMoreInfo("Check the select key SQL statement.");
256     Sql sql = processor.getSql();
257     setSqlForStatement(selectKeyStatement, sql);
258     ResultMap resultMap = new AutoResultMap(client.getDelegate(), false);
259     resultMap.setId(selectKeyStatement.getId() + "-AutoResultMap");
260     resultMap.setResultClass(resultClass);
261     resultMap.setResource(selectKeyStatement.getResource());
262     selectKeyStatement.setResultMap(resultMap);
263     errorContext.setMoreInfo(null);
264     insertStatement.setSelectKeyStatement(selectKeyStatement);
265   }
266 
267   /**
268    * Sets the sql for statement.
269    *
270    * @param statement
271    *          the statement
272    * @param sql
273    *          the sql
274    */
275   private void setSqlForStatement(MappedStatement statement, Sql sql) {
276     if (sql instanceof DynamicSql) {
277       statement.setSql(sql);
278     } else {
279       applyInlineParameterMap(statement, sql.getSql(null, null));
280     }
281   }
282 
283   /**
284    * Apply inline parameter map.
285    *
286    * @param statement
287    *          the statement
288    * @param sqlStatement
289    *          the sql statement
290    */
291   private void applyInlineParameterMap(MappedStatement statement, String sqlStatement) {
292     String newSql = sqlStatement;
293     errorContext.setActivity("building an inline parameter map");
294     ParameterMap parameterMap = statement.getParameterMap();
295     errorContext.setMoreInfo("Check the inline parameters.");
296     if (parameterMap == null) {
297       ParameterMap map;
298       map = new ParameterMap(client.getDelegate());
299       map.setId(statement.getId() + "-InlineParameterMap");
300       map.setParameterClass(statement.getParameterClass());
301       map.setResource(statement.getResource());
302       statement.setParameterMap(map);
303       SqlText sqlText = PARAM_PARSER.parseInlineParameterMap(client.getDelegate().getTypeHandlerFactory(), newSql,
304           statement.getParameterClass());
305       newSql = sqlText.getText();
306       List mappingList = Arrays.asList(sqlText.getParameterMappings());
307       map.setParameterMappingList(mappingList);
308     }
309     Sql sql;
310     if (SimpleDynamicSql.isSimpleDynamicSql(newSql)) {
311       sql = new SimpleDynamicSql(client.getDelegate(), newSql);
312     } else {
313       sql = new StaticSql(newSql);
314     }
315     statement.setSql(sql);
316 
317   }
318 
319   /**
320    * Builds the auto result map.
321    *
322    * @param allowRemapping
323    *          the allow remapping
324    * @param statement
325    *          the statement
326    * @param firstResultClass
327    *          the first result class
328    * @param xmlResultName
329    *          the xml result name
330    *
331    * @return the result map
332    */
333   private ResultMap buildAutoResultMap(boolean allowRemapping, MappedStatement statement, Class firstResultClass,
334       String xmlResultName) {
335     ResultMap resultMap = new AutoResultMap(client.getDelegate(), allowRemapping);
336     resultMap.setId(statement.getId() + "-AutoResultMap");
337     resultMap.setResultClass(firstResultClass);
338     resultMap.setXmlName(xmlResultName);
339     resultMap.setResource(statement.getResource());
340     return resultMap;
341   }
342 
343   /**
344    * Gets the mapped statement.
345    *
346    * @return the mapped statement
347    */
348   public MappedStatement getMappedStatement() {
349     return mappedStatement;
350   }
351 }