mybatis 源碼分析(六)StatementHandler 主體結構分析
- 2019 年 10 月 3 日
- 筆記
分析到這裡的時候,mybatis 初始化、介面、事務、快取等主要功能都已經講完了,現在就還剩下 StatementHandler 這個真正幹活的傢伙沒有分析了;所以接下來的部落格內容主要和資料庫的關係比較密切,而 StatementHandler 的主要流程也基本是和 JDBC 的流程是一一對應的;
一、StatementHandler 執行流程
在 mybatis 系列文章的第一篇,我放了一張 mybatis 整體的執行流程圖:
從上面的圖中也能比較清楚的看到 StatementHandler 的職責:獲取 Statement -> 設置參數 -> 查詢資料庫 -> 將查詢結果映射為 JavaBean,從這裡也能看到是和我們使用原生 JDBC 的流程是一樣的;而整個過程 StatementHandler 又將其拆分成了部分:
- KeyGenerator:主鍵設置
- ParameterHandler:參數設置
- ResultSetHandler:結果集設置
這裡我們首先介紹 StatementHandler 的類結構:
- RoutingStatementHandler:路由處理器,這個相當於一個靜態代理,根據 MappedStatement.statementType 創建對應的對處理器;
- SimpleStatementHandler:不需要預編譯的簡單處理器;
- PreparedStatementHandler:預編譯的 SQL 處理器;
- CallableStatementHandler:主要用於存儲過程的調度;
這裡的 SimpleStatementHandler、PreparedStatementHandler、CallableStatementHandler 同時也和 JDBC 的三個 Statement 一一對應;首先我們還是先看一下介面方法:
public interface StatementHandler { Statement prepare(Connection connection) throws SQLException; // 獲取 Statement void parameterize(Statement statement) throws SQLException; // 參數化 void batch(Statement statement) throws SQLException; // 批處理 int update(Statement statement) throws SQLException; <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException; BoundSql getBoundSql(); // 獲取綁定sql ParameterHandler getParameterHandler(); // 得到參數處理器 }
其中公共的方法都封裝到了 BaseStatementHandler 中(這裡使用的是模版模式);
// 獲取 Statement @Override public Statement prepare(Connection connection) throws SQLException { ErrorContext.instance().sql(boundSql.getSql()); Statement statement = null; try { statement = instantiateStatement(connection); // 實例化 Statement setStatementTimeout(statement); // 設置超時 setFetchSize(statement); // 設置讀取條數 return statement; } catch (SQLException e) { closeStatement(statement); throw e; } catch (Exception e) { closeStatement(statement); throw new ExecutorException("Error preparing statement. Cause: " + e, e); } } // 由子類提供不同的實例化 protected abstract Statement instantiateStatement(Connection connection) throws SQLException;
二、StatementHandler 子類
1. RoutingStatementHandler
靜態代理模式,根據 MappedStatement.statementType 創建對應的對處理,默認是 PREPARED,可以使用 XML 配置或者註解的方式指定;
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { // 創建路由選擇語句處理器 StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); // 插件攔截 statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); return statementHandler; }
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { switch (ms.getStatementType()) { // 根據語句類型,委派到不同的語句處理器(STATEMENT|PREPARED|CALLABLE) case STATEMENT: delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); break; case PREPARED: delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); break; case CALLABLE: delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); break; default: throw new ExecutorException("Unknown statement type: " + ms.getStatementType()); } }
2. SimpleStatementHandler
其功能和 JDBC.Statement 對應,從初始化方法也可以看到使用的是簡單 Statement;
@Override protected Statement instantiateStatement(Connection connection) throws SQLException { // 調用 Connection.createStatement if (mappedStatement.getResultSetType() != null) { return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY); } else { return connection.createStatement(); } }
@Override public int update(Statement statement) throws SQLException { String sql = boundSql.getSql(); Object parameterObject = boundSql.getParameterObject(); KeyGenerator keyGenerator = mappedStatement.getKeyGenerator(); int rows; if (keyGenerator instanceof Jdbc3KeyGenerator) { statement.execute(sql, Statement.RETURN_GENERATED_KEYS); rows = statement.getUpdateCount(); keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject); } else if (keyGenerator instanceof SelectKeyGenerator) { statement.execute(sql); rows = statement.getUpdateCount(); keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject); } else { //如果沒有keyGenerator,直接調用Statement.execute和Statement.getUpdateCount statement.execute(sql); rows = statement.getUpdateCount(); } return rows; }
這裡在更新的時候需要區分 KeyGenerator,因為使用的是簡單 Statement,所以需要在查詢的時候指定返回主鍵 statement.execute(sql, Statement.RETURN_GENERATED_KEYS);
;
3. PreparedStatementHandler
其功能和 PreparedStatement 對應,這裡初始化的時候可以看到比 SimpleStatementHandler 要複雜一些,因為 PreparedStatement 更新返回主鍵有三個方法,詳細分析後面會單獨放一篇詳細講解;
@Override protected Statement instantiateStatement(Connection connection) throws SQLException { //調用Connection.prepareStatement String sql = boundSql.getSql(); if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) { String[] keyColumnNames = mappedStatement.getKeyColumns(); if (keyColumnNames == null) { return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS); } else { return connection.prepareStatement(sql, keyColumnNames); } } else if (mappedStatement.getResultSetType() != null) { return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY); } else { return connection.prepareStatement(sql); } }
還有一點不同的就是 PreparedStatement 需要參數化設置,就是設置預編譯 SQL 對應佔位符的參數;
// DefaultParameterHandler @Override public void setParameters(PreparedStatement ps) throws SQLException { ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId()); List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); if (parameterMappings != null) { // 循環設參數 for (int i = 0; i < parameterMappings.size(); i++) { ParameterMapping parameterMapping = parameterMappings.get(i); if (parameterMapping.getMode() != ParameterMode.OUT) { // 如果不是OUT,才設進去 Object value; String propertyName = parameterMapping.getProperty(); if (boundSql.hasAdditionalParameter(propertyName)) { // 若有額外的參數, 設為額外的參數 value = boundSql.getAdditionalParameter(propertyName); } else if (parameterObject == null) { // 若參數為null,直接設null value = null; } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { // 若參數有相應的TypeHandler,直接設object value = parameterObject; } else { // 除此以外,MetaObject.getValue反射取得值設進去 MetaObject metaObject = configuration.newMetaObject(parameterObject); value = metaObject.getValue(propertyName); } TypeHandler typeHandler = parameterMapping.getTypeHandler(); JdbcType jdbcType = parameterMapping.getJdbcType(); if (value == null && jdbcType == null) { // 不同類型的set方法不同,所以委派給子類的setParameter方法 jdbcType = configuration.getJdbcTypeForNull(); } typeHandler.setParameter(ps, i + 1, value, jdbcType); } } } }
另外還有 CallableStatementHandler ,程式碼也很簡單,這裡就不詳細分析了;