Save This Page
Home » ibatis-2.3.src » com.ibatis.sqlmap.engine.execution » [javadoc | source]
    1   /*
    2    *  Copyright 2004 Clinton Begin
    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    *      http://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.execution;
   17   
   18   import com.ibatis.sqlmap.engine.mapping.parameter.BasicParameterMapping;
   19   import com.ibatis.sqlmap.engine.mapping.parameter.ParameterMap;
   20   import com.ibatis.sqlmap.engine.mapping.parameter.ParameterMapping;
   21   import com.ibatis.sqlmap.engine.mapping.result.ResultMap;
   22   import com.ibatis.sqlmap.engine.mapping.statement.RowHandlerCallback;
   23   import com.ibatis.sqlmap.engine.scope.ErrorContext;
   24   import com.ibatis.sqlmap.engine.scope.RequestScope;
   25   import com.ibatis.sqlmap.engine.scope.SessionScope;
   26   
   27   import java.sql;
   28   import java.util.ArrayList;
   29   import java.util.List;
   30   
   31   /**
   32    * Class responsible for executing the SQL
   33    */
   34   public class SqlExecutor {
   35   
   36     //
   37     // Constants
   38     //
   39   
   40     /**
   41      * Constant to let us know not to skip anything
   42      */
   43     public static final int NO_SKIPPED_RESULTS = 0;
   44     
   45     /**
   46      * Constant to let us know to include all records
   47      */
   48     public static final int NO_MAXIMUM_RESULTS = -999999;
   49   
   50     //
   51     // Public Methods
   52     //
   53   
   54     /**
   55      * Execute an update
   56      * 
   57      * @param request - the request scope
   58      * @param conn - the database connection
   59      * @param sql - the sql statement to execute
   60      * @param parameters - the parameters for the sql statement
   61      * 
   62      * @return - the number of records changed
   63      * 
   64      * @throws SQLException - if the update fails
   65      */
   66     public int executeUpdate(RequestScope request, Connection conn, String sql, Object[] parameters)
   67         throws SQLException {
   68       ErrorContext errorContext = request.getErrorContext();
   69       errorContext.setActivity("executing update");
   70       errorContext.setObjectId(sql);
   71   
   72       PreparedStatement ps = null;
   73       int rows = 0;
   74   
   75       try {
   76         errorContext.setMoreInfo("Check the SQL Statement (preparation failed).");
   77         ps = conn.prepareStatement(sql);
   78   
   79         errorContext.setMoreInfo("Check the parameters (set parameters failed).");
   80         request.getParameterMap().setParameters(request, ps, parameters);
   81   
   82         errorContext.setMoreInfo("Check the statement (update failed).");
   83   
   84         ps.execute();
   85         rows = ps.getUpdateCount();
   86       }
   87       finally {
   88         closeStatement(ps);
   89       }
   90   
   91       return rows;
   92     }
   93   
   94     /**
   95      * Adds a statement to a batch
   96      * 
   97      * @param request - the request scope
   98      * @param conn - the database connection
   99      * @param sql - the sql statement
  100      * @param parameters - the parameters for the statement
  101      * 
  102      * @throws SQLException - if the statement fails
  103      */
  104     public void addBatch(RequestScope request, Connection conn, String sql, Object[] parameters)
  105         throws SQLException {
  106       Batch batch = (Batch) request.getSession().getBatch();
  107       if (batch == null) {
  108         batch = new Batch();
  109         request.getSession().setBatch(batch);
  110       }
  111       batch.addBatch(request, conn, sql, parameters);
  112     }
  113   
  114     /**
  115      * Execute a batch of statements
  116      * 
  117      * @param session - the session scope
  118      * 
  119      * @return - the number of rows impacted by the batch
  120      * 
  121      * @throws SQLException - if a statement fails
  122      */
  123     public int executeBatch(SessionScope session)
  124         throws SQLException {
  125       int rows = 0;
  126       Batch batch = (Batch) session.getBatch();
  127       if (batch != null) {
  128         try {
  129           rows = batch.executeBatch();
  130         } finally {
  131           batch.cleanupBatch();
  132         }
  133       }
  134       return rows;
  135     }
  136   
  137     /**
  138      * Long form of the method to execute a query
  139      * 
  140      * @param request - the request scope
  141      * @param conn - the database connection
  142      * @param sql - the SQL statement to execute
  143      * @param parameters - the parameters for the statement
  144      * @param skipResults - the number of results to skip
  145      * @param maxResults - the maximum number of results to return
  146      * @param callback - the row handler for the query
  147      * 
  148      * @throws SQLException - if the query fails
  149      */
  150     public void executeQuery(RequestScope request, Connection conn, String sql, Object[] parameters,
  151                              int skipResults, int maxResults, RowHandlerCallback callback)
  152         throws SQLException {
  153       ErrorContext errorContext = request.getErrorContext();
  154       errorContext.setActivity("executing query");
  155       errorContext.setObjectId(sql);
  156   
  157       PreparedStatement ps = null;
  158       ResultSet rs = null;
  159   
  160       try {
  161         errorContext.setMoreInfo("Check the SQL Statement (preparation failed).");
  162   
  163         Integer rsType = request.getStatement().getResultSetType();
  164         if (rsType != null) {
  165           ps = conn.prepareStatement(sql, rsType.intValue(), ResultSet.CONCUR_READ_ONLY);
  166         } else {
  167           ps = conn.prepareStatement(sql);
  168         }
  169   
  170         Integer fetchSize = request.getStatement().getFetchSize();
  171         if (fetchSize != null) {
  172           ps.setFetchSize(fetchSize.intValue());
  173         }
  174   
  175         errorContext.setMoreInfo("Check the parameters (set parameters failed).");
  176         request.getParameterMap().setParameters(request, ps, parameters);
  177   
  178         errorContext.setMoreInfo("Check the statement (query failed).");
  179   
  180         ps.execute();
  181         rs = ps.getResultSet();
  182   
  183         errorContext.setMoreInfo("Check the results (failed to retrieve results).");
  184         handleResults(request, rs, skipResults, maxResults, callback);
  185   
  186         // clear out remaining results
  187         while (ps.getMoreResults());
  188   
  189       } finally {
  190         try {
  191           closeResultSet(rs);
  192         } finally {
  193           closeStatement(ps);
  194         }
  195       }
  196   
  197     }
  198   
  199     /**
  200      * Execute a stored procedure that updates data
  201      * 
  202      * @param request - the request scope
  203      * @param conn - the database connection
  204      * @param sql - the SQL to call the procedure
  205      * @param parameters - the parameters for the procedure
  206      * 
  207      * @return - the rows impacted by the procedure
  208      * 
  209      * @throws SQLException - if the procedure fails
  210      */
  211     public int executeUpdateProcedure(RequestScope request, Connection conn, String sql, Object[] parameters)
  212         throws SQLException {
  213       ErrorContext errorContext = request.getErrorContext();
  214       errorContext.setActivity("executing update procedure");
  215       errorContext.setObjectId(sql);
  216   
  217       CallableStatement cs = null;
  218       int rows = 0;
  219   
  220       try {
  221         errorContext.setMoreInfo("Check the SQL Statement (preparation failed).");
  222         cs = conn.prepareCall(sql);
  223   
  224         ParameterMap parameterMap = request.getParameterMap();
  225   
  226         ParameterMapping[] mappings = parameterMap.getParameterMappings();
  227   
  228         errorContext.setMoreInfo("Check the output parameters (register output parameters failed).");
  229         registerOutputParameters(cs, mappings);
  230   
  231         errorContext.setMoreInfo("Check the parameters (set parameters failed).");
  232         parameterMap.setParameters(request, cs, parameters);
  233   
  234         errorContext.setMoreInfo("Check the statement (update procedure failed).");
  235   
  236         cs.execute();
  237         rows = cs.getUpdateCount();
  238   
  239         errorContext.setMoreInfo("Check the output parameters (retrieval of output parameters failed).");
  240         retrieveOutputParameters(cs, mappings, parameters);
  241       } finally {
  242         closeStatement(cs);
  243       }
  244   
  245       return rows;
  246     }
  247   
  248     /**
  249      * Execute a stored procedure
  250      * 
  251      * @param request - the request scope
  252      * @param conn - the database connection
  253      * @param sql - the sql to call the procedure
  254      * @param parameters - the parameters for the procedure
  255      * @param skipResults - the number of results to skip
  256      * @param maxResults - the maximum number of results to return
  257      * @param callback - a row handler for processing the results
  258      * 
  259      * @throws SQLException - if the procedure fails
  260      */
  261     public void executeQueryProcedure(RequestScope request, Connection conn, String sql, Object[] parameters,
  262                                       int skipResults, int maxResults, RowHandlerCallback callback)
  263         throws SQLException {
  264       ErrorContext errorContext = request.getErrorContext();
  265       errorContext.setActivity("executing query procedure");
  266       errorContext.setObjectId(sql);
  267   
  268       CallableStatement cs = null;
  269       ResultSet rs = null;
  270   
  271       try {
  272         errorContext.setMoreInfo("Check the SQL Statement (preparation failed).");
  273         cs = conn.prepareCall(sql);
  274   
  275         ParameterMap parameterMap = request.getParameterMap();
  276   
  277         ParameterMapping[] mappings = parameterMap.getParameterMappings();
  278   
  279         errorContext.setMoreInfo("Check the output parameters (register output parameters failed).");
  280         registerOutputParameters(cs, mappings);
  281   
  282         errorContext.setMoreInfo("Check the parameters (set parameters failed).");
  283         parameterMap.setParameters(request, cs, parameters);
  284   
  285         errorContext.setMoreInfo("Check the statement (update procedure failed).");
  286   
  287         cs.execute();
  288         rs = cs.getResultSet();
  289   
  290         errorContext.setMoreInfo("Check the results (failed to retrieve results).");
  291         handleResults(request, rs, skipResults, maxResults, callback);
  292   
  293         errorContext.setMoreInfo("Check the output parameters (retrieval of output parameters failed).");
  294         retrieveOutputParameters(cs, mappings, parameters);
  295   
  296       } finally {
  297         try {
  298           closeResultSet(rs);
  299         } finally {
  300           closeStatement(cs);
  301         }
  302       }
  303   
  304     }
  305   
  306     /**
  307      * Clean up any batches on the session
  308      * 
  309      * @param session - the session to clean up
  310      */
  311     public void cleanup(SessionScope session) {
  312       Batch batch = (Batch) session.getBatch();
  313       if (batch != null) {
  314         batch.cleanupBatch();
  315         session.setBatch(null);
  316       }
  317     }
  318   
  319     //
  320     // Private Methods
  321     //
  322   
  323     private void retrieveOutputParameters(CallableStatement cs, ParameterMapping[] mappings, Object[] parameters) throws SQLException {
  324       for (int i = 0; i < mappings.length; i++) {
  325         BasicParameterMapping mapping = ((BasicParameterMapping) mappings[i]);
  326         if (mapping.isOutputAllowed()) {
  327           Object o = mapping.getTypeHandler().getResult(cs, i + 1);
  328           parameters[i] = o;
  329         }
  330       }
  331     }
  332   
  333     private void registerOutputParameters(CallableStatement cs, ParameterMapping[] mappings) throws SQLException {
  334       for (int i = 0; i < mappings.length; i++) {
  335         BasicParameterMapping mapping = ((BasicParameterMapping) mappings[i]);
  336         if (mapping.isOutputAllowed()) {
  337           cs.registerOutParameter(i + 1, mapping.getJdbcType());
  338         }
  339       }
  340     }
  341   
  342     private void handleResults(RequestScope request, ResultSet rs, int skipResults, int maxResults, RowHandlerCallback callback) throws SQLException {
  343       try {
  344         request.setResultSet(rs);
  345         ResultMap resultMap = request.getResultMap();
  346         if (resultMap != null) {
  347           // Skip Results
  348           if (rs.getType() != ResultSet.TYPE_FORWARD_ONLY) {
  349             if (skipResults > 0) {
  350               rs.absolute(skipResults);
  351             }
  352           } else {
  353             for (int i = 0; i < skipResults; i++) {
  354               if (!rs.next()) {
  355                 break;
  356               }
  357             }
  358           }
  359   
  360           // Get Results
  361           int resultsFetched = 0;
  362           while ((maxResults == SqlExecutor.NO_MAXIMUM_RESULTS || resultsFetched < maxResults) && rs.next()) {
  363             Object[] columnValues = resultMap.resolveSubMap(request, rs).getResults(request, rs);
  364             callback.handleResultObject(request, columnValues, rs);
  365             resultsFetched++;
  366           }
  367         }
  368       } finally {
  369         request.setResultSet(null);
  370       }
  371     }
  372   
  373     /**
  374      * @param ps
  375      */
  376     private static void closeStatement(PreparedStatement ps) {
  377       if (ps != null) {
  378         try {
  379           ps.close();
  380         } catch (SQLException e) {
  381           // ignore
  382         }
  383       }
  384     }
  385   
  386     /**
  387      * @param rs
  388      */
  389     private static void closeResultSet(ResultSet rs) {
  390       if (rs != null) {
  391         try {
  392           rs.close();
  393         } catch (SQLException e) {
  394           // ignore
  395         }
  396       }
  397     }
  398   
  399     //
  400     // Inner Classes
  401     //
  402   
  403     private static class Batch {
  404       private String currentSql;
  405       private List statementList = new ArrayList();
  406       private int size;
  407       private static final int SUCCESS_NO_INFO = -2;
  408       private static final int EXECUTE_FAILED = -3;
  409   
  410       /**
  411        * Create a new batch
  412        */
  413       public Batch() {
  414         this.size = 0;
  415       }
  416   
  417       /**
  418        * Getter for the batch size
  419        * 
  420        * @return - the batch size
  421        */
  422       public int getSize() {
  423         return size;
  424       }
  425   
  426       /**
  427        * Add a prepared statement to the batch
  428        * 
  429        * @param request - the request scope
  430        * @param conn - the database connection
  431        * @param sql - the SQL to add
  432        * @param parameters - the parameters for the SQL
  433        * 
  434        * @throws SQLException - if the prepare for the SQL fails
  435        */
  436       public void addBatch(RequestScope request, Connection conn, String sql, Object[] parameters) throws SQLException {
  437         PreparedStatement ps = null;
  438         if (currentSql != null
  439             && sql.hashCode() == currentSql.hashCode()
  440             && sql.length() == currentSql.length()) {
  441           int last = statementList.size() - 1;
  442           ps = (PreparedStatement) statementList.get(last);
  443         } else {
  444           ps = conn.prepareStatement(sql);
  445           currentSql = sql;
  446           statementList.add(ps);
  447         }
  448         request.getParameterMap().setParameters(request, ps, parameters);
  449         ps.addBatch();
  450         size++;
  451       }
  452   
  453       /**
  454        * Execute the current session's batch
  455        * 
  456        * @return - the number of rows updated
  457        * 
  458        * @throws SQLException - if the batch fails
  459        */
  460       public int executeBatch() throws SQLException {
  461         int totalRowCount = 0;
  462         for (int i = 0, n = statementList.size(); i < n; i++) {
  463           PreparedStatement ps = (PreparedStatement) statementList.get(i);
  464           int[] rowCounts = ps.executeBatch();
  465           for (int j = 0; j < rowCounts.length; j++) {
  466             if (rowCounts[j] == SUCCESS_NO_INFO) {
  467               // do nothing
  468             } else if (rowCounts[j] == EXECUTE_FAILED) {
  469               throw new SQLException("The batched statement at index " + j + " failed to execute.");
  470             } else {
  471               totalRowCount += rowCounts[j];
  472             }
  473           }
  474         }
  475         return totalRowCount;
  476       }
  477   
  478       /**
  479        * Close all the statements in the batch and clear all the statements
  480        */
  481       public void cleanupBatch() {
  482         for (int i = 0, n = statementList.size(); i < n; i++) {
  483           PreparedStatement ps = (PreparedStatement) statementList.get(i);
  484           closeStatement(ps);
  485         }
  486         currentSql = null;
  487         statementList.clear();
  488         size = 0;
  489       }
  490     }
  491   
  492   }

Save This Page
Home » ibatis-2.3.src » com.ibatis.sqlmap.engine.execution » [javadoc | source]