日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Mybatis的SqlSession运行原理

發(fā)布時間:2024/4/17 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Mybatis的SqlSession运行原理 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前言

  SqlSession是Mybatis最重要的構(gòu)建之一,可以簡單的認為Mybatis一系列的配置目的是生成類似 JDBC生成的Connection對象的SqlSession對象,這樣才能與數(shù)據(jù)庫開啟“溝通”,通過SqlSession可以實現(xiàn)增刪改查(當然現(xiàn)在更加推薦是使用Mapper接口形式),那么它是如何執(zhí)行實現(xiàn)的,這就是本篇博文所介紹的東西,其中會涉及到簡單的源碼講解。

  了解SqlSession的運作原理是學(xué)習(xí)Mybatis插件的必經(jīng)之路,因為Mybatis的插件會在SqlSession運行過程中“插入”運行,如果沒有很好理解的話,Mybatis插件可能會覆蓋相應(yīng)的源碼造成嚴重的問題。鑒于此,本篇博文盡量詳細介紹SqlSession運作原理!

  建議:在我之前的博文《Mybatis緩存(1)--------系統(tǒng)緩存及簡單配置介紹》中介紹到SqlSession的產(chǎn)生過程,可以先理解后再讀此博文可能會更加好理解!

  注:本篇博文也是我最近真正理解Mybatis才開始編寫的,可能有些地方不太準確,如果有錯誤之處敬請指出,另外創(chuàng)作不易,望轉(zhuǎn)載告之,謝謝!

  參數(shù)資料:《深入淺出Mybatis基礎(chǔ)原理與實踐》(我這里只有電子版PDF,需要的朋友可以聯(lián)系我)

?


?

1、SqlSession簡單介紹

  (1)SqlSession簡單原理介紹

  SqlSession提供select/insert/update/delete方法,在舊版本中使用使用SqlSession接口的這些方法,但是新版的Mybatis中就會建議使用Mapper接口的方法。

  映射器其實就是一個動態(tài)代理對象,進入到MapperMethod的execute方法就能簡單找到SqlSession的刪除、更新、查詢、選擇方法,從底層實現(xiàn)來說:通過動態(tài)代理技術(shù),讓接口跑起來,之后采用命令模式,最后還是采用了SqlSession的接口方法(getMapper()方法等到Mapper)執(zhí)行SQL查詢(也就是說Mapper接口方法的實現(xiàn)底層還是采用SqlSession接口方法實現(xiàn)的)。

  注:以上雖然只是簡單的描述,但實際上源碼相對復(fù)雜,下面將結(jié)合源碼進行簡單的介紹!

  (2)SqlSession重要的四個對象

    1)Execute:調(diào)度執(zhí)行StatementHandler、ParmmeterHandler、ResultHandler執(zhí)行相應(yīng)的SQL語句;

    2)StatementHandler:使用數(shù)據(jù)庫中Statement(PrepareStatement)執(zhí)行操作,即底層是封裝好了的prepareStatement;

    3)ParammeterHandler:處理SQL參數(shù);

    4)ResultHandler:結(jié)果集ResultSet封裝處理返回。

2、SqlSession四大對象

(1)Execute執(zhí)行器:

  執(zhí)行器起到至關(guān)重要的作用,它是真正執(zhí)行Java與數(shù)據(jù)庫交互的東西,參與了整個SQL查詢執(zhí)行過程中。

1)主要有三種執(zhí)行器:簡易執(zhí)行器SIMPLE(不配置就是默認執(zhí)行器REUSE是一種重用預(yù)處理語句、BATCH批量更新、批量專用處理器

package org.apache.ibatis.session;/*** @author Clinton Begin*/ public enum ExecutorType {SIMPLE, REUSE, BATCH }

2)執(zhí)行器作用:Executor會先調(diào)用StatementHandlerprepare()方法預(yù)編譯SQL語句,同時設(shè)置一些基本的運行參數(shù),然后調(diào)用StatementHandler的parameterize()方法(實際上是啟用了ParameterHandler設(shè)置參數(shù))設(shè)置參數(shù),resultHandler再組裝查詢結(jié)果返回調(diào)用者完成一次查詢完成預(yù)編譯,簡單總結(jié)起來就是即先預(yù)編譯SQL語句,之后設(shè)置參數(shù)(跟JDBC的prepareStatement過程類似)最后如果有查詢結(jié)果就會組裝返回。

首先,以SimpleExecutor為例,查看源碼我們得到如下幾點重要知識點:

第一:Executor通過Configuration對象中newExecutor()方法中選擇相應(yīng)的執(zhí)行器生成

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {executorType = executorType == null ? defaultExecutorType : executorType;executorType = executorType == null ? ExecutorType.SIMPLE : executorType;Executor executor;if (ExecutorType.BATCH == executorType) {executor = new BatchExecutor(this, transaction);} else if (ExecutorType.REUSE == executorType) {executor = new ReuseExecutor(this, transaction);} else {executor = new SimpleExecutor(this, transaction);}if (cacheEnabled) {executor = new CachingExecutor(executor);}executor = (Executor) interceptorChain.pluginAll(executor);return executor;}

(注:最后interceptorChain.pluginAll()中執(zhí)行層層動態(tài)代理,最后在可以在調(diào)用真正的Executor前可以修改插件代碼,這也就是為什么學(xué)會Mybatis的插件必須要知道SqlSession的運行過程)

第二:在執(zhí)行器中StatementHandler是根據(jù)Configuration構(gòu)建的

public SimpleExecutor(Configuration configuration, Transaction transaction) {super(configuration, transaction);}@Overridepublic int doUpdate(MappedStatement ms, Object parameter) throws SQLException {Statement stmt = null;try {Configuration configuration = ms.getConfiguration();StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);stmt = prepareStatement(handler, ms.getStatementLog());return handler.update(stmt);} finally {closeStatement(stmt);}}

第三:Executor會執(zhí)行StatementHandler的prepare()方法進行預(yù)編譯---->填入connection對象等參數(shù)---->再調(diào)用parameterize()方法設(shè)置參數(shù)---->完成預(yù)編譯

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {Statement stmt;Connection connection = getConnection(statementLog);stmt = handler.prepare(connection, transaction.getTimeout());handler.parameterize(stmt);return stmt;}

  總結(jié)以上繪制簡單思維圖如下:

      

?

?

(2)StatementHanlder數(shù)據(jù)庫會話器

      1)作用:簡單來說就是專門處理數(shù)據(jù)庫會話。詳細來說就是進行預(yù)編譯并且調(diào)用ParameterHandler的setParameters()方法設(shè)置參數(shù)

      2)數(shù)據(jù)庫會話器主要有三種:SimpleStatementHandler、PrepareStatementHandler、CallableStatementHandler,分別對應(yīng)Executor的三種執(zhí)行器(SIMPLE、REUSE、BATCH)

      我們從上述Executor的prepareStatement()方法中調(diào)用了StatementHandler的parameterize()開始一步步地查看源碼,如下得到幾點重要的知識點:

      第一:StatementHandler的生成是由Configuration方法中newStatementHandler()方法生成的,但是正在創(chuàng)建的是實現(xiàn)了StatementHandler接口的RoutingStatementHandler對象

public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject,
                               RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {StatementHandler statementHandler
= new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);return statementHandler;}

?      第二:RoutingStatementHandler的通過適配器模式找到對應(yīng)(根據(jù)上下文)的StatementHandler執(zhí)行的,并且有SimpleStatementHandler、PrepareStatementHandler、CallableStatementHandler,分別對應(yīng)Executor的三種執(zhí)行器(SIMPLE、REUSE、BATCH)

public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {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());}

?      之后主要以PrepareStatementHandler為例,我們觀察到:它是實現(xiàn)BaseStatementHandler接口的,最后BaseStatementHandler又是實現(xiàn)StatementHandler接口的

public class PreparedStatementHandler extends BaseStatementHandler ...... public abstract class BaseStatementHandler implements StatementHandler

      它主要有三種方法:prepareparameterizequery我們查看源碼:

      第三:在BaseStatementHandler中重寫prepare()方法,instantiateStatement()方法完成預(yù)編譯,之后設(shè)置一些基礎(chǔ)配置(獲取最大行數(shù),超時)

@Overridepublic Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {ErrorContext.instance().sql(boundSql.getSql());Statement statement = null;try {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);}}

      第四:instantiateStatement()預(yù)編譯實際上也是使用了JDBC的prepareStatement()完成預(yù)編譯

@Overrideprotected 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);}}

      第五:在prepareStatement中重寫parameterize()方法。prepare()預(yù)編譯完成之后,Executor會調(diào)用parameterize()方法(在上面的Executor部分中已經(jīng)做了介紹),實際上是調(diào)用ParameterHandler的setParameters()方法

@Overridepublic void parameterize(Statement statement) throws SQLException {parameterHandler.setParameters((PreparedStatement) statement);}

  (3)ParameterHandler參數(shù)處理器

      作用:對預(yù)編譯中參數(shù)進行設(shè)置,如果有配置typeHandler,自然會對注冊的typeHandler對參數(shù)進行處理

      查看并學(xué)習(xí)源碼,得到以下幾點重要知識點:

      第一:Mybatis提供了ParamterHandler的默認實現(xiàn)類DefalutParameterHandler

      

public interface ParameterHandler {Object getParameterObject();void setParameters(PreparedStatement ps)throws SQLException;}

      (其中:getParameterObject是返回參數(shù)對象,setParameters()是設(shè)置預(yù)編譯參數(shù))

      第二:從parameterObject中取到參數(shù),然后使用typeHandler(注冊在Configuration中)進行參數(shù)處理:

@Overridepublic 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);if (parameterMapping.getMode() != ParameterMode.OUT) {Object value;String propertyName = parameterMapping.getProperty();if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params value = boundSql.getAdditionalParameter(propertyName);} else if (parameterObject == null) {value = null;} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {value = parameterObject;} else {MetaObject metaObject = configuration.newMetaObject(parameterObject);value = metaObject.getValue(propertyName);} 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 e) {throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);} catch (SQLException e) {throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);}}}}}

  (4)ResultSetHandler結(jié)果集處理器

      作用:很簡單,就是組裝結(jié)果返回結(jié)果集

    第一:ResultSetHandler接口,handlerResultSets()是包裝并返回結(jié)果集的,handleOutputParameters()是處理存儲過程輸出參數(shù)的

public interface ResultSetHandler {<E> List<E> handleResultSets(Statement stmt) throws SQLException;<E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;void handleOutputParameters(CallableStatement cs) throws SQLException;

    第二:Mybatis提供了默認的ResultSetHandler實現(xiàn)類DefaultResultSetHandler,其中重點是handlerResultSets()的實現(xiàn),但是其實現(xiàn)過程比較復(fù)雜,這里不過多介紹(emmmmm....個人目前能力還達理解,仍需努力)

    

    第三:在Executor中doQuery()方法返回了封裝的結(jié)果集

@Overridepublic <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {Statement stmt = null;try {Configuration configuration = ms.getConfiguration();StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);stmt = prepareStatement(handler, ms.getStatementLog());return handler.<E>query(stmt, resultHandler);} finally {closeStatement(stmt);}}

    第四:實際上是返回結(jié)果是調(diào)用了resultSetHandler的handleResultSets()方法

@Overridepublic <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {PreparedStatement ps = (PreparedStatement) statement;ps.execute();return resultSetHandler.<E> handleResultSets(ps);}

3、SqlSession運行總結(jié)

(1)文字總結(jié)

 SqlSession的運行主要是依靠Executor執(zhí)行器調(diào)用(調(diào)度)StatementHandler、parameterHanlder、ResultSetHandler,Executor首先通過創(chuàng)建StamentHandler執(zhí)行預(yù)編譯并設(shè)置參數(shù)運行,而整個過程需要如下幾步才能完成:

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {Statement stmt;Connection connection = getConnection(statementLog);stmt = handler.prepare(connection, transaction.getTimeout());handler.parameterize(stmt);return stmt;}

  1)prepare預(yù)編譯SQL

    由適配模式生成的RoutingStatementHandler根據(jù)上下文選擇生成三種相應(yīng)的XXXStatementHandler;

    在生成的XXXStatementHandler內(nèi)部instantiateStatement()方法執(zhí)行底層JDBC的prepareStatement()方法完成預(yù)編譯

  2)parameterize設(shè)置參數(shù)

    默認是DefaultParameterHandler(實現(xiàn)了parameterHandler接口)中setParameter()方法完成參數(shù)配置,其中參數(shù)從ParameterObject中取出,交給typeHandler處理

  3)doUpdate/doQuery執(zhí)行SQL

    返回的結(jié)果通過默認的DefaultResultSetHandler(實現(xiàn)了ResultSetHandler接口)封裝

(2)運行圖總結(jié)

?      1)SqlSession內(nèi)部總運行圖

                                       

       2)prepare()方法運行圖:                   ????           3)parameterize()方法運行圖 ??????????????????

         

?

轉(zhuǎn)載于:https://www.cnblogs.com/jian0110/p/9452592.html

總結(jié)

以上是生活随笔為你收集整理的Mybatis的SqlSession运行原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。