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