View Javadoc
1   /*
2    * Copyright 2004-2022 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.impl;
17  
18  import com.ibatis.common.beans.Probe;
19  import com.ibatis.common.beans.ProbeFactory;
20  import com.ibatis.common.jdbc.exception.NestedSQLException;
21  import com.ibatis.common.util.PaginatedList;
22  import com.ibatis.sqlmap.client.SqlMapException;
23  import com.ibatis.sqlmap.client.event.RowHandler;
24  import com.ibatis.sqlmap.engine.cache.CacheKey;
25  import com.ibatis.sqlmap.engine.cache.CacheModel;
26  import com.ibatis.sqlmap.engine.exchange.DataExchangeFactory;
27  import com.ibatis.sqlmap.engine.execution.BatchException;
28  import com.ibatis.sqlmap.engine.execution.DefaultSqlExecutor;
29  import com.ibatis.sqlmap.engine.execution.SqlExecutor;
30  import com.ibatis.sqlmap.engine.mapping.parameter.ParameterMap;
31  import com.ibatis.sqlmap.engine.mapping.result.ResultMap;
32  import com.ibatis.sqlmap.engine.mapping.result.ResultObjectFactory;
33  import com.ibatis.sqlmap.engine.mapping.statement.InsertStatement;
34  import com.ibatis.sqlmap.engine.mapping.statement.MappedStatement;
35  import com.ibatis.sqlmap.engine.mapping.statement.PaginatedDataList;
36  import com.ibatis.sqlmap.engine.mapping.statement.SelectKeyStatement;
37  import com.ibatis.sqlmap.engine.scope.SessionScope;
38  import com.ibatis.sqlmap.engine.scope.StatementScope;
39  import com.ibatis.sqlmap.engine.transaction.Transaction;
40  import com.ibatis.sqlmap.engine.transaction.TransactionException;
41  import com.ibatis.sqlmap.engine.transaction.TransactionManager;
42  import com.ibatis.sqlmap.engine.transaction.TransactionState;
43  import com.ibatis.sqlmap.engine.transaction.user.UserProvidedTransaction;
44  import com.ibatis.sqlmap.engine.type.TypeHandlerFactory;
45  
46  import java.sql.Connection;
47  import java.sql.SQLException;
48  import java.util.HashMap;
49  import java.util.Iterator;
50  import java.util.List;
51  import java.util.Map;
52  
53  import javax.sql.DataSource;
54  
55  /**
56   * The workhorse that really runs the SQL.
57   */
58  public class SqlMapExecutorDelegate {
59  
60    /** The Constant PROBE. */
61    private static final Probe PROBE = ProbeFactory.getProbe();
62  
63    /** The lazy loading enabled. */
64    private boolean lazyLoadingEnabled = true;
65  
66    /** The cache models enabled. */
67    private boolean cacheModelsEnabled = true;
68  
69    /** The enhancement enabled. */
70    private boolean enhancementEnabled = false;
71  
72    /** The use column label. */
73    private boolean useColumnLabel = true;
74  
75    /** The force multiple result set support. */
76    private boolean forceMultipleResultSetSupport;
77  
78    /** The tx manager. */
79    private TransactionManager txManager;
80  
81    /** The mapped statements. */
82    private HashMap mappedStatements;
83  
84    /** The cache models. */
85    private HashMap cacheModels;
86  
87    /** The result maps. */
88    private HashMap resultMaps;
89  
90    /** The parameter maps. */
91    private HashMap parameterMaps;
92  
93    /** The sql executor. */
94    protected SqlExecutor sqlExecutor;
95  
96    /** The type handler factory. */
97    private TypeHandlerFactory typeHandlerFactory;
98  
99    /** The data exchange factory. */
100   private DataExchangeFactory dataExchangeFactory;
101 
102   /** The result object factory. */
103   private ResultObjectFactory resultObjectFactory;
104 
105   /** The statement cache enabled. */
106   private boolean statementCacheEnabled = true;
107 
108   /**
109    * Default constructor.
110    */
111   public SqlMapExecutorDelegate() {
112     mappedStatements = new HashMap();
113     cacheModels = new HashMap();
114     resultMaps = new HashMap();
115     parameterMaps = new HashMap();
116 
117     sqlExecutor = new DefaultSqlExecutor();
118     typeHandlerFactory = new TypeHandlerFactory();
119     dataExchangeFactory = new DataExchangeFactory(typeHandlerFactory);
120   }
121 
122   /**
123    * Sets the custom executor.
124    *
125    * @param sqlExecutorClass
126    *          the new custom executor
127    */
128   public void setCustomExecutor(String sqlExecutorClass) {
129     try {
130       Class factoryClass = Class.forName(sqlExecutorClass);
131       sqlExecutor = (SqlExecutor) factoryClass.newInstance();
132     } catch (Exception e) {
133       throw new SqlMapException(
134           "Error instantiating " + sqlExecutorClass + ". Please check the class given in properties file. Cause: " + e,
135           e);
136     }
137   }
138 
139   /**
140    * DO NOT DEPEND ON THIS. Here to avoid breaking spring integration.
141    *
142    * @return the max transactions
143    *
144    * @deprecated
145    */
146   public int getMaxTransactions() {
147     return -1;
148   }
149 
150   /**
151    * Getter for the DataExchangeFactory.
152    *
153    * @return - the DataExchangeFactory
154    */
155   public DataExchangeFactory getDataExchangeFactory() {
156     return dataExchangeFactory;
157   }
158 
159   /**
160    * Getter for the TypeHandlerFactory.
161    *
162    * @return - the TypeHandlerFactory
163    */
164   public TypeHandlerFactory getTypeHandlerFactory() {
165     return typeHandlerFactory;
166   }
167 
168   /**
169    * Getter for the status of lazy loading.
170    *
171    * @return - the status
172    */
173   public boolean isLazyLoadingEnabled() {
174     return lazyLoadingEnabled;
175   }
176 
177   /**
178    * Turn on or off lazy loading.
179    *
180    * @param lazyLoadingEnabled
181    *          - the new state of caching
182    */
183   public void setLazyLoadingEnabled(boolean lazyLoadingEnabled) {
184     this.lazyLoadingEnabled = lazyLoadingEnabled;
185   }
186 
187   /**
188    * Getter for the status of caching.
189    *
190    * @return - the status
191    */
192   public boolean isCacheModelsEnabled() {
193     return cacheModelsEnabled;
194   }
195 
196   /**
197    * Turn on or off caching.
198    *
199    * @param cacheModelsEnabled
200    *          - the new state of caching
201    */
202   public void setCacheModelsEnabled(boolean cacheModelsEnabled) {
203     this.cacheModelsEnabled = cacheModelsEnabled;
204   }
205 
206   /**
207    * Getter for the status of CGLib enhancements.
208    *
209    * @return - the status
210    */
211   public boolean isEnhancementEnabled() {
212     return enhancementEnabled;
213   }
214 
215   /**
216    * Turn on or off CGLib enhancements.
217    *
218    * @param enhancementEnabled
219    *          - the new state
220    */
221   public void setEnhancementEnabled(boolean enhancementEnabled) {
222     this.enhancementEnabled = enhancementEnabled;
223   }
224 
225   /**
226    * Checks if is use column label.
227    *
228    * @return true, if is use column label
229    */
230   public boolean isUseColumnLabel() {
231     return useColumnLabel;
232   }
233 
234   /**
235    * Sets the use column label.
236    *
237    * @param useColumnLabel
238    *          the new use column label
239    */
240   public void setUseColumnLabel(boolean useColumnLabel) {
241     this.useColumnLabel = useColumnLabel;
242   }
243 
244   /**
245    * Getter for the transaction manager.
246    *
247    * @return - the transaction manager
248    */
249   public TransactionManager getTxManager() {
250     return txManager;
251   }
252 
253   /**
254    * Setter for the transaction manager.
255    *
256    * @param txManager
257    *          - the transaction manager
258    */
259   public void setTxManager(TransactionManager txManager) {
260     this.txManager = txManager;
261   }
262 
263   /**
264    * Add a mapped statement.
265    *
266    * @param ms
267    *          - the mapped statement to add
268    */
269   public void addMappedStatement(MappedStatement ms) {
270     if (mappedStatements.containsKey(ms.getId())) {
271       throw new SqlMapException("There is already a statement named " + ms.getId() + " in this SqlMap.");
272     }
273     ms.setBaseCacheKey(hashCode());
274     mappedStatements.put(ms.getId(), ms);
275   }
276 
277   /**
278    * Get an iterator of the mapped statements.
279    *
280    * @return - the iterator
281    */
282   public Iterator getMappedStatementNames() {
283     return mappedStatements.keySet().iterator();
284   }
285 
286   /**
287    * Get a mappedstatement by its ID.
288    *
289    * @param id
290    *          - the statement ID
291    *
292    * @return - the mapped statement
293    */
294   public MappedStatement getMappedStatement(String id) {
295     MappedStatement ms = (MappedStatement) mappedStatements.get(id);
296     if (ms == null) {
297       throw new SqlMapException("There is no statement named " + id + " in this SqlMap.");
298     }
299     return ms;
300   }
301 
302   /**
303    * Add a cache model.
304    *
305    * @param model
306    *          - the model to add
307    */
308   public void addCacheModel(CacheModel model) {
309     cacheModels.put(model.getId(), model);
310   }
311 
312   /**
313    * Get an iterator of the cache models.
314    *
315    * @return - the cache models
316    */
317   public Iterator getCacheModelNames() {
318     return cacheModels.keySet().iterator();
319   }
320 
321   /**
322    * Get a cache model by ID.
323    *
324    * @param id
325    *          - the ID
326    *
327    * @return - the cache model
328    */
329   public CacheModel getCacheModel(String id) {
330     CacheModel model = (CacheModel) cacheModels.get(id);
331     if (model == null) {
332       throw new SqlMapException("There is no cache model named " + id + " in this SqlMap.");
333     }
334     return model;
335   }
336 
337   /**
338    * Add a result map.
339    *
340    * @param map
341    *          - the result map to add
342    */
343   public void addResultMap(ResultMap map) {
344     resultMaps.put(map.getId(), map);
345   }
346 
347   /**
348    * Get an iterator of the result maps.
349    *
350    * @return - the result maps
351    */
352   public Iterator getResultMapNames() {
353     return resultMaps.keySet().iterator();
354   }
355 
356   /**
357    * Get a result map by ID.
358    *
359    * @param id
360    *          - the ID
361    *
362    * @return - the result map
363    */
364   public ResultMap getResultMap(String id) {
365     ResultMap map = (ResultMap) resultMaps.get(id);
366     if (map == null) {
367       throw new SqlMapException("There is no result map named " + id + " in this SqlMap.");
368     }
369     return map;
370   }
371 
372   /**
373    * Add a parameter map.
374    *
375    * @param map
376    *          - the map to add
377    */
378   public void addParameterMap(ParameterMap map) {
379     parameterMaps.put(map.getId(), map);
380   }
381 
382   /**
383    * Get an iterator of all of the parameter maps.
384    *
385    * @return - the parameter maps
386    */
387   public Iterator getParameterMapNames() {
388     return parameterMaps.keySet().iterator();
389   }
390 
391   /**
392    * Get a parameter map by ID.
393    *
394    * @param id
395    *          - the ID
396    *
397    * @return - the parameter map
398    */
399   public ParameterMap getParameterMap(String id) {
400     ParameterMap map = (ParameterMap) parameterMaps.get(id);
401     if (map == null) {
402       throw new SqlMapException("There is no parameter map named " + id + " in this SqlMap.");
403     }
404     return map;
405   }
406 
407   /**
408    * Flush all of the data caches.
409    */
410   public void flushDataCache() {
411     Iterator models = cacheModels.values().iterator();
412     while (models.hasNext()) {
413       ((CacheModel) models.next()).flush();
414     }
415   }
416 
417   /**
418    * Flush a single cache by ID.
419    *
420    * @param id
421    *          - the ID
422    */
423   public void flushDataCache(String id) {
424     CacheModel model = getCacheModel(id);
425     if (model != null) {
426       model.flush();
427     }
428   }
429 
430   // -- Basic Methods
431   /**
432    * Call an insert statement by ID.
433    *
434    * @param sessionScope
435    *          - the session
436    * @param id
437    *          - the statement ID
438    * @param param
439    *          - the parameter object
440    *
441    * @return - the generated key (or null)
442    *
443    * @throws SQLException
444    *           - if the insert fails
445    */
446   public Object insert(SessionScope sessionScope, String id, Object param) throws SQLException {
447     Object generatedKey = null;
448 
449     MappedStatement ms = getMappedStatement(id);
450     Transaction trans = getTransaction(sessionScope);
451     boolean autoStart = trans == null;
452 
453     try {
454       trans = autoStartTransaction(sessionScope, autoStart, trans);
455 
456       SelectKeyStatement selectKeyStatement = null;
457       if (ms instanceof InsertStatement) {
458         selectKeyStatement = ((InsertStatement) ms).getSelectKeyStatement();
459       }
460 
461       // Here we get the old value for the key property. We'll want it later if for some
462       // reason the
463       // insert fails.
464       Object oldKeyValue = null;
465       String keyProperty = null;
466       boolean resetKeyValueOnFailure = false;
467       if (selectKeyStatement != null && !selectKeyStatement.isRunAfterSQL()) {
468         keyProperty = selectKeyStatement.getKeyProperty();
469         oldKeyValue = PROBE.getObject(param, keyProperty);
470         generatedKey = executeSelectKey(sessionScope, trans, ms, param);
471         resetKeyValueOnFailure = true;
472       }
473 
474       StatementScope statementScope = beginStatementScope(sessionScope, ms);
475       try {
476         ms.executeUpdate(statementScope, trans, param);
477       } catch (SQLException e) {
478         // uh-oh, the insert failed, so if we set the reset flag earlier, we'll put the old
479         // value
480         // back...
481         if (resetKeyValueOnFailure)
482           PROBE.setObject(param, keyProperty, oldKeyValue);
483         // ...and still throw the exception.
484         throw e;
485       } finally {
486         endStatementScope(statementScope);
487       }
488 
489       if (selectKeyStatement != null && selectKeyStatement.isRunAfterSQL()) {
490         generatedKey = executeSelectKey(sessionScope, trans, ms, param);
491       }
492 
493       autoCommitTransaction(sessionScope, autoStart);
494     } finally {
495       autoEndTransaction(sessionScope, autoStart);
496     }
497 
498     return generatedKey;
499   }
500 
501   /**
502    * Execute select key.
503    *
504    * @param sessionScope
505    *          the session scope
506    * @param trans
507    *          the trans
508    * @param ms
509    *          the ms
510    * @param param
511    *          the param
512    *
513    * @return the object
514    *
515    * @throws SQLException
516    *           the SQL exception
517    */
518   private Object executeSelectKey(SessionScope sessionScope, Transaction trans, MappedStatement ms, Object param)
519       throws SQLException {
520     Object generatedKey = null;
521     StatementScope statementScope;
522     InsertStatement insert = (InsertStatement) ms;
523     SelectKeyStatement selectKeyStatement = insert.getSelectKeyStatement();
524     if (selectKeyStatement != null) {
525       statementScope = beginStatementScope(sessionScope, selectKeyStatement);
526       try {
527         generatedKey = selectKeyStatement.executeQueryForObject(statementScope, trans, param, null);
528         String keyProp = selectKeyStatement.getKeyProperty();
529         if (keyProp != null) {
530           PROBE.setObject(param, keyProp, generatedKey);
531         }
532       } finally {
533         endStatementScope(statementScope);
534       }
535     }
536     return generatedKey;
537   }
538 
539   /**
540    * Execute an update statement.
541    *
542    * @param sessionScope
543    *          - the session scope
544    * @param id
545    *          - the statement ID
546    * @param param
547    *          - the parameter object
548    *
549    * @return - the number of rows updated
550    *
551    * @throws SQLException
552    *           - if the update fails
553    */
554   public int update(SessionScope sessionScope, String id, Object param) throws SQLException {
555     int rows = 0;
556 
557     MappedStatement ms = getMappedStatement(id);
558     Transaction trans = getTransaction(sessionScope);
559     boolean autoStart = trans == null;
560 
561     try {
562       trans = autoStartTransaction(sessionScope, autoStart, trans);
563 
564       StatementScope statementScope = beginStatementScope(sessionScope, ms);
565       try {
566         rows = ms.executeUpdate(statementScope, trans, param);
567       } finally {
568         endStatementScope(statementScope);
569       }
570 
571       autoCommitTransaction(sessionScope, autoStart);
572     } finally {
573       autoEndTransaction(sessionScope, autoStart);
574     }
575 
576     return rows;
577   }
578 
579   /**
580    * Execute a delete statement.
581    *
582    * @param sessionScope
583    *          - the session scope
584    * @param id
585    *          - the statement ID
586    * @param param
587    *          - the parameter object
588    *
589    * @return - the number of rows deleted
590    *
591    * @throws SQLException
592    *           - if the delete fails
593    */
594   public int delete(SessionScope sessionScope, String id, Object param) throws SQLException {
595     return update(sessionScope, id, param);
596   }
597 
598   /**
599    * Execute a select for a single object.
600    *
601    * @param sessionScope
602    *          - the session scope
603    * @param id
604    *          - the statement ID
605    * @param paramObject
606    *          - the parameter object
607    *
608    * @return - the result of the query
609    *
610    * @throws SQLException
611    *           - if the query fails
612    */
613   public Object queryForObject(SessionScope sessionScope, String id, Object paramObject) throws SQLException {
614     return queryForObject(sessionScope, id, paramObject, null);
615   }
616 
617   /**
618    * Execute a select for a single object.
619    *
620    * @param sessionScope
621    *          - the session scope
622    * @param id
623    *          - the statement ID
624    * @param paramObject
625    *          - the parameter object
626    * @param resultObject
627    *          - the result object (if not supplied or null, a new object will be created)
628    *
629    * @return - the result of the query
630    *
631    * @throws SQLException
632    *           - if the query fails
633    */
634   public Object queryForObject(SessionScope sessionScope, String id, Object paramObject, Object resultObject)
635       throws SQLException {
636     Object object = null;
637 
638     MappedStatement ms = getMappedStatement(id);
639     Transaction trans = getTransaction(sessionScope);
640     boolean autoStart = trans == null;
641 
642     try {
643       trans = autoStartTransaction(sessionScope, autoStart, trans);
644 
645       StatementScope statementScope = beginStatementScope(sessionScope, ms);
646       try {
647         object = ms.executeQueryForObject(statementScope, trans, paramObject, resultObject);
648       } finally {
649         endStatementScope(statementScope);
650       }
651 
652       autoCommitTransaction(sessionScope, autoStart);
653     } finally {
654       autoEndTransaction(sessionScope, autoStart);
655     }
656 
657     return object;
658   }
659 
660   /**
661    * Execute a query for a list.
662    *
663    * @param sessionScope
664    *          - the session scope
665    * @param id
666    *          - the statement ID
667    * @param paramObject
668    *          - the parameter object
669    *
670    * @return - the data list
671    *
672    * @throws SQLException
673    *           - if the query fails
674    */
675   public List queryForList(SessionScope sessionScope, String id, Object paramObject) throws SQLException {
676     return queryForList(sessionScope, id, paramObject, SqlExecutor.NO_SKIPPED_RESULTS, SqlExecutor.NO_MAXIMUM_RESULTS);
677   }
678 
679   /**
680    * Execute a query for a list.
681    *
682    * @param sessionScope
683    *          - the session scope
684    * @param id
685    *          - the statement ID
686    * @param paramObject
687    *          - the parameter object
688    * @param skip
689    *          - the number of rows to skip
690    * @param max
691    *          - the maximum number of rows to return
692    *
693    * @return - the data list
694    *
695    * @throws SQLException
696    *           - if the query fails
697    */
698   public List queryForList(SessionScope sessionScope, String id, Object paramObject, int skip, int max)
699       throws SQLException {
700     List list = null;
701 
702     MappedStatement ms = getMappedStatement(id);
703     Transaction trans = getTransaction(sessionScope);
704     boolean autoStart = trans == null;
705 
706     try {
707       trans = autoStartTransaction(sessionScope, autoStart, trans);
708 
709       StatementScope statementScope = beginStatementScope(sessionScope, ms);
710       try {
711         list = ms.executeQueryForList(statementScope, trans, paramObject, skip, max);
712       } finally {
713         endStatementScope(statementScope);
714       }
715 
716       autoCommitTransaction(sessionScope, autoStart);
717     } finally {
718       autoEndTransaction(sessionScope, autoStart);
719     }
720 
721     return list;
722   }
723 
724   /**
725    * Execute a query with a row handler. The row handler is called once per row in the query results.
726    *
727    * @param sessionScope
728    *          - the session scope
729    * @param id
730    *          - the statement ID
731    * @param paramObject
732    *          - the parameter object
733    * @param rowHandler
734    *          - the row handler
735    *
736    * @throws SQLException
737    *           - if the query fails
738    */
739   public void queryWithRowHandler(SessionScope sessionScope, String id, Object paramObject, RowHandler rowHandler)
740       throws SQLException {
741 
742     MappedStatement ms = getMappedStatement(id);
743     Transaction trans = getTransaction(sessionScope);
744     boolean autoStart = trans == null;
745 
746     try {
747       trans = autoStartTransaction(sessionScope, autoStart, trans);
748 
749       StatementScope statementScope = beginStatementScope(sessionScope, ms);
750       try {
751         ms.executeQueryWithRowHandler(statementScope, trans, paramObject, rowHandler);
752       } finally {
753         endStatementScope(statementScope);
754       }
755 
756       autoCommitTransaction(sessionScope, autoStart);
757     } finally {
758       autoEndTransaction(sessionScope, autoStart);
759     }
760 
761   }
762 
763   /**
764    * Execute a query and return a paginated list.
765    *
766    * @param sessionScope
767    *          - the session scope
768    * @param id
769    *          - the statement ID
770    * @param paramObject
771    *          - the parameter object
772    * @param pageSize
773    *          - the page size
774    *
775    * @return - the data list
776    *
777    * @throws SQLException
778    *           - if the query fails
779    *
780    * @deprecated All paginated list features have been deprecated
781    */
782   public PaginatedList queryForPaginatedList(SessionScope sessionScope, String id, Object paramObject, int pageSize)
783       throws SQLException {
784     return new PaginatedDataList(sessionScope.getSqlMapExecutor(), id, paramObject, pageSize);
785   }
786 
787   /**
788    * Execute a query for a map. The map has the table key as the key, and the results as the map data
789    *
790    * @param sessionScope
791    *          - the session scope
792    * @param id
793    *          - the statement ID
794    * @param paramObject
795    *          - the parameter object
796    * @param keyProp
797    *          - the key property (from the results for the map)
798    *
799    * @return - the Map
800    *
801    * @throws SQLException
802    *           - if the query fails
803    */
804   public Map queryForMap(SessionScope sessionScope, String id, Object paramObject, String keyProp) throws SQLException {
805     return queryForMap(sessionScope, id, paramObject, keyProp, null);
806   }
807 
808   /**
809    * Execute a query for a map. The map has the table key as the key, and a property from the results as the map data
810    *
811    * @param sessionScope
812    *          - the session scope
813    * @param id
814    *          - the statement ID
815    * @param paramObject
816    *          - the parameter object
817    * @param keyProp
818    *          - the property for the map key
819    * @param valueProp
820    *          - the property for the map data
821    *
822    * @return - the Map
823    *
824    * @throws SQLException
825    *           - if the query fails
826    */
827   public Map queryForMap(SessionScope sessionScope, String id, Object paramObject, String keyProp, String valueProp)
828       throws SQLException {
829     Map map = new HashMap();
830 
831     List list = queryForList(sessionScope, id, paramObject);
832 
833     for (int i = 0, n = list.size(); i < n; i++) {
834       Object object = list.get(i);
835       Object key = PROBE.getObject(object, keyProp);
836       Object value = null;
837       if (valueProp == null) {
838         value = object;
839       } else {
840         value = PROBE.getObject(object, valueProp);
841       }
842       map.put(key, value);
843     }
844 
845     return map;
846   }
847 
848   // -- Transaction Control Methods
849   /**
850    * Start a transaction on the session.
851    *
852    * @param sessionScope
853    *          - the session
854    *
855    * @throws SQLException
856    *           - if the transaction could not be started
857    */
858   public void startTransaction(SessionScope sessionScope) throws SQLException {
859     try {
860       txManager.begin(sessionScope);
861     } catch (TransactionException e) {
862       throw new NestedSQLException("Could not start transaction.  Cause: " + e, e);
863     }
864   }
865 
866   /**
867    * Start a transaction on the session with the specified isolation level.
868    *
869    * @param sessionScope
870    *          - the session
871    * @param transactionIsolation
872    *          the transaction isolation
873    *
874    * @throws SQLException
875    *           - if the transaction could not be started
876    */
877   public void startTransaction(SessionScope sessionScope, int transactionIsolation) throws SQLException {
878     try {
879       txManager.begin(sessionScope, transactionIsolation);
880     } catch (TransactionException e) {
881       throw new NestedSQLException("Could not start transaction.  Cause: " + e, e);
882     }
883   }
884 
885   /**
886    * Commit the transaction on a session.
887    *
888    * @param sessionScope
889    *          - the session
890    *
891    * @throws SQLException
892    *           - if the transaction could not be committed
893    */
894   public void commitTransaction(SessionScope sessionScope) throws SQLException {
895     try {
896       // Auto batch execution
897       if (sessionScope.isInBatch()) {
898         executeBatch(sessionScope);
899       }
900       sqlExecutor.cleanup(sessionScope);
901       txManager.commit(sessionScope);
902     } catch (TransactionException e) {
903       throw new NestedSQLException("Could not commit transaction.  Cause: " + e, e);
904     }
905   }
906 
907   /**
908    * End the transaction on a session.
909    *
910    * @param sessionScope
911    *          - the session
912    *
913    * @throws SQLException
914    *           - if the transaction could not be ended
915    */
916   public void endTransaction(SessionScope sessionScope) throws SQLException {
917     try {
918       try {
919         sqlExecutor.cleanup(sessionScope);
920       } finally {
921         txManager.end(sessionScope);
922       }
923     } catch (TransactionException e) {
924       throw new NestedSQLException("Error while ending transaction.  Cause: " + e, e);
925     }
926   }
927 
928   /**
929    * Start a batch for a session.
930    *
931    * @param sessionScope
932    *          - the session
933    */
934   public void startBatch(SessionScope sessionScope) {
935     sessionScope.setInBatch(true);
936   }
937 
938   /**
939    * Execute a batch for a session.
940    *
941    * @param sessionScope
942    *          - the session
943    *
944    * @return - the number of rows impacted by the batch
945    *
946    * @throws SQLException
947    *           - if the batch fails
948    */
949   public int executeBatch(SessionScope sessionScope) throws SQLException {
950     sessionScope.setInBatch(false);
951     return sqlExecutor.executeBatch(sessionScope);
952   }
953 
954   /**
955    * Execute a batch for a session.
956    *
957    * @param sessionScope
958    *          - the session
959    *
960    * @return - a List of BatchResult objects (may be null if no batch has been initiated). There will be one BatchResult
961    *         object in the list for each sub-batch executed
962    *
963    * @throws SQLException
964    *           if a database access error occurs, or the drive does not support batch statements
965    * @throws BatchException
966    *           if the driver throws BatchUpdateException
967    */
968   public List executeBatchDetailed(SessionScope sessionScope) throws SQLException, BatchException {
969     sessionScope.setInBatch(false);
970     return sqlExecutor.executeBatchDetailed(sessionScope);
971   }
972 
973   /**
974    * Use a user-provided transaction for a session.
975    *
976    * @param sessionScope
977    *          - the session scope
978    * @param userConnection
979    *          - the user supplied connection
980    */
981   public void setUserProvidedTransaction(SessionScope sessionScope, Connection userConnection) {
982     if (sessionScope.getTransactionState() == TransactionState.STATE_USER_PROVIDED) {
983       sessionScope.recallTransactionState();
984     }
985     if (userConnection != null) {
986       Connection conn = userConnection;
987       sessionScope.saveTransactionState();
988       sessionScope.setTransaction(new UserProvidedTransaction(conn));
989       sessionScope.setTransactionState(TransactionState.STATE_USER_PROVIDED);
990     } else {
991       sessionScope.setTransaction(null);
992       sessionScope.closePreparedStatements();
993       sessionScope.cleanup();
994     }
995   }
996 
997   /**
998    * Get the DataSource for the session.
999    *
1000    * @return - the DataSource
1001    */
1002   public DataSource getDataSource() {
1003     DataSource ds = null;
1004     if (txManager != null) {
1005       ds = txManager.getConfig().getDataSource();
1006     }
1007     return ds;
1008   }
1009 
1010   /**
1011    * Getter for the SqlExecutor.
1012    *
1013    * @return the SqlExecutor
1014    */
1015   public SqlExecutor getSqlExecutor() {
1016     return sqlExecutor;
1017   }
1018 
1019   /**
1020    * Get a transaction for the session.
1021    *
1022    * @param sessionScope
1023    *          - the session
1024    *
1025    * @return - the transaction
1026    */
1027   public Transaction getTransaction(SessionScope sessionScope) {
1028     return sessionScope.getTransaction();
1029   }
1030 
1031   // -- Protected Methods
1032 
1033   /**
1034    * Auto end transaction.
1035    *
1036    * @param sessionScope
1037    *          the session scope
1038    * @param autoStart
1039    *          the auto start
1040    *
1041    * @throws SQLException
1042    *           the SQL exception
1043    */
1044   protected void autoEndTransaction(SessionScope sessionScope, boolean autoStart) throws SQLException {
1045     if (autoStart) {
1046       sessionScope.getSqlMapTxMgr().endTransaction();
1047     }
1048   }
1049 
1050   /**
1051    * Auto commit transaction.
1052    *
1053    * @param sessionScope
1054    *          the session scope
1055    * @param autoStart
1056    *          the auto start
1057    *
1058    * @throws SQLException
1059    *           the SQL exception
1060    */
1061   protected void autoCommitTransaction(SessionScope sessionScope, boolean autoStart) throws SQLException {
1062     if (autoStart) {
1063       sessionScope.getSqlMapTxMgr().commitTransaction();
1064     }
1065   }
1066 
1067   /**
1068    * Auto start transaction.
1069    *
1070    * @param sessionScope
1071    *          the session scope
1072    * @param autoStart
1073    *          the auto start
1074    * @param trans
1075    *          the trans
1076    *
1077    * @return the transaction
1078    *
1079    * @throws SQLException
1080    *           the SQL exception
1081    */
1082   protected Transaction autoStartTransaction(SessionScope sessionScope, boolean autoStart, Transaction trans)
1083       throws SQLException {
1084     Transaction transaction = trans;
1085     if (autoStart) {
1086       sessionScope.getSqlMapTxMgr().startTransaction();
1087       transaction = getTransaction(sessionScope);
1088     }
1089     return transaction;
1090   }
1091 
1092   @Override
1093   public boolean equals(Object obj) {
1094     return this == obj;
1095   }
1096 
1097   @Override
1098   public int hashCode() {
1099     CacheKey key = new CacheKey();
1100     if (txManager != null) {
1101       key.update(txManager);
1102       if (txManager.getConfig().getDataSource() != null) {
1103         key.update(txManager.getConfig().getDataSource());
1104       }
1105     }
1106     key.update(System.identityHashCode(this));
1107     return key.hashCode();
1108   }
1109 
1110   /**
1111    * Begin statement scope.
1112    *
1113    * @param sessionScope
1114    *          the session scope
1115    * @param mappedStatement
1116    *          the mapped statement
1117    *
1118    * @return the statement scope
1119    */
1120   protected StatementScope beginStatementScope(SessionScope sessionScope, MappedStatement mappedStatement) {
1121     StatementScope statementScope = new StatementScope(sessionScope);
1122     sessionScope.incrementRequestStackDepth();
1123     mappedStatement.initRequest(statementScope);
1124     return statementScope;
1125   }
1126 
1127   /**
1128    * End statement scope.
1129    *
1130    * @param statementScope
1131    *          the statement scope
1132    */
1133   protected void endStatementScope(StatementScope statementScope) {
1134     statementScope.getSession().decrementRequestStackDepth();
1135   }
1136 
1137   /**
1138    * Begin session scope.
1139    *
1140    * @return the session scope
1141    */
1142   protected SessionScope beginSessionScope() {
1143     return new SessionScope();
1144   }
1145 
1146   /**
1147    * End session scope.
1148    *
1149    * @param sessionScope
1150    *          the session scope
1151    */
1152   protected void endSessionScope(SessionScope sessionScope) {
1153     sessionScope.cleanup();
1154   }
1155 
1156   /**
1157    * Gets the result object factory.
1158    *
1159    * @return the result object factory
1160    */
1161   public ResultObjectFactory getResultObjectFactory() {
1162     return resultObjectFactory;
1163   }
1164 
1165   /**
1166    * Sets the result object factory.
1167    *
1168    * @param resultObjectFactory
1169    *          the new result object factory
1170    */
1171   public void setResultObjectFactory(ResultObjectFactory resultObjectFactory) {
1172     this.resultObjectFactory = resultObjectFactory;
1173   }
1174 
1175   /**
1176    * Checks if is statement cache enabled.
1177    *
1178    * @return true, if is statement cache enabled
1179    */
1180   public boolean isStatementCacheEnabled() {
1181     return statementCacheEnabled;
1182   }
1183 
1184   /**
1185    * Sets the statement cache enabled.
1186    *
1187    * @param statementCacheEnabled
1188    *          the new statement cache enabled
1189    */
1190   public void setStatementCacheEnabled(boolean statementCacheEnabled) {
1191     this.statementCacheEnabled = statementCacheEnabled;
1192   }
1193 
1194   /**
1195    * Checks if is force multiple result set support.
1196    *
1197    * @return true, if is force multiple result set support
1198    */
1199   public boolean isForceMultipleResultSetSupport() {
1200     return forceMultipleResultSetSupport;
1201   }
1202 
1203   /**
1204    * Sets the force multiple result set support.
1205    *
1206    * @param forceMultipleResultSetSupport
1207    *          the new force multiple result set support
1208    */
1209   public void setForceMultipleResultSetSupport(boolean forceMultipleResultSetSupport) {
1210     this.forceMultipleResultSetSupport = forceMultipleResultSetSupport;
1211   }
1212 }