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。歡迎交流。