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.ClassInfo;
19  import com.ibatis.common.beans.Probe;
20  import com.ibatis.common.beans.ProbeFactory;
21  import com.ibatis.common.resources.Resources;
22  import com.ibatis.sqlmap.client.SqlMapException;
23  import com.ibatis.sqlmap.client.extensions.TypeHandlerCallback;
24  import com.ibatis.sqlmap.engine.accessplan.AccessPlanFactory;
25  import com.ibatis.sqlmap.engine.cache.CacheController;
26  import com.ibatis.sqlmap.engine.cache.CacheModel;
27  import com.ibatis.sqlmap.engine.cache.fifo.FifoCacheController;
28  import com.ibatis.sqlmap.engine.cache.lru.LruCacheController;
29  import com.ibatis.sqlmap.engine.cache.memory.MemoryCacheController;
30  import com.ibatis.sqlmap.engine.datasource.DbcpDataSourceFactory;
31  import com.ibatis.sqlmap.engine.datasource.JndiDataSourceFactory;
32  import com.ibatis.sqlmap.engine.datasource.SimpleDataSourceFactory;
33  import com.ibatis.sqlmap.engine.impl.SqlMapClientImpl;
34  import com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate;
35  import com.ibatis.sqlmap.engine.mapping.result.Discriminator;
36  import com.ibatis.sqlmap.engine.mapping.result.ResultMap;
37  import com.ibatis.sqlmap.engine.mapping.result.ResultObjectFactory;
38  import com.ibatis.sqlmap.engine.mapping.statement.MappedStatement;
39  import com.ibatis.sqlmap.engine.scope.ErrorContext;
40  import com.ibatis.sqlmap.engine.transaction.TransactionManager;
41  import com.ibatis.sqlmap.engine.transaction.external.ExternalTransactionConfig;
42  import com.ibatis.sqlmap.engine.transaction.jdbc.JdbcTransactionConfig;
43  import com.ibatis.sqlmap.engine.transaction.jta.JTANamespace;
44  import com.ibatis.sqlmap.engine.transaction.jta.JakartaTransactionConfig;
45  import com.ibatis.sqlmap.engine.transaction.jta.JavaxTransactionConfig;
46  import com.ibatis.sqlmap.engine.type.CustomTypeHandler;
47  import com.ibatis.sqlmap.engine.type.DomCollectionTypeMarker;
48  import com.ibatis.sqlmap.engine.type.DomTypeMarker;
49  import com.ibatis.sqlmap.engine.type.TypeHandler;
50  import com.ibatis.sqlmap.engine.type.TypeHandlerFactory;
51  import com.ibatis.sqlmap.engine.type.XmlCollectionTypeMarker;
52  import com.ibatis.sqlmap.engine.type.XmlTypeMarker;
53  
54  import java.util.Iterator;
55  
56  /**
57   * The Class SqlMapConfiguration.
58   */
59  public class SqlMapConfiguration {
60  
61    /** The Constant PROBE. */
62    private static final Probe PROBE = ProbeFactory.getProbe();
63  
64    /** The error context. */
65    private ErrorContext errorContext;
66  
67    /** The delegate. */
68    private SqlMapExecutorDelegate delegate;
69  
70    /** The type handler factory. */
71    private TypeHandlerFactory typeHandlerFactory;
72  
73    /** The client. */
74    private SqlMapClientImpl client;
75  
76    /** The default statement timeout. */
77    private Integer defaultStatementTimeout;
78  
79    /**
80     * Instantiates a new sql map configuration.
81     */
82    public SqlMapConfiguration() {
83      errorContext = new ErrorContext();
84      delegate = new SqlMapExecutorDelegate();
85      typeHandlerFactory = delegate.getTypeHandlerFactory();
86      client = new SqlMapClientImpl(delegate);
87      registerDefaultTypeAliases();
88    }
89  
90    /**
91     * Gets the type handler factory.
92     *
93     * @return the type handler factory
94     */
95    public TypeHandlerFactory getTypeHandlerFactory() {
96      return typeHandlerFactory;
97    }
98  
99    /**
100    * Gets the error context.
101    *
102    * @return the error context
103    */
104   public ErrorContext getErrorContext() {
105     return errorContext;
106   }
107 
108   /**
109    * Gets the client.
110    *
111    * @return the client
112    */
113   public SqlMapClientImpl getClient() {
114     return client;
115   }
116 
117   /**
118    * Gets the delegate.
119    *
120    * @return the delegate
121    */
122   public SqlMapExecutorDelegate getDelegate() {
123     return delegate;
124   }
125 
126   /**
127    * Sets the class info cache enabled.
128    *
129    * @param classInfoCacheEnabled
130    *          the new class info cache enabled
131    */
132   public void setClassInfoCacheEnabled(boolean classInfoCacheEnabled) {
133     errorContext.setActivity("setting class info cache enabled/disabled");
134     ClassInfo.setCacheEnabled(classInfoCacheEnabled);
135   }
136 
137   /**
138    * Sets the lazy loading enabled.
139    *
140    * @param lazyLoadingEnabled
141    *          the new lazy loading enabled
142    */
143   public void setLazyLoadingEnabled(boolean lazyLoadingEnabled) {
144     errorContext.setActivity("setting lazy loading enabled/disabled");
145     client.getDelegate().setLazyLoadingEnabled(lazyLoadingEnabled);
146   }
147 
148   /**
149    * Sets the statement caching enabled.
150    *
151    * @param statementCachingEnabled
152    *          the new statement caching enabled
153    */
154   public void setStatementCachingEnabled(boolean statementCachingEnabled) {
155     errorContext.setActivity("setting statement caching enabled/disabled");
156     client.getDelegate().setStatementCacheEnabled(statementCachingEnabled);
157   }
158 
159   /**
160    * Sets the cache models enabled.
161    *
162    * @param cacheModelsEnabled
163    *          the new cache models enabled
164    */
165   public void setCacheModelsEnabled(boolean cacheModelsEnabled) {
166     errorContext.setActivity("setting cache models enabled/disabled");
167     client.getDelegate().setCacheModelsEnabled(cacheModelsEnabled);
168   }
169 
170   /**
171    * Sets the enhancement enabled.
172    *
173    * @param enhancementEnabled
174    *          the new enhancement enabled
175    */
176   public void setEnhancementEnabled(boolean enhancementEnabled) {
177     errorContext.setActivity("setting enhancement enabled/disabled");
178     try {
179       enhancementEnabled = enhancementEnabled && Resources.classForName("net.sf.cglib.proxy.InvocationHandler") != null;
180     } catch (ClassNotFoundException e) {
181       enhancementEnabled = false;
182     }
183     client.getDelegate().setEnhancementEnabled(enhancementEnabled);
184     AccessPlanFactory.setBytecodeEnhancementEnabled(enhancementEnabled);
185   }
186 
187   /**
188    * Sets the use column label.
189    *
190    * @param useColumnLabel
191    *          the new use column label
192    */
193   public void setUseColumnLabel(boolean useColumnLabel) {
194     client.getDelegate().setUseColumnLabel(useColumnLabel);
195   }
196 
197   /**
198    * Sets the force multiple result set support.
199    *
200    * @param forceMultipleResultSetSupport
201    *          the new force multiple result set support
202    */
203   public void setForceMultipleResultSetSupport(boolean forceMultipleResultSetSupport) {
204     client.getDelegate().setForceMultipleResultSetSupport(forceMultipleResultSetSupport);
205   }
206 
207   /**
208    * Sets the default statement timeout.
209    *
210    * @param defaultTimeout
211    *          the new default statement timeout
212    */
213   public void setDefaultStatementTimeout(Integer defaultTimeout) {
214     errorContext.setActivity("setting default timeout");
215     if (defaultTimeout != null) {
216       try {
217         defaultStatementTimeout = defaultTimeout;
218       } catch (NumberFormatException e) {
219         throw new SqlMapException("Specified defaultStatementTimeout is not a valid integer");
220       }
221     }
222   }
223 
224   /**
225    * Sets the transaction manager.
226    *
227    * @param txManager
228    *          the new transaction manager
229    */
230   public void setTransactionManager(TransactionManager txManager) {
231     delegate.setTxManager(txManager);
232   }
233 
234   /**
235    * Sets the result object factory.
236    *
237    * @param rof
238    *          the new result object factory
239    */
240   public void setResultObjectFactory(ResultObjectFactory rof) {
241     delegate.setResultObjectFactory(rof);
242   }
243 
244   /**
245    * New type handler.
246    *
247    * @param javaType
248    *          the java type
249    * @param jdbcType
250    *          the jdbc type
251    * @param callback
252    *          the callback
253    */
254   public void newTypeHandler(Class javaType, String jdbcType, Object callback) {
255     try {
256       errorContext.setActivity("building a building custom type handler");
257       TypeHandlerFactory typeHandlerFactory = client.getDelegate().getTypeHandlerFactory();
258       TypeHandler typeHandler;
259       if (callback instanceof TypeHandlerCallback) {
260         typeHandler = new CustomTypeHandler((TypeHandlerCallback) callback);
261       } else if (callback instanceof TypeHandler) {
262         typeHandler = (TypeHandler) callback;
263       } else {
264         throw new RuntimeException(
265             "The object '" + callback + "' is not a valid implementation of TypeHandler or TypeHandlerCallback");
266       }
267       errorContext.setMoreInfo("Check the javaType attribute '" + javaType + "' (must be a classname) or the jdbcType '"
268           + jdbcType + "' (must be a JDBC type name).");
269       if (jdbcType != null && jdbcType.length() > 0) {
270         typeHandlerFactory.register(javaType, jdbcType, typeHandler);
271       } else {
272         typeHandlerFactory.register(javaType, typeHandler);
273       }
274     } catch (Exception e) {
275       throw new SqlMapException("Error registering occurred.  Cause: " + e, e);
276     }
277     errorContext.setMoreInfo(null);
278     errorContext.setObjectId(null);
279   }
280 
281   /**
282    * New cache model config.
283    *
284    * @param id
285    *          the id
286    * @param controller
287    *          the controller
288    * @param readOnly
289    *          the read only
290    * @param serialize
291    *          the serialize
292    *
293    * @return the cache model config
294    */
295   public CacheModelConfig newCacheModelConfig(String id, CacheController controller, boolean readOnly,
296       boolean serialize) {
297     return new CacheModelConfig(this, id, controller, readOnly, serialize);
298   }
299 
300   /**
301    * New parameter map config.
302    *
303    * @param id
304    *          the id
305    * @param parameterClass
306    *          the parameter class
307    *
308    * @return the parameter map config
309    */
310   public ParameterMapConfig newParameterMapConfig(String id, Class parameterClass) {
311     return new ParameterMapConfig(this, id, parameterClass);
312   }
313 
314   /**
315    * New result map config.
316    *
317    * @param id
318    *          the id
319    * @param resultClass
320    *          the result class
321    * @param groupBy
322    *          the group by
323    * @param extended
324    *          the extended
325    * @param xmlName
326    *          the xml name
327    *
328    * @return the result map config
329    */
330   public ResultMapConfig newResultMapConfig(String id, Class resultClass, String groupBy, String extended,
331       String xmlName) {
332     return new ResultMapConfig(this, id, resultClass, groupBy, extended, xmlName);
333   }
334 
335   /**
336    * New mapped statement config.
337    *
338    * @param id
339    *          the id
340    * @param statement
341    *          the statement
342    * @param processor
343    *          the processor
344    * @param parameterMapName
345    *          the parameter map name
346    * @param parameterClass
347    *          the parameter class
348    * @param resultMapName
349    *          the result map name
350    * @param additionalResultMapNames
351    *          the additional result map names
352    * @param resultClass
353    *          the result class
354    * @param additionalResultClasses
355    *          the additional result classes
356    * @param resultSetType
357    *          the result set type
358    * @param fetchSize
359    *          the fetch size
360    * @param allowRemapping
361    *          the allow remapping
362    * @param timeout
363    *          the timeout
364    * @param cacheModelName
365    *          the cache model name
366    * @param xmlResultName
367    *          the xml result name
368    *
369    * @return the mapped statement config
370    */
371   public MappedStatementConfig newMappedStatementConfig(String id, MappedStatement statement, SqlSource processor,
372       String parameterMapName, Class parameterClass, String resultMapName, String[] additionalResultMapNames,
373       Class resultClass, Class[] additionalResultClasses, String resultSetType, Integer fetchSize,
374       boolean allowRemapping, Integer timeout, String cacheModelName, String xmlResultName) {
375     return new MappedStatementConfig(this, id, statement, processor, parameterMapName, parameterClass, resultMapName,
376         additionalResultMapNames, resultClass, additionalResultClasses, cacheModelName, resultSetType, fetchSize,
377         allowRemapping, timeout, defaultStatementTimeout, xmlResultName);
378   }
379 
380   /**
381    * Finalize sql map config.
382    */
383   public void finalizeSqlMapConfig() {
384     wireUpCacheModels();
385     bindResultMapDiscriminators();
386   }
387 
388   /**
389    * Resolve type handler.
390    *
391    * @param typeHandlerFactory
392    *          the type handler factory
393    * @param clazz
394    *          the clazz
395    * @param propertyName
396    *          the property name
397    * @param javaType
398    *          the java type
399    * @param jdbcType
400    *          the jdbc type
401    *
402    * @return the type handler
403    */
404   TypeHandler resolveTypeHandler(TypeHandlerFactory typeHandlerFactory, Class clazz, String propertyName,
405       Class javaType, String jdbcType) {
406     return resolveTypeHandler(typeHandlerFactory, clazz, propertyName, javaType, jdbcType, false);
407   }
408 
409   /**
410    * Resolve type handler.
411    *
412    * @param typeHandlerFactory
413    *          the type handler factory
414    * @param clazz
415    *          the clazz
416    * @param propertyName
417    *          the property name
418    * @param javaType
419    *          the java type
420    * @param jdbcType
421    *          the jdbc type
422    * @param useSetterToResolve
423    *          the use setter to resolve
424    *
425    * @return the type handler
426    */
427   TypeHandler resolveTypeHandler(TypeHandlerFactory typeHandlerFactory, Class clazz, String propertyName,
428       Class javaType, String jdbcType, boolean useSetterToResolve) {
429     TypeHandler handler;
430     if (clazz == null) {
431       // Unknown
432       handler = typeHandlerFactory.getUnkownTypeHandler();
433     } else if (DomTypeMarker.class.isAssignableFrom(clazz)) {
434       // DOM
435       handler = typeHandlerFactory.getTypeHandler(String.class, jdbcType);
436     } else if (java.util.Map.class.isAssignableFrom(clazz)) {
437       // Map
438       if (javaType == null) {
439         handler = typeHandlerFactory.getUnkownTypeHandler();
440         // BUG 1012591 - typeHandlerFactory.getTypeHandler(java.lang.Object.class, jdbcType);
441       } else {
442         handler = typeHandlerFactory.getTypeHandler(javaType, jdbcType);
443       }
444     } else if (typeHandlerFactory.getTypeHandler(clazz, jdbcType) != null) {
445       // Primitive
446       handler = typeHandlerFactory.getTypeHandler(clazz, jdbcType);
447     } else {
448       // JavaBean
449       if (javaType == null) {
450         if (useSetterToResolve) {
451           Class type = PROBE.getPropertyTypeForSetter(clazz, propertyName);
452           handler = typeHandlerFactory.getTypeHandler(type, jdbcType);
453         } else {
454           Class type = PROBE.getPropertyTypeForGetter(clazz, propertyName);
455           handler = typeHandlerFactory.getTypeHandler(type, jdbcType);
456         }
457       } else {
458         handler = typeHandlerFactory.getTypeHandler(javaType, jdbcType);
459       }
460     }
461     return handler;
462   }
463 
464   /**
465    * Register default type aliases.
466    */
467   private void registerDefaultTypeAliases() {
468     // TRANSACTION ALIASES
469     typeHandlerFactory.putTypeAlias("JDBC", JdbcTransactionConfig.class.getName());
470     typeHandlerFactory.putTypeAlias("JTA", JTANamespace.JAKARTA_TRANSACTION_CLASS == null
471         ? JavaxTransactionConfig.class.getName() : JakartaTransactionConfig.class.getName());
472     typeHandlerFactory.putTypeAlias("EXTERNAL", ExternalTransactionConfig.class.getName());
473 
474     // DATA SOURCE ALIASES
475     typeHandlerFactory.putTypeAlias("SIMPLE", SimpleDataSourceFactory.class.getName());
476     typeHandlerFactory.putTypeAlias("DBCP", DbcpDataSourceFactory.class.getName());
477     typeHandlerFactory.putTypeAlias("JNDI", JndiDataSourceFactory.class.getName());
478 
479     // CACHE ALIASES
480     typeHandlerFactory.putTypeAlias("FIFO", FifoCacheController.class.getName());
481     typeHandlerFactory.putTypeAlias("LRU", LruCacheController.class.getName());
482     typeHandlerFactory.putTypeAlias("MEMORY", MemoryCacheController.class.getName());
483 
484     // TYPE ALIASEs
485     typeHandlerFactory.putTypeAlias("dom", DomTypeMarker.class.getName());
486     typeHandlerFactory.putTypeAlias("domCollection", DomCollectionTypeMarker.class.getName());
487     typeHandlerFactory.putTypeAlias("xml", XmlTypeMarker.class.getName());
488     typeHandlerFactory.putTypeAlias("xmlCollection", XmlCollectionTypeMarker.class.getName());
489   }
490 
491   /**
492    * Wire up cache models.
493    */
494   private void wireUpCacheModels() {
495     // Wire Up Cache Models
496     Iterator cacheNames = client.getDelegate().getCacheModelNames();
497     while (cacheNames.hasNext()) {
498       String cacheName = (String) cacheNames.next();
499       CacheModel cacheModel = client.getDelegate().getCacheModel(cacheName);
500       Iterator statementNames = cacheModel.getFlushTriggerStatementNames();
501       while (statementNames.hasNext()) {
502         String statementName = (String) statementNames.next();
503         MappedStatement statement = client.getDelegate().getMappedStatement(statementName);
504         if (statement == null) {
505           throw new RuntimeException("Could not find statement named '" + statementName
506               + "' for use as a flush trigger for the cache model named '" + cacheName + "'.");
507         }
508         statement.addExecuteListener(cacheModel);
509       }
510     }
511   }
512 
513   /**
514    * Bind result map discriminators.
515    */
516   private void bindResultMapDiscriminators() {
517     // Bind discriminators
518     Iterator names = delegate.getResultMapNames();
519     while (names.hasNext()) {
520       String name = (String) names.next();
521       ResultMap rm = delegate.getResultMap(name);
522       Discriminator disc = rm.getDiscriminator();
523       if (disc != null) {
524         disc.bindSubMaps();
525       }
526     }
527   }
528 
529 }