1. 程式人生 > >Mybatis原始碼分析--StatementHandler原始碼分析

Mybatis原始碼分析--StatementHandler原始碼分析

1 概述

在上一篇文章(Mybatis原始碼分析--Executor原始碼分析),我們分析Executor原始碼的時候就提到了StatementHandler的作用是和資料庫對話。當然StatementHandler和資料庫對話是依賴於Statement來完成的。在這裡隨便說一下ParameterHandler和ResultHandler的作用分別是繫結SQL引數和組裝最後的結果返回。

2 生成StatementHandler

StatementHandler物件的生成是通過呼叫Configuration的newStatementHandler函式來完成的。

public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    
	//呼叫RoutingStatementHandler的建構函式來生成StatementHandler
	StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }

從上面的原始碼我們可想而知,針對StatementHandler物件的生成完全交給了RoutingStatementHandler的建構函式。至於RoutingStatementHandler和StatementHandler的關係,我們下面先來看一看StatementHandler的UML類圖。

3 StatementHandler的結構

首先我們直接來看一下StatementHandler的UML類圖。

我們再來看一下RoutingStatementHandler的原始碼。

public class RoutingStatementHandler implements StatementHandler {

  private final StatementHandler delegate;

  public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {

    //通過Statement的型別來構造對應的StatementHandler
    switch (ms.getStatementType()) {
      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());
    }

  }

  @Override
  public Statement prepare(Connection connection) throws SQLException {
    return delegate.prepare(connection);
  }

  @Override
  public void parameterize(Statement statement) throws SQLException {
    delegate.parameterize(statement);
  }

  @Override
  public void batch(Statement statement) throws SQLException {
    delegate.batch(statement);
  }

  @Override
  public int update(Statement statement) throws SQLException {
    return delegate.update(statement);
  }

  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    return delegate.<E>query(statement, resultHandler);
  }

  @Override
  public BoundSql getBoundSql() {
    return delegate.getBoundSql();
  }

  @Override
  public ParameterHandler getParameterHandler() {
    return delegate.getParameterHandler();
  }
}

檢視上面的原始碼,我們可以發現RoutingStatementHandler根據不同的Statement型別持有相應的StatementHandler,RoutingStatementHandler的所有函式都是依賴於具體的StatementHandler的具體函式來實現的。這裡相當於一個什麼設計模式呢?相當於一個策略模式,而RoutingStatementHandler就相當於一個策略容器,StatemetnHandler是抽象策略類,而BaseStatementHandler的子類SimpleStatementHandler、PreparedStatementHandler和CallableStatementHandler就是具體策略類。

接下來,我們來看一下StatemetnHandler介面的各個實現類的具體作用。

(1)RoutingStatementHandler:這是一個封裝類,不提供任何具體的實現,只是根據不同的Executor來建立不同的StatementHandler,然後依賴於具體的statementHandler來實現不同的方法。其實這就相當於一個策略模式的策略容器。 (2)SimpleStatementHandler:這個類對應於JDBC的Statement物件,用於沒有預編譯的SQL的執行。 (3)PreparedStatementHandler :這個用於預編譯引數的SQL執行。 (4)CallableStatementHandler :用於儲存過程的呼叫。

接下來我們來看一看StatementHandler各個函式的作用及部分函式的邏輯。

4 BaseStatementHandler

BaseStatementHandler裡面實現的函式其實就是做好準備工作,我們來看prepare函式的邏輯。

    /**
     * 傳入Connection,建立並初始化Statement
     */
    @Override
    public Statement prepare(Connection connection) throws SQLException {
        ErrorContext.instance().sql(boundSql.getSql());
        Statement statement = null;
        try {

            //初始化statement		
            statement = instantiateStatement(connection);

            //設定查詢超時時間
            setStatementTimeout(statement);

            //設定每次允許傳遞到資料庫的查詢語句條數
            setFetchSize(statement);
            return statement;
        } catch (SQLException e) {

            //如果發生異常則關閉Statement		
            closeStatement(statement);
            throw e;
        } catch (Exception e) {

            //如果發生異常則關閉Statement	
            closeStatement(statement);
            throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
        }
    }

針對具體的StatemetnHandler我們來看一下SimpleStatementHandler的實現就行,至於其餘的StatementHandler函式的實現其實就是呼叫不同的Statement(StatementImpl、PreparedStatement、CallableStatement)來實現不同型別的資料庫操作而已。

5 SimpleStatementHandler

(1)update函式

    @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) {

            //指定sql語句	
            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 {
            statement.execute(sql);
            rows = statement.getUpdateCount();
        }
        return rows;
    }

(2)batch函式

@Override
public void batch(Statement statement) throws SQLException {
    String sql = boundSql.getSql();
	
    //執行statement的批量新增
    statement.addBatch(sql);
}

(3)query函式

@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    String sql = boundSql.getSql();
    statement.execute(sql);
	
    //這裡需要使用到ResultHandler來處理查詢結果
    return resultSetHandler.<E>handleResultSets(statement);
}

(4)instantiateStatement函式

@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
	
    //使用Connection的createStatement函式建立Statement物件
    if (mappedStatement.getResultSetType() != null) {
        return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    } else {
        return connection.createStatement();
    }
}

(5)parameterize函式

//引數預處理,只有PrepareStatementHandler類才有這個函式的具體實現
@Override
public void parameterize(Statement statement) throws SQLException {
    // N/A
}

上面的類容就是我們對StatementHandler的分析,接下來我們將要繼續分析Mybatis繫結引數和處理執行結果的Handler,ParameterHandler和ResultHandler。歡迎交流。