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