Mybatis源碼解析5:Mapper執行流程1

Mybatis源碼解析5:Mapper執行流程1

    • 1.項目結構
    • 2. 源碼分析
      • 2.1 Mapper代理 MapperProxy#invoke
      • 2.2 創建MapperMethod
        • 2.2.1 方法名稱解析器ParamNameResolve
        • 2.2.2 MapperMethod#execute
      • 2.3 DefaultSqlSession
      • 2.4 CachingExecutor
      • 2.5 SimpleExecutor#doQuery
        • 獲取連接對象 Connection

1.項目結構

2. 源碼分析

2.1 Mapper代理 MapperProxy#invoke

當調用BlogMapper.getById(id)時,由于BlogMapper時JDK代理的,會回調MapperProxy#invoke方法

@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);} else {return cachedInvoker(method).invoke(proxy, method, args, sqlSession);}} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}}

2.2 創建MapperMethod

public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {this.command = new SqlCommand(config, mapperInterface, method);this.method = new MethodSignature(config, mapperInterface, method);
}public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);if (resolvedReturnType instanceof Class<?>) {this.returnType = (Class<?>) resolvedReturnType;} else if (resolvedReturnType instanceof ParameterizedType) {this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();} else {this.returnType = method.getReturnType();}this.returnsVoid = void.class.equals(this.returnType);this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();this.returnsCursor = Cursor.class.equals(this.returnType);this.returnsOptional = Optional.class.equals(this.returnType);this.mapKey = getMapKey(method);this.returnsMap = this.mapKey != null;this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);this.paramNameResolver = new ParamNameResolver(configuration, method);
}

創建MapperMethod對象,其中的方法簽名MethodSignature解析了方法的信息,我們最常用的@Param注解就是在這個時候解析的–ParamNameResolver。

2.2.1 方法名稱解析器ParamNameResolve

ParamNameResolver#ParamNameResolver

public ParamNameResolver(Configuration config, Method method) {final Class<?>[] paramTypes = method.getParameterTypes();final Annotation[][] paramAnnotations = method.getParameterAnnotations();final SortedMap<Integer, String> map = new TreeMap<>();int paramCount = paramAnnotations.length;// get names from @Param annotationsfor (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {if (isSpecialParameter(paramTypes[paramIndex])) {// skip special parameterscontinue;}String name = null;for (Annotation annotation : paramAnnotations[paramIndex]) {if (annotation instanceof Param) {hasParamAnnotation = true;name = ((Param) annotation).value();break;}}if (name == null) {// @Param was not specified.if (config.isUseActualParamName()) {name = getActualParamName(method, paramIndex);}if (name == null) {// use the parameter index as the name ("0", "1", ...)// gcode issue #71name = String.valueOf(map.size());}}map.put(paramIndex, name);}names = Collections.unmodifiableSortedMap(map);}
  1. 獲取方法參數數組,它是一個二階數組,一個方法多個參數,一個參數多個注解
  2. 如果有@Param注解就將hasParamAnnotation屬性設置為true。并且將該參數在方法中的索引與@Param設置的別名綁定起來(key為索引,value為@Param的值)最終封裝到names。以便后期對參數的名稱和實際參數的值進行綁定。

ParamNameResolver#getNamedParams

public Object getNamedParams(Object[] args) {final int paramCount = names.size();if (args == null || paramCount == 0) {return null;} else if (!hasParamAnnotation && paramCount == 1) {return args[names.firstKey()];} else {final Map<String, Object> param = new ParamMap<>();int i = 0;for (Map.Entry<Integer, String> entry : names.entrySet()) {param.put(entry.getValue(), args[entry.getKey()]);// add generic param names (param1, param2, ...)final String genericParamName = GENERIC_NAME_PREFIX + (i + 1);// ensure not to overwrite parameter named with @Paramif (!names.containsValue(genericParamName)) {param.put(genericParamName, args[entry.getKey()]);}i++;}return param;}}
  1. 參數args為Mapper接口的參數,為數組。
  2. 遍歷方法names,key為索引,value為@Param的名稱。最后將@Param的名稱找到參數的索引,通過索引可以找到args里面對象的值。最后將他們綁定返回。這個方法返回的也就是parameterObject,后面會提到。
2.2.2 MapperMethod#execute
public Object execute(SqlSession sqlSession, Object[] args) {Object result;switch (command.getType()) {case INSERT: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.insert(command.getName(), param));break;}case UPDATE: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.update(command.getName(), param));break;}case DELETE: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.delete(command.getName(), param));break;}case SELECT:if (method.returnsVoid() && method.hasResultHandler()) {executeWithResultHandler(sqlSession, args);result = null;} else if (method.returnsMany()) {result = executeForMany(sqlSession, args);} else if (method.returnsMap()) {result = executeForMap(sqlSession, args);} else if (method.returnsCursor()) {result = executeForCursor(sqlSession, args);} else {Object param = method.convertArgsToSqlCommandParam(args);result = sqlSession.selectOne(command.getName(), param);if (method.returnsOptional()&& (result == null || !method.getReturnType().equals(result.getClass()))) {result = Optional.ofNullable(result);}}break;case FLUSH:result = sqlSession.flushStatements();break;default:throw new BindingException("Unknown execution method for: " + command.getName());}if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {throw new BindingException("Mapper method '" + command.getName()+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");}return result;}
  1. method.convertArgsToSqlCommandParam(args)調用了ParamNameResolver#getNamedParams獲取到已經綁定的參數信息
  2. 目前為止,對方法的簽名已經處理完畢,接下來就交給SqlSession了。result = sqlSession.selectOne(command.getName(), param)

2.3 DefaultSqlSession

  @Overridepublic <E> List<E> selectList(String statement, Object parameter) {return this.selectList(statement, parameter, RowBounds.DEFAULT);}@Overridepublic <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {try {MappedStatement ms = configuration.getMappedStatement(statement);return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);} catch (Exception e) {throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);} finally {ErrorContext.instance().reset();}}
  1. 從Configiration#mappedStatements里面取出Satement對象,然后交給執行器Executor執行
  2. 執行器是在SqlSession里面創建,這里可以對Executor代理

2.4 CachingExecutor

  @Overridepublic <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {BoundSql boundSql = ms.getBoundSql(parameterObject);CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);}
  1. BoundSql boundSql = ms.getBoundSql(parameterObject),這個方法對Sql節點進行了解析,如果是動態Sql,這里會很復雜,后面專題在講。只需要記住,這個方法返回了原始Sql,參數值(parameterObject),參數映射關系(parameterMappings)
    在這里插入圖片描述
  2. createCacheKey這個方法創建了緩存key
  @Overridepublic <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)throws SQLException {Cache cache = ms.getCache();if (cache != null) {flushCacheIfRequired(ms);if (ms.isUseCache() && resultHandler == null) {ensureNoOutParams(ms, boundSql);@SuppressWarnings("unchecked")List<E> list = (List<E>) tcm.getObject(cache, key);if (list == null) {list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);tcm.putObject(cache, key, list); // issue #578 and #116}return list;}}return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);}

如果有緩存,就從緩存里面取;沒有緩存,就從數據庫查詢,這里是委托SimpleExecutor查詢

@SuppressWarnings("unchecked")@Overridepublic <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());if (closed) {throw new ExecutorException("Executor was closed.");}if (queryStack == 0 && ms.isFlushCacheRequired()) {clearLocalCache();}List<E> list;try {queryStack++;list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;if (list != null) {handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);} else {list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);}} finally {queryStack--;}if (queryStack == 0) {for (DeferredLoad deferredLoad : deferredLoads) {deferredLoad.load();}// issue #601deferredLoads.clear();if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {// issue #482clearLocalCache();}}return list;}

這里又定義了一個localCache緩存,緩存中沒有,就queryFromDatabase從數據庫中查詢

2.5 SimpleExecutor#doQuery

  @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.query(stmt, resultHandler);} finally {closeStatement(stmt);}}
  1. 創建SatementHandler對象,這里會代理
  2. prepareStatement方法主要負責創建連接,通過ParameterHandler設置參數
  3. 通過StatementHandler執行Satement
  4. 通過ResultSetHandler處理結果集
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) { // issue #435, get the key before calculating the statementgenerateKeys(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);}
獲取連接對象 Connection

BaseExecutor#getConnection

protected Connection getConnection(Log statementLog) throws SQLException {Connection connection = transaction.getConnection();if (statementLog.isDebugEnabled()) {return ConnectionLogger.newInstance(connection, statementLog, queryStack);} else {return connection;}
}ConnectionLogger#invoke
@Overridepublic Object invoke(Object proxy, Method method, Object[] params)throws Throwable {try {if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, params);}if ("prepareStatement".equals(method.getName())) {if (isDebugEnabled()) {debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true);}PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);return stmt;} else if ("prepareCall".equals(method.getName())) {if (isDebugEnabled()) {debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true);}PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);return stmt;} else if ("createStatement".equals(method.getName())) {Statement stmt = (Statement) method.invoke(connection, params);stmt = StatementLogger.newInstance(stmt, statementLog, queryStack);return stmt;} else {return method.invoke(connection, params);}} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}}

通過transaction獲取連接對象,如果開啟了日志,那么就創建代理對象,可打印日志

ManagedTransaction#openConnection

protected void openConnection() throws SQLException {if (log.isDebugEnabled()) {log.debug("Opening JDBC Connection");}connection = dataSource.getConnection();if (level != null) {connection.setTransactionIsolation(level.getLevel());}setDesiredAutoCommit(autoCommit);
}@Override
public Connection getConnection() throws SQLException {return popConnection(dataSource.getUsername(), dataSource.getPassword()).getProxyConnection();
}public PooledConnection(Connection connection, PooledDataSource dataSource) {this.hashCode = connection.hashCode();this.realConnection = connection;this.dataSource = dataSource;this.createdTimestamp = System.currentTimeMillis();this.lastUsedTimestamp = System.currentTimeMillis();this.valid = true;this.proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), IFACES, this);}
  1. 通過數據源的信息獲取連接對象,PooledConnection對Connection對象進行代理,便于連接池對連接對象的管理,返回的是連接對象
  2. 可設置事務隔離等級

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

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

相關文章

Nacos源碼解讀09——配置中心配置信息創建修改怎么處理的

存儲配置 從整體上Nacos服務端的配置存儲分為三層&#xff1a; 內存&#xff1a;Nacos每個節點都在內存里緩存了配置&#xff0c;但是只包含配置的md5&#xff08;緩存配置文件太多了&#xff09;&#xff0c;所以內存級別的配置只能用于比較配置是否發生了變更&#xff0c;只用…

進行生成簡單數字圖片

1.之前只能做一些圖像預測,我有個大膽的想法,如果神經網絡正向就是預測圖片的類別,如果我只有一個類別那就可以進行生成圖片,專業術語叫做gan對抗網絡 2.訓練代碼 import torch import torch.nn as nn import torch.optim as optim import torchvision.transforms as transfo…

飛天使-rsync大文件斷點續傳與vim批量刪除

文章目錄 rsync 斷點續傳&#xff0c;親測有效vim 批量刪除消息 rsync 斷點續傳&#xff0c;親測有效 rsync -vzrtp -P --append -e "/usr/bin/ssh -p 22 -o StrictHostKeyCheckingno" m.tar.gz root10.0.0.1:/tmp后臺運行 screem 既可 或者 nohup rsync -vzrt…

【華為od】存在一個m*n的二維數組,其成員取值范圍為0,1。其中值為1的元素具備擴散性,每經過1S,將上下左右值為0的元素同化為1。

存在一個m*n的二維數組,其成員取值范圍為0,1。其中值為1的元素具備擴散性,每經過1S,將上下左右值為0的元素同化為1。將數組所有成員初始化為0,將矩陣的[i, j]和[m,n]位置上元素修改成1后,在經過多長時間所有元素變為1。 輸入描述 輸入的前兩個數字是矩陣大小。后面是數字…

盛域宏數合伙人張天:AI時代,數字化要以AI重構

大數據產業創新服務媒體 ——聚焦數據 改變商業 在這個飛速發展的科技時代&#xff0c;數字化已經深刻地改變了我們的生活和商業方式。信息技術的迅猛發展使得數據成為現代社會最寶貴的資源之一。數字化已經不再是可選項&#xff0c;而是企業持續發展的必由之路。背靠著數據的…

【React】路由的基礎使用

react-router-dom6的基礎使用 1、安裝依賴 npm i react-router-dom默認安裝最新版本的 2、在src/router/index.js import { createBrowserRouter } from "react-router-dom"/* createBrowserRouter&#xff1a;[/home]--h5路由createHashRouter&#xff1a;[/#/ho…

Linux訪問NFS存儲及自動掛載

本章主要介紹NFS客戶端的使用 創建NFS服務器并通過NFS共享一個目錄在客戶端上訪問NFS共享的目錄自動掛載的配置和使用 1.1 訪問NFS存儲 前面那篇介紹了本地存儲&#xff0c;本章就來介紹如何使用網絡上上的存儲設備。NFS即網絡文件系統&#xff0c;所實現的是Linux和Linux之…

通信:mqtt學習網址

看這個網址&#xff1a;講的很詳細&#xff0c;后面補實戰例子 第一章 - MQTT介紹 MQTT協議中文版 (gitbooks.io)https://mcxiaoke.gitbooks.io/mqtt-cn/content/mqtt/01-Introduction.html

【網絡編程】-- 04 UDP

網絡編程 6 UDP 6.1 初識Tomcat 服務端 自定義 STomcat S 客戶端 自定義 C瀏覽器 B 6.2 UDP 6.2.1 udp實現發送消息 接收端&#xff1a; package com.duo.lesson03;import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketExceptio…

【論文極速讀】LVM,視覺大模型的GPT時刻?

【論文極速讀】LVM&#xff0c;視覺大模型的GPT時刻&#xff1f; FesianXu 20231210 at Baidu Search Team 前言 這一周&#xff0c;LVM在arxiv上剛掛出不久&#xff0c;就被眾多自媒體宣傳為『視覺大模型的GPT時刻』&#xff0c;筆者抱著強烈的好奇心&#xff0c;在繁忙工作之…

m.2固態硬盤怎么選擇?

一、什么是固態硬盤 固態硬盤又稱SSD&#xff0c;是Solid State Drive的簡稱&#xff0c;由于采用了閃存技術&#xff0c;其處理速度遠遠超過傳統的機械硬盤&#xff0c;這主要是因為固態硬盤的數據以電子的方式存儲在閃存芯片中&#xff0c;不需要像機械硬盤那樣通過磁頭讀寫磁…

linux查看筆記本電池健康情況

本人的老電腦&#xff0c;筆記本x1 carbon 5th 用久了&#xff0c;電池不行了&#xff0c;實際容量只有27.657%&#xff0c;充電到40%的時候&#xff0c;一瞬間彪滿100%。到某寶淘了一個 model: 01AV430的電池,等更換了再看看使用情況 $ upower --help 用法&#xff1a;upower…

Linux 安裝 中間件 Tuxedo

安裝步聚 一、首先&#xff0c;下載中間件安裝包&#xff1a; tuxedo121300_64_Linux_01_x86 Tuxedo下載地址&#xff1a; Oracle Tuxedo Downloads 二、新建Oracle用戶組&#xff08;創建Oracle用戶時&#xff0c;需要root權限操作&#xff0c;登陸&#xff09; [rootloca…

【CiteSpace】引文可視化分析軟件CiteSpace下載與安裝

CiteSpace 譯“引文空間”&#xff0c;是一款著眼于分析科學分析中蘊含的潛在知識&#xff0c;是在科學計量學、數據可視化背景下逐漸發展起來的引文可視化分析軟件。由于是通過可視化的手段來呈現科學知識的結構、規律和分布情況&#xff0c;因此也將通過此類方法分析得到的可…

【Spring教程23】Spring框架實戰:從零開始學習SpringMVC 之 SpringMVC簡介與SpringMVC概述

目錄 1&#xff0c;SpringMVC簡介2、SpringMVC概述 歡迎大家回到《Java教程之Spring30天快速入門》&#xff0c;本教程所有示例均基于Maven實現&#xff0c;如果您對Maven還很陌生&#xff0c;請移步本人的博文《如何在windows11下安裝Maven并配置以及 IDEA配置Maven環境》&…

python使用vtk與mayavi三維可視化繪圖

VTK&#xff08;Visualization Toolkit&#xff09;是3D計算機圖形學、圖像處理和可視化的強大工具。它可以通過Python綁定使用&#xff0c;適合于科學數據的復雜可視化。Mayavi 依賴于 VTK (Visualization Toolkit)&#xff0c;一個用于 3D 計算機圖形、圖像處理和可視化的強大…

AS安裝目錄

編輯器&#xff1a; sdk: gradle: gradle使用的jdk目錄&#xff1a;Gradle使用的jdk是android studio安裝目錄下的jbr 成功項目的android studio配置&#xff1a;

H264碼流結構

視頻編碼的碼流結構是指視頻經過編碼之后得到的二進制數據是怎么組織的&#xff0c;或者說&#xff0c;就是編碼后的碼流我們怎么將一幀幀編碼后的圖像數據分離出來&#xff0c;以及在二進制碼流數據中&#xff0c;哪一塊數據是一幀圖像&#xff0c;哪一塊數據是另外一幀圖像。…

C++面試寶典第4題:合并鏈表

題目 有一個鏈表&#xff0c;其節點聲明如下&#xff1a; struct TNode {int nData;struct TNode *pNext;TNode(int x) : nData(x), pNext(NULL) {} }; 現給定兩個按升序排列的單鏈表pA和pB&#xff0c;請編寫一個函數&#xff0c;實現這兩個單鏈表的合并。合并后&#xff0c;…

scheduleatfixedrate詳解

scheduleatfixedrate詳解 大家好&#xff0c;我是免費搭建查券返利機器人賺傭金就用微賺淘客系統3.0的小編&#xff0c;也是冬天不穿秋褲&#xff0c;天冷也要風度的程序猿&#xff01;在Java開發中&#xff0c;我們常常需要執行定時任務&#xff0c;并且需要保證任務按照一定…