首先約定文中將的四大對象是指:executor, statementHandler,parameterHandler,resultHandler對象。(爲了方便下面的文章說道四大對象就專指它們)java
講到statementHandler,毫無疑問它是咱們四大對象最重要的一個,它的任務就是和數據庫對話。在它這裏會使用parameterHandler和ResultHandler對象爲咱們綁定SQL參數和組裝最後的結果返回。sql
1、statementHandler對象的定義:數據庫
首先咱們先來看看statementHandler接口的定義:app
- public interface StatementHandler {
-
- Statement prepare(Connection connection)
- throws SQLException;
-
- 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();
-
- ParameterHandler getParameterHandler();
-
- }
這裏有幾個重要的方法,prepare,parameterize和query,update,他們的做用是不同的。ide
在MyBatis實現了statementHandler的有四個類:spa
RoutingStatementHandler,這是一個封裝類,它不提供具體的實現,只是根據Executor的類型,建立不一樣的類型StatementHandler。.net
SimpleStatementHandler,這個類對應於JDBC的Statement對象,用於沒有預編譯參數的SQL的運行。插件
PreparedStatementHandler 這個用於預編譯參數SQL的運行。對象
CallableStatementHandler 它將實存儲過程的調度。blog
在MyBatis中,Configuration對象會採用new RoutingStatementHandler()來生成StatementHandler對象,換句話說咱們真正使用的是RoutingStatementHandler對象,而後它會根據Executor的類型去建立對應具體的statementHandler對象(SimpleStatementHandler,PreparedStatementHandler和CallableStatementHandler)。
而後利用具體statementHandler的方法完成所須要的功能。那麼這個具體的statementHandler是保存在RoutingStatementHandler對象的delegate屬性的,因此當咱們攔截statementHandler的時候就要經常訪問它了。它們的關係以下圖所示。
2、prepare方法
首先prepare方法是用來編譯SQL的,讓咱們看看它的源碼實現。這裏咱們看到了BaseStatementHandler對prepare方法的實現,
- @Override
- public Statement prepare(Connection connection) throws SQLException {
- ErrorContext.instance().sql(boundSql.getSql());
- Statement statement = null;
- try {
- statement = instantiateStatement(connection);
- 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;
顯然咱們經過源碼更加關注抽象方法instantiateStatement是作了什麼事情。它依舊是一個抽象方法,那麼它就有其實現類。那就是以前說的那幾個具體的StatementHandler對象,讓咱們看看PreparedStatementHandler:
- @Override
- protected Statement instantiateStatement(Connection connection) throws SQLException {
- 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);
- }
- }
好這個方法很是簡單,咱們能夠看到它主要是根據上下文來預編譯SQL,這是咱們尚未設置參數。設置參數的任務是交由,statement接口的parameterize方法來實現的。
三、parameterize方法:
上面咱們在prepare方法裏面預編譯了SQL。那麼咱們這個時候但願設置參數。在Statement中咱們是使用parameterize方法進行設置參數的。
讓咱們看看PreparedStatementHandler中的parameterize方法:
- @Override
- public void parameterize(Statement statement) throws SQLException {
- parameterHandler.setParameters((PreparedStatement) statement);
- }
很顯然這裏很簡單是經過parameterHandler來實現的,咱們這篇文章只是停留在statementhandler的程度,等咱們講解parameterHandler的時候再來看它如何實現吧,期待一下吧。
四、query/update方法
咱們用了prepare方法預編譯了SQL,用了parameterize方法設置參數,那麼咱們接下來確定是想執行SQL,而SQL無非是兩種:
一種是進行查詢——query,另外就是更新——update。
這些方法都很簡單,讓咱們看看PreparedStatementHandler的實現:
- @Override
- public int update(Statement statement) throws SQLException {
- PreparedStatement ps = (PreparedStatement) statement;
- ps.execute();
- int rows = ps.getUpdateCount();
- Object parameterObject = boundSql.getParameterObject();
- KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
- keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
- return rows;
- }
-
- ......
- @Override
- public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
- PreparedStatement ps = (PreparedStatement) statement;
- ps.execute();
- return resultSetHandler.<E> handleResultSets(ps);
- }
咱們能夠看到若是是進行update的,它將會執行生成主鍵的操做(插入數據要自動生成主鍵的時候),而後就返回影響行數。
若是是進行query的就更加簡單了,它就是執行SQL語句,而後講結果使用resultHandler的handleResultSets去完成咱們的結果組裝。至於resultHandler的內部實現仍是很複雜的,值得期待哦。這裏咱們暫且不講等待下一章吧。
五、總結
StatementHandler是MyBatis四大對象裏面最重要的對象,它的方法是十分重要的,也是咱們插件的基礎。
當咱們須要改變sql的時候,顯然咱們要在預編譯SQL(prepare方法前加入修改的邏輯)。
當咱們須要修改參數的時候咱們能夠在調用parameterize方法前修改邏輯。或者使用ParameterHandler來改造設置參數。
咱們須要控制組裝結果集的時候,也能夠在query方法先後加入邏輯,或者使用ResultHandler來改造組裝結果。
懂的這些方法,才能理解我須要攔截什麼對象,如何處理插件,這是MyBatis的核心內容。