StatementHandler解析
接口的作用是statement處理器,位於mybatis包的org.apache.ibatis.executor.statement目錄下,源碼如下:
1 package org.apache.ibatis.executor.statement; 2 3 import java.sql.Connection; 4 import java.sql.SQLException; 5 import java.sql.Statement; 6 import java.util.List; 7 8 import org.apache.ibatis.executor.parameter.ParameterHandler; 9 import org.apache.ibatis.mapping.BoundSql; 10 import org.apache.ibatis.session.ResultHandler; 11 12 public interface StatementHandler { 13 14 //sql預編譯,構建Statement對象 15 Statement prepare(Connection connection) 16 throws SQLException; 17 18 //對prepare方法構建的預編譯的SQL進行參數的設置 19 void parameterize(Statement statement) 20 throws SQLException; 21 22 //批量處理 23 void batch(Statement statement) 24 throws SQLException; 25 26 //執行預編譯后的SQL--update語句 27 int update(Statement statement) 28 throws SQLException; 29 30 //執行預編譯后的SQL--select語句 31 <E> List<E> query(Statement statement, ResultHandler resultHandler) 32 throws SQLException; 33 34 //獲取SQL封裝類BoundSql對象 35 BoundSql getBoundSql(); 36 37 //獲取參數處理器對象 38 ParameterHandler getParameterHandler(); 39 40 }
可見StatementHandler的作用就是先通過prepare方法構建一個Statement對象,然后再調用其他方法對Statement對象進行處理
StatementHandler和Executor類似,StatementHandler也有兩個實現類,BaseStatementHandler和RoutingStatementHandler
而BaseStatement又有三個子類實現它的抽象方法,下面再挨個分析,先看最簡單的BaseStatementHandler
BaseStatementHandler解析
BaseStatementHandler是一個抽象父類,有三個子類繼承於它,源碼如下:
1 package org.apache.ibatis.executor.statement; 2 3 import java.sql.Connection; 4 import java.sql.SQLException; 5 import java.sql.Statement; 6 7 import org.apache.ibatis.executor.ErrorContext; 8 import org.apache.ibatis.executor.Executor; 9 import org.apache.ibatis.executor.ExecutorException; 10 import org.apache.ibatis.executor.keygen.KeyGenerator; 11 import org.apache.ibatis.executor.parameter.ParameterHandler; 12 import org.apache.ibatis.executor.resultset.ResultSetHandler; 13 import org.apache.ibatis.mapping.BoundSql; 14 import org.apache.ibatis.mapping.MappedStatement; 15 import org.apache.ibatis.reflection.factory.ObjectFactory; 16 import org.apache.ibatis.session.Configuration; 17 import org.apache.ibatis.session.ResultHandler; 18 import org.apache.ibatis.session.RowBounds; 19 import org.apache.ibatis.type.TypeHandlerRegistry; 20 21 public abstract class BaseStatementHandler implements StatementHandler { 22 23 protected final Configuration configuration;//全局配置 24 protected final ObjectFactory objectFactory;//對象工廠 25 protected final TypeHandlerRegistry typeHandlerRegistry; 26 protected final ResultSetHandler resultSetHandler;//結果集處理器 27 protected final ParameterHandler parameterHandler;//參數處理器 28 29 protected final Executor executor;//執行器 30 protected final MappedStatement mappedStatement;//mapper的SQL對象 31 protected final RowBounds rowBounds;//分頁參數 32 33 protected BoundSql boundSql;//sql封裝對象 34 35 //構造方法 36 protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { 37 this.configuration = mappedStatement.getConfiguration(); 38 this.executor = executor; 39 this.mappedStatement = mappedStatement; 40 this.rowBounds = rowBounds; 41 42 this.typeHandlerRegistry = configuration.getTypeHandlerRegistry(); 43 this.objectFactory = configuration.getObjectFactory(); 44 45 if (boundSql == null) { // issue #435, get the key before calculating the statement 46 generateKeys(parameterObject); 47 boundSql = mappedStatement.getBoundSql(parameterObject); 48 } 49 50 this.boundSql = boundSql; 51 52 this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql); 53 this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql); 54 } 55 56 //返回boundSql 57 public BoundSql getBoundSql() { 58 return boundSql; 59 } 60 61 //返回parameterHandler 62 public ParameterHandler getParameterHandler() { 63 return parameterHandler; 64 } 65 66 //預編譯SQL語句 67 public Statement prepare(Connection connection) throws SQLException { 68 ErrorContext.instance().sql(boundSql.getSql()); 69 Statement statement = null; 70 try { 71 statement = instantiateStatement(connection);//調用抽象方法構建statement對象是,但是沒有具體實現,而是交給其子類去實現 72 setStatementTimeout(statement);//設置statement超時時間 73 setFetchSize(statement);//設置statement的fetchSize 74 return statement; 75 } catch (SQLException e) { 76 closeStatement(statement); 77 throw e; 78 } catch (Exception e) { 79 closeStatement(statement); 80 throw new ExecutorException("Error preparing statement. Cause: " + e, e); 81 } 82 } 83 84 protected abstract Statement instantiateStatement(Connection connection) throws SQLException; 85 86 //給statement對象設置timeout 87 protected void setStatementTimeout(Statement stmt) throws SQLException { 88 Integer timeout = mappedStatement.getTimeout(); 89 Integer defaultTimeout = configuration.getDefaultStatementTimeout(); 90 if (timeout != null) { 91 stmt.setQueryTimeout(timeout); 92 } else if (defaultTimeout != null) { 93 stmt.setQueryTimeout(defaultTimeout); 94 } 95 } 96 97 //給statement對象設置fetchSize 98 protected void setFetchSize(Statement stmt) throws SQLException { 99 Integer fetchSize = mappedStatement.getFetchSize(); 100 if (fetchSize != null) { 101 stmt.setFetchSize(fetchSize); 102 } 103 } 104 105 //關閉statement 106 protected void closeStatement(Statement statement) { 107 try { 108 if (statement != null) { 109 statement.close(); 110 } 111 } catch (SQLException e) { 112 //ignore 113 } 114 } 115 116 //根據參數對象生成key 117 protected void generateKeys(Object parameter) { 118 KeyGenerator keyGenerator = mappedStatement.getKeyGenerator(); 119 ErrorContext.instance().store(); 120 keyGenerator.processBefore(executor, mappedStatement, null, parameter); 121 ErrorContext.instance().recall(); 122 } 123 124 }
可以看出BaseStatementHandler只是實現了StatementHandler的三個方法,其中getBoundSql和getParameterHandler方法只是返回了由構造方法初始化的boundSql和parameterHandler屬性,
而prepare方法是用於構建Statement對象的,但是BaseStatementHandler只是調用了自身的抽象方法instantiateStatement來創建,然后對statement對象進行其他處理,但是創建的過程則沒有實現,而是交給了其子類去實現。
BaseStatementHandler有三個子類,分別為:
SimpleStatememtHandler:最簡單的StatementHandler,處理不帶參數運行的SQL
PreparedStatementHandler:預處理Statement的handler,處理帶參數允許的SQL
CallableStatementHandler:存儲過程的Statement的handler,處理存儲過程SQL
先來看最簡單的SimpleStatementHandler,它繼承於BaseStatementHandler,所以它需要實現StatementHandler的接口,還需要重寫父類BaseStatementHandler的抽象方法,源碼如下:
1 package org.apache.ibatis.executor.statement; 2 3 import java.sql.Connection; 4 import java.sql.ResultSet; 5 import java.sql.SQLException; 6 import java.sql.Statement; 7 import java.util.List; 8 9 import org.apache.ibatis.executor.Executor; 10 import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator; 11 import org.apache.ibatis.executor.keygen.KeyGenerator; 12 import org.apache.ibatis.executor.keygen.SelectKeyGenerator; 13 import org.apache.ibatis.mapping.BoundSql; 14 import org.apache.ibatis.mapping.MappedStatement; 15 import org.apache.ibatis.session.ResultHandler; 16 import org.apache.ibatis.session.RowBounds; 17 18 public class SimpleStatementHandler extends BaseStatementHandler { 19 20 //構造方法執行父類的構造方法 21 public SimpleStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { 22 super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql); 23 } 24 25 //執行update操作 26 public int update(Statement statement) 27 throws SQLException { 28 String sql = boundSql.getSql(); 29 Object parameterObject = boundSql.getParameterObject(); 30 KeyGenerator keyGenerator = mappedStatement.getKeyGenerator(); 31 int rows; 32 //最終都是執行statement的getUpdateCount方法 33 if (keyGenerator instanceof Jdbc3KeyGenerator) { 34 statement.execute(sql, Statement.RETURN_GENERATED_KEYS); 35 rows = statement.getUpdateCount(); 36 keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject); 37 } else if (keyGenerator instanceof SelectKeyGenerator) { 38 statement.execute(sql); 39 rows = statement.getUpdateCount(); 40 keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject); 41 } else { 42 statement.execute(sql); 43 rows = statement.getUpdateCount(); 44 } 45 return rows; 46 } 47 48 //給statement添加批量處理sql語句 49 public void batch(Statement statement) 50 throws SQLException { 51 String sql = boundSql.getSql(); 52 statement.addBatch(sql); 53 } 54 55 //執行查詢語句 56 public <E> List<E> query(Statement statement, ResultHandler resultHandler) 57 throws SQLException { 58 String sql = boundSql.getSql(); 59 statement.execute(sql);//statement.execute方法執行sql語句 60 return resultSetHandler.<E>handleResultSets(statement); 61 } 62 63 //構造Statement對象 64 protected Statement instantiateStatement(Connection connection) throws SQLException { 65 //通過Connection來create一個Statement對象 66 if (mappedStatement.getResultSetType() != null) { 67 return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY); 68 } else { 69 return connection.createStatement(); 70 } 71 } 72 73 //由於SimpleStatementHandler是處理沒有參數的SQL,所以參數設置的方法無需任何處理 74 public void parameterize(Statement statement) throws SQLException { 75 // N/A 76 } 77 78 }
可以看出主要是通過Connection創建一個Statement對象,然后通過調用Statement的execute方法執行sql語句
再看下PreparedStatementHandler,源碼如下
1 package org.apache.ibatis.executor.statement; 2 3 import java.sql.Connection; 4 import java.sql.PreparedStatement; 5 import java.sql.ResultSet; 6 import java.sql.SQLException; 7 import java.sql.Statement; 8 import java.util.List; 9 10 import org.apache.ibatis.executor.Executor; 11 import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator; 12 import org.apache.ibatis.executor.keygen.KeyGenerator; 13 import org.apache.ibatis.mapping.BoundSql; 14 import org.apache.ibatis.mapping.MappedStatement; 15 import org.apache.ibatis.session.ResultHandler; 16 import org.apache.ibatis.session.RowBounds; 17 18 public class PreparedStatementHandler extends BaseStatementHandler { 19 20 //執行父類構造方法 21 public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { 22 super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql); 23 } 24 25 //執行update操作 26 public int update(Statement statement) throws SQLException { 27 PreparedStatement ps = (PreparedStatement) statement; 28 ps.execute(); 29 int rows = ps.getUpdateCount(); 30 Object parameterObject = boundSql.getParameterObject(); 31 KeyGenerator keyGenerator = mappedStatement.getKeyGenerator(); 32 keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject); 33 return rows; 34 } 35 36 //給preparedStatement對象添加批量處理sql語句 37 public void batch(Statement statement) throws SQLException { 38 PreparedStatement ps = (PreparedStatement) statement; 39 ps.addBatch(); 40 } 41 42 //執行preparedStatement的查詢語句 43 public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { 44 PreparedStatement ps = (PreparedStatement) statement; 45 ps.execute(); 46 return resultSetHandler.<E> handleResultSets(ps); 47 } 48 49 //構建Statement的子類PreparedStatement對象 50 protected Statement instantiateStatement(Connection connection) throws SQLException { 51 String sql = boundSql.getSql(); 52 if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) { 53 String[] keyColumnNames = mappedStatement.getKeyColumns(); 54 if (keyColumnNames == null) { 55 return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS); 56 } else { 57 return connection.prepareStatement(sql, keyColumnNames); 58 } 59 } else if (mappedStatement.getResultSetType() != null) { 60 return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY); 61 } else { 62 return connection.prepareStatement(sql); 63 } 64 } 65 66 //給Statement對象設置參數 67 public void parameterize(Statement statement) throws SQLException { 68 parameterHandler.setParameters((PreparedStatement) statement); 69 } 70 71 }
再看下RoutingStatementHandler,這個相當於一個StatementHandler的路由器,本身不實現任何功能,只是根據傳入的參數來選擇調用哪個類型的StatementHandler來處理,代碼如下:
1 public class RoutingStatementHandler implements StatementHandler { 2 3 private final StatementHandler delegate; 4 5 public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { 6 7 switch (ms.getStatementType()) { 8 case STATEMENT: 9 delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); 10 break; 11 case PREPARED: 12 delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); 13 break; 14 case CALLABLE: 15 delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); 16 break; 17 default: 18 throw new ExecutorException("Unknown statement type: " + ms.getStatementType()); 19 } 20 21 } 22 23 @Override 24 public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException { 25 return delegate.prepare(connection, transactionTimeout); 26 } 27 28 @Override 29 public void parameterize(Statement statement) throws SQLException { 30 delegate.parameterize(statement); 31 } 32 33 @Override 34 public void batch(Statement statement) throws SQLException { 35 delegate.batch(statement); 36 } 37 38 @Override 39 public int update(Statement statement) throws SQLException { 40 return delegate.update(statement); 41 } 42 43 @Override 44 public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { 45 return delegate.<E>query(statement, resultHandler); 46 } 47 48 @Override 49 public <E> Cursor<E> queryCursor(Statement statement) throws SQLException { 50 return delegate.queryCursor(statement); 51 } 52 53 @Override 54 public BoundSql getBoundSql() { 55 return delegate.getBoundSql(); 56 } 57 58 @Override 59 public ParameterHandler getParameterHandler() { 60 return delegate.getParameterHandler(); 61 } 62 }
可見RoutingStatementHandler的作用就是在構造的時候根據Statement類型來創建不同的處理器,然后調用對應的處理器來進行操作。而在StatementHandler在初始化的時候,都是通過RoutingStatementHandler來進行路由的,Configuration中的創建StatementHandler代碼如下:
1 public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { 2 StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); 3 statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); 4 return statementHandler; 5 }
再看看SimpleStatementHandler是如何執行sql語句的:
1 @Override 2 protected Statement instantiateStatement(Connection connection) throws SQLException { 3 if (mappedStatement.getResultSetType() != null) { 4 return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY); 5 } else { 6 return connection.createStatement(); 7 } 8 }
先通過Collection創建Statement對象
1 @Override 2 public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { 3 String sql = boundSql.getSql(); 4 statement.execute(sql); 5 return resultSetHandler.<E>handleResultSets(statement); 6 }
然后通過Statement對象執行sql語句,最后通過ResultSetHandler對象來處理執行后的結果。