View Javadoc
1   /*
2    * Copyright 2004-2023 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 (int i = 0; i < additionalResultMapNames.length; i++) {
125           statement.addResultMap(client.getDelegate().getResultMap(additionalResultMapNames[i]));
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       if ("FORWARD_ONLY".equals(resultSetType)) {
137         statement.setResultSetType(Integer.valueOf(ResultSet.TYPE_FORWARD_ONLY));
138       } else if ("SCROLL_INSENSITIVE".equals(resultSetType)) {
139         statement.setResultSetType(Integer.valueOf(ResultSet.TYPE_SCROLL_INSENSITIVE));
140       } else if ("SCROLL_SENSITIVE".equals(resultSetType)) {
141         statement.setResultSetType(Integer.valueOf(ResultSet.TYPE_SCROLL_SENSITIVE));
142       }
143     }
144     if (fetchSize != null) {
145       statement.setFetchSize(fetchSize);
146     }
147 
148     // set parameter class either from attribute or from map (make sure to match)
149     ParameterMap parameterMap = statement.getParameterMap();
150     if (parameterMap == null) {
151       statement.setParameterClass(parameterClass);
152     } else {
153       statement.setParameterClass(parameterMap.getParameterClass());
154     }
155 
156     // process SQL statement, including inline parameter maps
157     errorContext.setMoreInfo("Check the SQL statement.");
158     Sql sql = processor.getSql();
159     setSqlForStatement(statement, sql);
160 
161     // set up either null result map or automatic result mapping
162     ResultMap resultMap = (ResultMap) statement.getResultMap();
163     if (resultMap == null && resultClass == null) {
164       statement.setResultMap(null);
165     } else if (resultMap == null) {
166       resultMap = buildAutoResultMap(allowRemapping, statement, resultClass, xmlResultName);
167       statement.setResultMap(resultMap);
168       if (additionalResultClasses != null) {
169         for (int i = 0; i < additionalResultClasses.length; i++) {
170           statement
171               .addResultMap(buildAutoResultMap(allowRemapping, statement, additionalResultClasses[i], xmlResultName));
172         }
173       }
174 
175     }
176     statement.setTimeout(defaultStatementTimeout);
177     if (timeout != null) {
178       try {
179         statement.setTimeout(timeout);
180       } catch (NumberFormatException e) {
181         throw new SqlMapException(
182             "Specified timeout value for statement " + statement.getId() + " is not a valid integer");
183       }
184     }
185     errorContext.setMoreInfo(null);
186     errorContext.setObjectId(null);
187     statement.setSqlMapClient(client);
188     if (cacheModelName != null && cacheModelName.length() > 0 && client.getDelegate().isCacheModelsEnabled()) {
189       CacheModel cacheModel = client.getDelegate().getCacheModel(cacheModelName);
190       mappedStatement = new CachingStatement(statement, cacheModel);
191     } else {
192       mappedStatement = statement;
193     }
194     rootStatement = statement;
195     delegate.addMappedStatement(mappedStatement);
196   }
197 
198   /**
199    * Sets the select key statement.
200    *
201    * @param processor
202    *          the processor
203    * @param resultClassName
204    *          the result class name
205    * @param keyPropName
206    *          the key prop name
207    * @param runAfterSQL
208    *          the run after SQL
209    * @param type
210    *          the type
211    */
212   public void setSelectKeyStatement(SqlSource processor, String resultClassName, String keyPropName,
213       boolean runAfterSQL, String type) {
214     if (rootStatement instanceof InsertStatement) {
215       InsertStatement insertStatement = ((InsertStatement) rootStatement);
216       Class parameterClass = insertStatement.getParameterClass();
217       errorContext.setActivity("parsing a select key");
218       SelectKeyStatement selectKeyStatement = new SelectKeyStatement();
219       resultClassName = typeHandlerFactory.resolveAlias(resultClassName);
220       Class resultClass = null;
221 
222       // get parameter and result maps
223       selectKeyStatement.setSqlMapClient(client);
224       selectKeyStatement.setId(insertStatement.getId() + "-SelectKey");
225       selectKeyStatement.setResource(errorContext.getResource());
226       selectKeyStatement.setKeyProperty(keyPropName);
227       selectKeyStatement.setRunAfterSQL(runAfterSQL);
228       // process the type (pre or post) attribute
229       if (type != null) {
230         selectKeyStatement.setRunAfterSQL("post".equals(type));
231       }
232       try {
233         if (resultClassName != null) {
234           errorContext.setMoreInfo("Check the select key result class.");
235           resultClass = Resources.classForName(resultClassName);
236         } else {
237           if (keyPropName != null && parameterClass != null) {
238             resultClass = PROBE.getPropertyTypeForSetter(parameterClass, selectKeyStatement.getKeyProperty());
239           }
240         }
241       } catch (ClassNotFoundException e) {
242         throw new SqlMapException("Error.  Could not set result class.  Cause: " + e, e);
243       }
244       if (resultClass == null) {
245         resultClass = Object.class;
246       }
247 
248       // process SQL statement, including inline parameter maps
249       errorContext.setMoreInfo("Check the select key SQL statement.");
250       Sql sql = processor.getSql();
251       setSqlForStatement(selectKeyStatement, sql);
252       ResultMap resultMap;
253       resultMap = new AutoResultMap(client.getDelegate(), false);
254       resultMap.setId(selectKeyStatement.getId() + "-AutoResultMap");
255       resultMap.setResultClass(resultClass);
256       resultMap.setResource(selectKeyStatement.getResource());
257       selectKeyStatement.setResultMap(resultMap);
258       errorContext.setMoreInfo(null);
259       insertStatement.setSelectKeyStatement(selectKeyStatement);
260     } else {
261       throw new SqlMapException("You cant set a select key statement on statement named " + rootStatement.getId()
262           + " because it is not an InsertStatement.");
263     }
264   }
265 
266   /**
267    * Sets the sql for statement.
268    *
269    * @param statement
270    *          the statement
271    * @param sql
272    *          the sql
273    */
274   private void setSqlForStatement(MappedStatement statement, Sql sql) {
275     if (sql instanceof DynamicSql) {
276       statement.setSql(sql);
277     } else {
278       applyInlineParameterMap(statement, sql.getSql(null, null));
279     }
280   }
281 
282   /**
283    * Apply inline parameter map.
284    *
285    * @param statement
286    *          the statement
287    * @param sqlStatement
288    *          the sql statement
289    */
290   private void applyInlineParameterMap(MappedStatement statement, String sqlStatement) {
291     String newSql = sqlStatement;
292     errorContext.setActivity("building an inline parameter map");
293     ParameterMap parameterMap = statement.getParameterMap();
294     errorContext.setMoreInfo("Check the inline parameters.");
295     if (parameterMap == null) {
296       ParameterMap map;
297       map = new ParameterMap(client.getDelegate());
298       map.setId(statement.getId() + "-InlineParameterMap");
299       map.setParameterClass(statement.getParameterClass());
300       map.setResource(statement.getResource());
301       statement.setParameterMap(map);
302       SqlText sqlText = PARAM_PARSER.parseInlineParameterMap(client.getDelegate().getTypeHandlerFactory(), newSql,
303           statement.getParameterClass());
304       newSql = sqlText.getText();
305       List mappingList = Arrays.asList(sqlText.getParameterMappings());
306       map.setParameterMappingList(mappingList);
307     }
308     Sql sql;
309     if (SimpleDynamicSql.isSimpleDynamicSql(newSql)) {
310       sql = new SimpleDynamicSql(client.getDelegate(), newSql);
311     } else {
312       sql = new StaticSql(newSql);
313     }
314     statement.setSql(sql);
315 
316   }
317 
318   /**
319    * Builds the auto result map.
320    *
321    * @param allowRemapping
322    *          the allow remapping
323    * @param statement
324    *          the statement
325    * @param firstResultClass
326    *          the first result class
327    * @param xmlResultName
328    *          the xml result name
329    *
330    * @return the result map
331    */
332   private ResultMap buildAutoResultMap(boolean allowRemapping, MappedStatement statement, Class firstResultClass,
333       String xmlResultName) {
334     ResultMap resultMap;
335     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 }