91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

一文讓你秒懂Mybatis的SqlSession運行原理

發布時間:2020-07-24 20:04:36 來源:網絡 閱讀:222 作者:沙漏半杯 欄目:編程語言

前言

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

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

1、SqlSession簡單介紹

(1)SqlSession簡單原理介紹

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

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

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

(2)SqlSession重要的四個對象

 1)Execute:調度執行StatementHandler、ParmmeterHandler、ResultHandler執行相應的SQL語句;

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

3)ParammeterHandler:處理SQL參數;

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

2、SqlSession四大對象

(1)Execute執行器:

  執行器起到至關重要的作用,它是真正執行Java與數據庫交互的東西,參與了整個SQL查詢執行過程中。

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

package org.apache.ibatis.session;

/**

* @author Clinton Begin

*/

public enum ExecutorType {

SIMPLE, REUSE, BATCH

}

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

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

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

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()中執行層層動態代理,最后在可以在調用真正的Executor前可以修改插件代碼,這也就是為什么學會Mybatis的插件必須要知道SqlSession的運行過程)

第二:在執行器中StatementHandler是根據Configuration構建的

public SimpleExecutor(Configuration configuration, Transaction transaction) {

super(configuration, transaction);

}

@Override

public 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會執行StatementHandler的prepare()方法進行預編譯---->填入connection對象等參數---->再調用parameterize()方法設置參數---->完成預編譯

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;

}

總結以上繪制簡單思維圖如下:

一文讓你秒懂Mybatis的SqlSession運行原理


(2)StatementHanlder數據庫會話器

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

2)數據庫會話器主要有三種:SimpleStatementHandler、PrepareStatementHandler、CallableStatementHandler,分別對應Executor的三種執行器(SIMPLE、REUSE、BATCH)

我們從上述Executor的prepareStatement()方法中調用了StatementHandler的parameterize()開始一步步地查看源碼,如下得到幾點重要的知識點:小編整理了一套java架構資料和BAT面試題,加659270626領取,限前20名,先到先得。

第一:StatementHandler的生成是由Configuration方法中newStatementHandler()方法生成的,但是正在創建的是實現了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的通過適配器模式找到對應(根據上下文)的StatementHandler執行的,并且有SimpleStatementHandler、PrepareStatementHandler、CallableStatementHandler,分別對應Executor的三種執行器(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為例,我們觀察到:它是實現BaseStatementHandler接口的,最后BaseStatementHandler又是實現StatementHandler接口的

public class PreparedStatementHandler extends BaseStatementHandler

......

public abstract class BaseStatementHandler implements StatementHandler

它主要有三種方法:prepare、parameterize和query,我們查看源碼:

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

@Override

public 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()預編譯實際上也是使用了JDBC的prepareStatement()完成預編譯

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

}

}

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

@Override

public void parameterize(Statement statement) throws SQLException {

parameterHandler.setParameters((PreparedStatement) statement);

}

(3)ParameterHandler參數處理器

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

查看并學習源碼,得到以下幾點重要知識點:

第一:Mybatis提供了ParamterHandler的默認實現類DefalutParameterHandler

一文讓你秒懂Mybatis的SqlSession運行原理


public interface ParameterHandler {

Object getParameterObject();

void setParameters(PreparedStatement ps)

throws SQLException;

}

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

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

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

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結果集處理器

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

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

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實現類DefaultResultSetHandler,其中重點是handlerResultSets()的實現,但是其實現過程比較復雜,這里不過多介紹(emmmmm....個人目前能力還達理解,仍需努力)

一文讓你秒懂Mybatis的SqlSession運行原理


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

@Override

public <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);

}

}

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

@Override

public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {

PreparedStatement ps = (PreparedStatement) statement;

ps.execute();

return resultSetHandler.<E> handleResultSets(ps);

}

3、SqlSession運行總結

(1)文字總結

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

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預編譯SQL

由適配模式生成的RoutingStatementHandler根據上下文選擇生成三種相應的XXXStatementHandler;

在生成的XXXStatementHandler內部instantiateStatement()方法執行底層JDBC的prepareStatement()方法完成預編譯

2)parameterize設置參數

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

3)doUpdate/doQuery執行SQL

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

(2)運行圖總結

1)SqlSession內部總運行圖

一文讓你秒懂Mybatis的SqlSession運行原理


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

一文讓你秒懂Mybatis的SqlSession運行原理


一文讓你秒懂Mybatis的SqlSession運行原理


向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

安溪县| 宜章县| 蕉岭县| 涿鹿县| 青神县| 敖汉旗| 平江县| 镇坪县| 隆尧县| 宜黄县| 烟台市| 汉寿县| 通化市| 安泽县| 玛多县| 湖州市| 秀山| 柳河县| 定州市| 锡林郭勒盟| 西华县| 涟水县| 延长县| 宜昌市| 都兰县| 杭锦后旗| 鲜城| 徐闻县| 屏边| 濮阳县| 秦安县| 通化县| 米易县| 托里县| 太保市| 资溪县| 府谷县| 彝良县| 衡山县| 冕宁县| 安岳县|