Mybatis源碼閱讀(四):核心接口4.1——StatementHandler

*************************************優雅的分割線 **********************************

分享一波:程序員賺外快-必看的巔峰干貨

如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程

請關注微信公眾號:HB荷包
在這里插入圖片描述
一個能讓你學習技術和賺錢方法的公眾號,持續更新
*************************************優雅的分割線 **********************************
前言

難得復工了,公司百業待興,有一大堆接口需要對接,忙的不行。回過神來發現自己快一個月沒寫博客了,趕緊抽時間寫一寫,不能斷更。

截止上一篇博客,我們已經把結果集映射的內容介紹完畢,接下來就是對Mybatis中的核心接口進行介紹,通過介紹這些核心接口,使讀者們更深刻地理解Mybatis的運行機制以及原理。
StatementHandler

StatementHandler接口是Mybatis的核心接口之一,它完成了Mybatis中最核心的工作,也是Executor接口實現的基礎。

StatementHandler接口中功能有很多,如創建Statement對象、執行SQL語句、批量執行SQL語句等。StatementHandler接口定義如下。

/**

  • Mybatis核心接口之一,完成了Mybatis中最核心的工作,也是Executor接口實現的基礎。

  • @author Clinton Begin
    */
    public interface StatementHandler {

    /**

    • 從數據庫連接中獲取一個Statement
    • @param connection
    • @param transactionTimeout
    • @return
    • @throws SQLException
      */
      Statement prepare(Connection connection, Integer transactionTimeout)
      throws SQLException;

    /**

    • 綁定statement執行時所需的實參
    • @param statement
    • @throws SQLException
      */
      void parameterize(Statement statement)
      throws SQLException;

    /**

    • 批量執行sql語句
    • @param statement
    • @throws SQLException
      */
      void batch(Statement statement)
      throws SQLException;

    /**

    • 執行 update/insert/delete語句
    • @param statement
    • @return
    • @throws SQLException
      */
      int update(Statement statement)
      throws SQLException;

    /**

    • 執行select語句
    • @param statement
    • @param resultHandler
    • @param
    • @return
    • @throws SQLException
      */
      List query(Statement statement, ResultHandler resultHandler)
      throws SQLException;

    /**

    • 查詢游標
    • @param statement
    • @param
    • @return
    • @throws SQLException
      */
      Cursor queryCursor(Statement statement)
      throws SQLException;

    /**

    • 獲取BoundSql
    • @return
      */
      BoundSql getBoundSql();

    /**

    • 獲取ParameterHandler
    • @return
      */
      ParameterHandler getParameterHandler();

}

該接口的繼承關系如下。其中,CallableStatementHandler用于調用存儲過程,而mysql的存儲過程在大多數公司都很少使用甚至禁止使用,這里就不對其進行介紹了,有興趣的朋友可以自己參考源碼閱讀

RoutingStatementHandler

RoutingStatementHandler使用策略模式,根據MappedStatement中指定的statementType字段,創建對應的StatementHandler接口實現。由于其這種思路,還有人認為這是個路由。RoutingStatementHandler核心代碼如下。

public class RoutingStatementHandler implements StatementHandler {

/*** 底層封裝的真正的StatementHandler對象* 這里的delegate在寫框架的過程中使用較多*/
private final StatementHandler delegate;public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {// RoutingStatementHandler的主要功能是根據MappedStatement的配置,生成對應的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());}}

}

BaseStatementHandler

BaseStatementHandler是一個抽象類,實現了StatementHandler接口。它只提供了一些參數綁定相關的方法,對數據庫不進行任何操作。該類的字段如下。

protected final Configuration configuration;
protected final ObjectFactory objectFactory;
protected final TypeHandlerRegistry typeHandlerRegistry;/*** 記錄結果集映射對象*/
protected final ResultSetHandler resultSetHandler;/*** 記錄使用的參數處理器對象。ParameterHandler的主要功能是為SQL綁定實參* 也就是使用傳入的實參替換SQL語句中的 ? 占位符*/
protected final ParameterHandler parameterHandler;protected final Executor executor;/*** 記錄SQL語句對應的MappedStatement*/
protected final MappedStatement mappedStatement;/*** 記錄用戶設置的offset和limit,用于在結果集中定位映射的起始位置和結束位置*/
protected final RowBounds rowBounds;protected BoundSql boundSql;

在BaseStatementHandler的構造方法中,除對上面的字段進行初始化之外,還會調用KeyGenerator.processBefore()方法初始化SQL的主鍵,具體實現如下。

protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {this.configuration = mappedStatement.getConfiguration();this.executor = executor;this.mappedStatement = mappedStatement;this.rowBounds = rowBounds;this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();this.objectFactory = configuration.getObjectFactory();if (boundSql == null) {// 調用KeyGenerator.processBefore 方法獲取主鍵generateKeys(parameterObject);boundSql = mappedStatement.getBoundSql(parameterObject);}this.boundSql = boundSql;this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
}protected void generateKeys(Object parameter) {KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();ErrorContext.instance().store();keyGenerator.processBefore(executor, mappedStatement, null, parameter);ErrorContext.instance().recall();
}

BaseStatementHandler實現了StatementHandler的prepare方法,該方法用來初始化Statement對象,然后為其分配超時時間等屬性。其中,初始化StatementHandler的方法是個抽象方法instantiateStatement,是一個抽象方法,需要由子類去實現,代碼如下。

@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {ErrorContext.instance().sql(boundSql.getSql());Statement statement = null;try {// 初始化statement對象,交給子類去實現statement = instantiateStatement(connection);// 設置超時時間setStatementTimeout(statement, transactionTimeout);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);}
}

BaseStatementHandler依賴兩個重要的組件,分別是ParameterHandler和ResultSetHandler。后者在前面的文章中已經介紹,不在重復。
ParameterHandler

通過前面介紹動態SQL可知,在BoundSql中記錄的SQL語句可能包含?占位符,每個?占位符都對應了BoundSql.parameterMapings集合中的一個元素。在ParameterHandler中只定義了一個setParameter方法,該方法用于為SQL語句綁定實參。ParameterHandler接口只有唯一一個實現類 DefaultParameterHandler,核心字段如下。

/*** 管理Mybatis中的全部TypeHandler對象*/
private final TypeHandlerRegistry typeHandlerRegistry;/*** SQL節點*/
private final MappedStatement mappedStatement;/*** 用戶傳入的實參對象*/
private final Object parameterObject;
private final BoundSql boundSql;
private final Configuration configuration;

DefaultParameterHandler的setParameters方法中會遍歷parameterMappings集合中記錄的ParameterMapping對象,并根據其中記錄的參數名稱找到對應的實參,再與SQL綁定。setParameters方法如下。

/*** 設置參數* @param ps*/
@Override
public void setParameters(PreparedStatement ps) {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);// OUT是存儲過程中的輸出參數,這里需要過濾掉這些參數if (parameterMapping.getMode() != ParameterMode.OUT) {Object value;// 獲取參數名稱String propertyName = parameterMapping.getProperty();// 獲取對應的實參值if (boundSql.hasAdditionalParameter(propertyName)) {value = boundSql.getAdditionalParameter(propertyName);} else if (parameterObject == null) {value = null;} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {// 實參可以直接通過TypeHandler轉換成jdbcTypevalue = parameterObject;} else {MetaObject metaObject = configuration.newMetaObject(parameterObject);value = metaObject.getValue(propertyName);}// 獲取parameterMapping中設置的TypeHandler對象TypeHandler typeHandler = parameterMapping.getTypeHandler();JdbcType jdbcType = parameterMapping.getJdbcType();if (value == null && jdbcType == null) {jdbcType = configuration.getJdbcTypeForNull();}try {// 為語句綁定實參typeHandler.setParameter(ps, i + 1, value, jdbcType);} catch (TypeException | SQLException e) {throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);}}}}
}

對SQL語句綁定完實參后,就可以調用Statement對象的execute方法執行SQL了。
SimpleStatementHandler

SimpleStatementHandler是BaseStatementHandler的子類,底層使用了Statement對象完成數據庫的相關操作,所以SQL語句中不能存在占位符,因此parameterize方法是空實現。

SimpleStatementHandler的instantiateStatement方法直接通過JDBC Connection創建Statement,代碼如下。

/*** 創建statement對象* @param connection* @return* @throws SQLException*/
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {return connection.createStatement();} else {return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);}
}

query方法完成了數據庫查詢的操作,并通過ResultSetHandler將結果集映射成結果對象,代碼如下。

@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {String sql = boundSql.getSql();statement.execute(sql);return resultSetHandler.handleResultSets(statement);
}

update方法負責執行insert、update、delete的SQL語句,并根據配置的KeyGenerator獲取數據庫生成的主鍵,具體實現如下。

/*** 負責執行insert、update、delete語句* @param statement* @return* @throws SQLException*/
@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 {statement.execute(sql);rows = statement.getUpdateCount();}return rows;
}

PreparedStatementHandler

該類底層依賴于PrepareStatement對象完成數據庫的操作,instantiateStatement方法直接調用Connection的prepareStatement方法創建PrepareStatement對象,代碼如下。

/*** 直接調用Connection的prepareStatement方法創建PrepareStatement對象* @param connection* @return* @throws SQLException*/
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {// 獲取待執行的sqlString sql = boundSql.getSql();// 根據keyGenerator的值創建PrepareStatement對象if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {String[] keyColumnNames = mappedStatement.getKeyColumns();if (keyColumnNames == null) {// 返回數據庫生成的主鍵return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);} else {// 在insert語句執行完成之后,將keyColumnNames指定的列返回return connection.prepareStatement(sql, keyColumnNames);}} else if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {// 設置結果集是否可以滾動以及游標是否可以上下移動,設置結果集是否可更新return connection.prepareStatement(sql);} else {// 創建普通的PrepareStatement對象return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);}
}

PrepareStatement的其他方法的實現與SimpleStatementHandler對應的方法實現類型,這里就不贅述了。

*************************************優雅的分割線 **********************************

分享一波:程序員賺外快-必看的巔峰干貨

如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程

請關注微信公眾號:HB荷包
在這里插入圖片描述
一個能讓你學習技術和賺錢方法的公眾號,持續更新
*************************************優雅的分割線 **********************************

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:
http://www.pswp.cn/news/536966.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/536966.shtml
英文地址,請注明出處:http://en.pswp.cn/news/536966.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Shell學習之結合正則表達式與通配符的使用(五)

Shell學習之結合正則表達式與通配符的使用 目錄 通配符 正則表達式與通配符通配符通配符的使用正則表達式 正則表達式正則表達式的使用通配符 正則表達式與通配符 正則表達式用來在文件中匹配符合條件的字符串&#xff0c;正則是包含匹配。grep、awk、sed等命令可以支持正則表達…

Mybatis源碼閱讀(四):核心接口4.2——Executor(上)

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

接收xml參數_SpringBoot實戰(二):接收xml請求

強烈推薦一個大神的人工智能的教程&#xff1a;http://www.captainbed.net/zhanghan【前言】最近在對接一個第三方系統&#xff0c;需要接收第三方系統的回調&#xff0c;而且格式為XML形式&#xff0c;之前自己一般接收的參數是Json形式&#xff0c;于是乎做個實驗驗證一下使用…

報錯 插入更新_window如何解決mysql數據量過大導致的報錯

window如何解決報錯“The total number of locks exceeds the lock table size”第一大步&#xff0c;查看mysql配置信息在CMD中輸入mysql -hlocalhost -uroot -p #如果設置了密碼直接接在p 后面 show variables like %storage_engine%以下為結果可以看到InnoDB是MySQL的默認引…

148. Sort List

Sort a linked list in O(n log n) time using constant space complexity. Example 1: Input: 4->2->1->3 Output: 1->2->3->4 Example 2: Input: -1->5->3->4->0 Output: -1->0->3->4->5難度&#xff1a;medium 題目&#xff1a;排…

Mybatis源碼閱讀(四):核心接口4.2——Executor(下)

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

python解橢圓方程的例題_橢圓標準方程典型例題及練習題

橢圓標準方程典型例題例1已知P 點在以坐標軸為對稱軸的橢圓上&#xff0c;點P 到兩焦點的距離分別為354和352&#xff0c;過P 點作焦點所在軸的垂線&#xff0c;它恰好過橢圓的一個焦點&#xff0c;求橢圓方程&#xff0e; 解&#xff1a;設兩焦點為1F 、2F &#xff0c;且3541…

leetcode393. UTF-8 Validation

題目要求 A character in UTF8 can be from 1 to 4 bytes long, subjected to the following rules:For 1-byte character, the first bit is a 0, followed by its unicode code. For n-bytes character, the first n-bits are all ones, the n1 bit is 0, followed by n-1 by…

Mybatis源碼閱讀(五 ):接口層——SqlSession

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

插入公式_一個小工具,徹底幫你搞定在Markdown中插入公式的問題

在編輯Markdown文檔時&#xff0c;插入公式是一個挺麻煩的活兒。需要掌握LaTex語法。我自己看完語法后&#xff0c;直接放棄&#xff0c;這絕對是反人類的語法。&#xff08;好吧&#xff0c;是我不會用...&#xff09;但是&#xff0c;我相信你看了這篇文章后&#xff0c;絕對…

JavaScript數據結構與算法——字典

1.字典數據結構 在字典中&#xff0c;存儲的是【鍵&#xff0c;值】對&#xff0c;其中鍵名是用來查詢特定元素的。字典和集合很相似&#xff0c;集合以【值&#xff0c;值】的形式存儲&#xff0c;字典則是用【鍵&#xff0c;值】對的形式存儲。字典也稱作映射。 2.創建字典 f…

Mybatis源碼閱讀(一):Mybatis初始化1.2 —— 解析別名、插件、對象工廠、反射工具箱、環境

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

中西方對時間的差異_中西方時間觀念差異 英文

The concept of time(時間觀念)①Inchina&#xff0c;words and phrases about time are very general. Forexample&#xff0c;ifyoudatewithsomeone,mostofChineseusedtoanswer: in the afternoon /at night/after a while and so on.Butinwestern,peoplehaveaverystrongconc…

Google 修改 Chrome API,防止隱身模式檢測

開發四年只會寫業務代碼&#xff0c;分布式高并發都不會還做程序員&#xff1f; 在使用 Chrome 瀏覽網頁時&#xff0c;某些網站會使用某種方法來確定訪問者是否處于隱身模式&#xff0c;這是一種隱私泄漏行為。Google 目前正在考慮修改 Chrome 的相關 API&#xff0c;來杜絕…

Mybatis源碼閱讀(一):Mybatis初始化1.1 解析properties、settings

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

亞馬遜推薦python_使用python查找amazon類別

我想得到amazon的類別&#xff0c;我計劃廢棄不用API。我已經取消了http://www.amazon.com&#xff0c;我已經在Shop By Department下拉列表中抓取了所有的類別和子類別&#xff0c;我創建了一個web服務來完成這項工作&#xff0c;代碼就在這里route(/hello)def hello():textli…

JavaScript異步基礎

唯一比不知道代碼為什么崩潰更可怕的事情是&#xff0c;不知道為什么一開始它是工作的&#xff01;在 ECMA 規范的最近幾次版本里不斷有新成員加入&#xff0c;尤其在處理異步的問題上&#xff0c;更是不斷推陳出新。然而&#xff0c;我們在享受便利的同時&#xff0c;也應該了…

Flutter、ReactNative、uniapp對比

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

JavaScript數組方法

一、基本類型和引用類型 數值、字符串、布爾值、undefined、null可以直接寫出來&#xff0c;比較簡單的數據稱為基本類型&#xff0c;在比較的時候&#xff0c;是直接按值比較。對象、函數、數組復雜的數據是引用類型&#xff0c;在比較的時候&#xff0c;是按照地址比較。cons…

nodejs mysql模塊_NodeJs使用Mysql模塊實現事務處理

依賴模塊&#xff1a;1. mysql&#xff1a;https://github.com/felixge/node-mysqlnpm install mysql --save2. async&#xff1a;https://github.com/caolan/asyncnpm install async --save(ps: async模塊可換成其它Promise模塊如bluebird、q等)因為Node.js的mysql模塊本身對于…