MyBatis原理第四篇——statementHandler對象(sqlSession內部核心實現,插件的基礎)

首先約定文中將的四大對象是指:executor, statementHandler,parameterHandler,resultHandler對象。(爲了方便下面的文章說道四大對象就專指它們)java

講到statementHandler,毫無疑問它是咱們四大對象最重要的一個,它的任務就是和數據庫對話。在它這裏會使用parameterHandler和ResultHandler對象爲咱們綁定SQL參數和組裝最後的結果返回。sql

1、statementHandler對象的定義:數據庫

首先咱們先來看看statementHandler接口的定義:app

 

[java]  view plain  copy
 
  1. public interface StatementHandler {  
  2.   
  3.   Statement prepare(Connection connection)  
  4.       throws SQLException;  
  5.   
  6.   void parameterize(Statement statement)  
  7.       throws SQLException;  
  8.   
  9.   void batch(Statement statement)  
  10.       throws SQLException;  
  11.   
  12.   int update(Statement statement)  
  13.       throws SQLException;  
  14.   
  15.   <E> List<E> query(Statement statement, ResultHandler resultHandler)  
  16.       throws SQLException;  
  17.   
  18.   BoundSql getBoundSql();  
  19.   
  20.   ParameterHandler getParameterHandler();  
  21.   
  22. }  

 

這裏有幾個重要的方法,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方法的實現,

 

[java]  view plain  copy
 
  1. @Override  
  2.  public Statement prepare(Connection connection) throws SQLException {  
  3.    ErrorContext.instance().sql(boundSql.getSql());  
  4.    Statement statement = null;  
  5.    try {  
  6.      statement = instantiateStatement(connection);  
  7.      setStatementTimeout(statement);  
  8.      setFetchSize(statement);  
  9.      return statement;  
  10.    } catch (SQLException e) {  
  11.      closeStatement(statement);  
  12.      throw e;  
  13.    } catch (Exception e) {  
  14.      closeStatement(statement);  
  15.      throw new ExecutorException("Error preparing statement.  Cause: " + e, e);  
  16.    }  
  17.  }  
  18.   
  19.  protected abstract Statement instantiateStatement(Connection connection) throws SQLException;  


顯然咱們經過源碼更加關注抽象方法instantiateStatement是作了什麼事情。它依舊是一個抽象方法,那麼它就有其實現類。那就是以前說的那幾個具體的StatementHandler對象,讓咱們看看PreparedStatementHandler:

 

 

[java]  view plain  copy
 
  1. @Override  
  2.   protected Statement instantiateStatement(Connection connection) throws SQLException {  
  3.     String sql = boundSql.getSql();  
  4.     if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {  
  5.       String[] keyColumnNames = mappedStatement.getKeyColumns();  
  6.       if (keyColumnNames == null) {  
  7.         return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);  
  8.       } else {  
  9.         return connection.prepareStatement(sql, keyColumnNames);  
  10.       }  
  11.     } else if (mappedStatement.getResultSetType() != null) {  
  12.       return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);  
  13.     } else {  
  14.       return connection.prepareStatement(sql);  
  15.     }  
  16.   }  

好這個方法很是簡單,咱們能夠看到它主要是根據上下文來預編譯SQL,這是咱們尚未設置參數。設置參數的任務是交由,statement接口的parameterize方法來實現的。

 

 

 

三、parameterize方法:

上面咱們在prepare方法裏面預編譯了SQL。那麼咱們這個時候但願設置參數。在Statement中咱們是使用parameterize方法進行設置參數的。

讓咱們看看PreparedStatementHandler中的parameterize方法:

 

[java]  view plain  copy
 
  1. @Override  
  2.   public void parameterize(Statement statement) throws SQLException {  
  3.     parameterHandler.setParameters((PreparedStatement) statement);  
  4.   }  

很顯然這裏很簡單是經過parameterHandler來實現的,咱們這篇文章只是停留在statementhandler的程度,等咱們講解parameterHandler的時候再來看它如何實現吧,期待一下吧。

 

 

四、query/update方法

咱們用了prepare方法預編譯了SQL,用了parameterize方法設置參數,那麼咱們接下來確定是想執行SQL,而SQL無非是兩種:

一種是進行查詢——query,另外就是更新——update。

這些方法都很簡單,讓咱們看看PreparedStatementHandler的實現:

 

[java]  view plain  copy
 
  1. @Override  
  2.   public int update(Statement statement) throws SQLException {  
  3.     PreparedStatement ps = (PreparedStatement) statement;  
  4.     ps.execute();  
  5.     int rows = ps.getUpdateCount();  
  6.     Object parameterObject = boundSql.getParameterObject();  
  7.     KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();  
  8.     keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);  
  9.     return rows;  
  10.   }  
  11.   
  12. ......  
  13. @Override  
  14.   public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {  
  15.     PreparedStatement ps = (PreparedStatement) statement;  
  16.     ps.execute();  
  17.     return resultSetHandler.<E> handleResultSets(ps);  
  18.   }  


咱們能夠看到若是是進行update的,它將會執行生成主鍵的操做(插入數據要自動生成主鍵的時候),而後就返回影響行數。

 

若是是進行query的就更加簡單了,它就是執行SQL語句,而後講結果使用resultHandler的handleResultSets去完成咱們的結果組裝。至於resultHandler的內部實現仍是很複雜的,值得期待哦。這裏咱們暫且不講等待下一章吧。

 

五、總結

 

StatementHandler是MyBatis四大對象裏面最重要的對象,它的方法是十分重要的,也是咱們插件的基礎。

 

當咱們須要改變sql的時候,顯然咱們要在預編譯SQL(prepare方法前加入修改的邏輯)。

當咱們須要修改參數的時候咱們能夠在調用parameterize方法前修改邏輯。或者使用ParameterHandler來改造設置參數。

咱們須要控制組裝結果集的時候,也能夠在query方法先後加入邏輯,或者使用ResultHandler來改造組裝結果。

懂的這些方法,才能理解我須要攔截什麼對象,如何處理插件,這是MyBatis的核心內容。

相關文章
相關標籤/搜索