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.mapping.parameter;
17  
18  import com.ibatis.common.logging.Log;
19  import com.ibatis.common.logging.LogFactory;
20  import com.ibatis.sqlmap.engine.cache.CacheKey;
21  import com.ibatis.sqlmap.engine.exchange.DataExchange;
22  import com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate;
23  import com.ibatis.sqlmap.engine.scope.ErrorContext;
24  import com.ibatis.sqlmap.engine.scope.StatementScope;
25  import com.ibatis.sqlmap.engine.type.CustomTypeHandler;
26  import com.ibatis.sqlmap.engine.type.JdbcTypeRegistry;
27  import com.ibatis.sqlmap.engine.type.TypeHandler;
28  
29  import java.sql.DatabaseMetaData;
30  import java.sql.PreparedStatement;
31  import java.sql.SQLException;
32  import java.sql.Types;
33  import java.util.HashMap;
34  import java.util.List;
35  import java.util.Map;
36  
37  /**
38   * The Class ParameterMap.
39   */
40  public class ParameterMap {
41  
42    /** The Constant log. */
43    private static final Log log = LogFactory.getLog(ParameterMap.class);
44  
45    /** The id. */
46    private String id;
47  
48    /** The parameter class. */
49    private Class parameterClass;
50  
51    /** The parameter mappings. */
52    private ParameterMapping[] parameterMappings;
53  
54    /** The use set object for null value. */
55    private Boolean useSetObjectForNullValue;
56  
57    /** The sql type to use for null value. */
58    private int sqlTypeToUseForNullValue;
59  
60    /** The data exchange. */
61    private DataExchange dataExchange;
62  
63    /** The resource. */
64    private String resource;
65  
66    /** The parameter mapping index. */
67    private Map parameterMappingIndex = new HashMap();
68  
69    /** The delegate. */
70    private SqlMapExecutorDelegate delegate;
71  
72    /**
73     * Instantiates a new parameter map.
74     *
75     * @param delegate
76     *          the delegate
77     */
78    public ParameterMap(SqlMapExecutorDelegate delegate) {
79      this.delegate = delegate;
80    }
81  
82    /**
83     * Gets the delegate.
84     *
85     * @return the delegate
86     */
87    public SqlMapExecutorDelegate getDelegate() {
88      return delegate;
89    }
90  
91    /**
92     * Gets the id.
93     *
94     * @return the id
95     */
96    public String getId() {
97      return id;
98    }
99  
100   /**
101    * Sets the id.
102    *
103    * @param id
104    *          the new id
105    */
106   public void setId(String id) {
107     this.id = id;
108   }
109 
110   /**
111    * Gets the parameter class.
112    *
113    * @return the parameter class
114    */
115   public Class getParameterClass() {
116     return parameterClass;
117   }
118 
119   /**
120    * Sets the parameter class.
121    *
122    * @param parameterClass
123    *          the new parameter class
124    */
125   public void setParameterClass(Class parameterClass) {
126     this.parameterClass = parameterClass;
127   }
128 
129   /**
130    * Gets the data exchange.
131    *
132    * @return the data exchange
133    */
134   public DataExchange getDataExchange() {
135     return dataExchange;
136   }
137 
138   /**
139    * Sets the data exchange.
140    *
141    * @param dataExchange
142    *          the new data exchange
143    */
144   public void setDataExchange(DataExchange dataExchange) {
145     this.dataExchange = dataExchange;
146   }
147 
148   /**
149    * Gets the parameter mappings.
150    *
151    * @return the parameter mappings
152    */
153   public ParameterMapping[] getParameterMappings() {
154     return parameterMappings;
155   }
156 
157   /**
158    * Sets the parameter mapping list.
159    *
160    * @param parameterMappingList
161    *          the new parameter mapping list
162    */
163   public void setParameterMappingList(List parameterMappingList) {
164     this.parameterMappings = (ParameterMapping[]) parameterMappingList
165         .toArray(new ParameterMapping[parameterMappingList.size()]);
166     parameterMappingIndex.clear();
167     for (int i = 0; i < parameterMappings.length; i++) {
168       parameterMappingIndex.put(parameterMappings[i].getPropertyName(), Integer.valueOf(i));
169     }
170     Map props = new HashMap();
171     props.put("map", this);
172 
173     dataExchange = delegate.getDataExchangeFactory().getDataExchangeForClass(parameterClass);
174     dataExchange.initialize(props);
175   }
176 
177   /**
178    * Gets the parameter index.
179    *
180    * @param propertyName
181    *          the property name
182    *
183    * @return the parameter index
184    */
185   public int getParameterIndex(String propertyName) {
186     Integer idx = null;
187     idx = (Integer) parameterMappingIndex.get(propertyName);
188     return idx == null ? -1 : idx.intValue();
189   }
190 
191   /**
192    * Gets the parameter count.
193    *
194    * @return the parameter count
195    */
196   public int getParameterCount() {
197     return this.parameterMappings.length;
198   }
199 
200   /**
201    * Sets the parameters.
202    *
203    * @param statementScope
204    *          the statement scope
205    * @param ps
206    *          the ps
207    * @param parameters
208    *          the parameters
209    *
210    * @throws SQLException
211    *           the SQL exception
212    */
213   public void setParameters(StatementScope statementScope, PreparedStatement ps, Object[] parameters)
214       throws SQLException {
215 
216     ErrorContext errorContext = statementScope.getErrorContext();
217     errorContext.setActivity("applying a parameter map");
218     errorContext.setObjectId(this.getId());
219     errorContext.setResource(this.getResource());
220     errorContext.setMoreInfo("Check the parameter map.");
221 
222     if (parameterMappings != null) {
223       for (int i = 0; i < parameterMappings.length; i++) {
224         ParameterMapping mapping = parameterMappings[i];
225         errorContext.setMoreInfo(mapping.getErrorString());
226         if (mapping.isInputAllowed()) {
227           setParameter(ps, mapping, parameters, i);
228         }
229       }
230     }
231   }
232 
233   /**
234    * Gets the parameter object values.
235    *
236    * @param statementScope
237    *          the statement scope
238    * @param parameterObject
239    *          the parameter object
240    *
241    * @return the parameter object values
242    */
243   public Object[] getParameterObjectValues(StatementScope statementScope, Object parameterObject) {
244     return dataExchange.getData(statementScope, this, parameterObject);
245   }
246 
247   /**
248    * Gets the cache key.
249    *
250    * @param statementScope
251    *          the statement scope
252    * @param parameterObject
253    *          the parameter object
254    *
255    * @return the cache key
256    */
257   public CacheKey getCacheKey(StatementScope statementScope, Object parameterObject) {
258     return dataExchange.getCacheKey(statementScope, this, parameterObject);
259   }
260 
261   /**
262    * Refresh parameter object values.
263    *
264    * @param statementScope
265    *          the statement scope
266    * @param parameterObject
267    *          the parameter object
268    * @param values
269    *          the values
270    */
271   public void refreshParameterObjectValues(StatementScope statementScope, Object parameterObject, Object[] values) {
272     dataExchange.setData(statementScope, this, parameterObject, values);
273   }
274 
275   /**
276    * Gets the resource.
277    *
278    * @return the resource
279    */
280   public String getResource() {
281     return resource;
282   }
283 
284   /**
285    * Sets the resource.
286    *
287    * @param resource
288    *          the new resource
289    */
290   public void setResource(String resource) {
291     this.resource = resource;
292   }
293 
294   /**
295    * Sets the parameter.
296    *
297    * @param ps
298    *          the ps
299    * @param mapping
300    *          the mapping
301    * @param parameters
302    *          the parameters
303    * @param i
304    *          the i
305    *
306    * @throws SQLException
307    *           the SQL exception
308    */
309   protected void setParameter(PreparedStatement ps, ParameterMapping mapping, Object[] parameters, int i)
310       throws SQLException {
311     Object value = parameters[i];
312     // Apply Null Value
313     String nullValueString = mapping.getNullValue();
314     if (nullValueString != null) {
315       TypeHandler handler = mapping.getTypeHandler();
316       if (handler.equals(value, nullValueString)) {
317         value = null;
318       }
319     }
320 
321     // Set Parameter
322     TypeHandler typeHandler = mapping.getTypeHandler();
323     if (value != null) {
324       typeHandler.setParameter(ps, i + 1, value, mapping.getJdbcTypeName());
325     } else if (typeHandler instanceof CustomTypeHandler) {
326       typeHandler.setParameter(ps, i + 1, value, mapping.getJdbcTypeName());
327     } else {
328       int jdbcType = mapping.getJdbcType();
329       if (jdbcType != JdbcTypeRegistry.UNKNOWN_TYPE) {
330         ps.setNull(i + 1, jdbcType);
331       } else {
332         // Cloned from Spring StatementCreatorUtils.java (IBATIS-536)
333         if (useSetObjectForNullValue == null) {
334           // Keep current JDBC connection preferences for limiting introspections
335           useSetObjectForNullValue = Boolean.FALSE;
336           sqlTypeToUseForNullValue = Types.NULL;
337           try {
338             DatabaseMetaData dbmd = ps.getConnection().getMetaData();
339             String databaseProductName = dbmd.getDatabaseProductName();
340             String jdbcDriverName = dbmd.getDriverName();
341             if (databaseProductName.startsWith("Informix") || databaseProductName.startsWith("Microsoft SQL Server")) {
342               useSetObjectForNullValue = Boolean.TRUE;
343             } else if (databaseProductName.startsWith("DB2") || jdbcDriverName.startsWith("jConnect")
344                 || jdbcDriverName.startsWith("SQLServer") || jdbcDriverName.startsWith("Apache Derby Embedded")) {
345               sqlTypeToUseForNullValue = Types.VARCHAR;
346             }
347           } catch (Throwable ex) {
348             log.debug("Could not check database or driver name: " + ex.getMessage());
349           }
350         }
351         if (useSetObjectForNullValue.booleanValue()) {
352           ps.setObject(i + 1, null);
353         } else {
354           ps.setNull(i + 1, sqlTypeToUseForNullValue);
355         }
356       }
357     }
358   }
359 
360 }