MyBatis - 7 - 工作原理(builder❤️factory❤️session❤️mapper❤️查詢)【多圖】

# 分層架構圖

在這裏插入圖片描述

# 用戶基本操作

分析基本操作,後面深入瞭解每步操作的源碼流程

  1. 獲取 sqlSessionFactory 對象
    在這裏插入圖片描述

  2. 獲取 sqlSession 對象
    在這裏插入圖片描述

  3. 獲取接口代理對象(MapperProxy)
    在這裏插入圖片描述
    在這裏插入圖片描述

  4. 執行增刪改查方法


# 瞭解源碼 - 目的

爲 mybatis 插件開發做鋪墊

在這裏插入圖片描述
源碼學習過程中

着重注意以下幾個類

  • Executor - 執行器
  • ParameterHandler - 參數處理器
  • ResultSetHandler - 結果集處理器
  • StatementHandler - SQL語句處理器

# 源碼 - 1: 看 builder ⇒ factory 全流程

在 我們 自己寫的 getSqlSessionFactory 上打一個斷點
在這裏插入圖片描述
在這裏插入圖片描述

build 一進來, 創建 XMLConfigBuilder 用來解析 xml 配置文件

在這裏插入圖片描述
在這裏插入圖片描述

進入到 parse 方法

看到 調用 parseConfiguration ,用來解析配置文件

在這裏插入圖片描述

看看 parseConfiguration 怎麼玩的
在這裏插入圖片描述
可見(上圖),先拿到 configuration 節點(根節點,對應xml文件就能清楚看到)

然後 挨個解析每一個節點信息

  • settings
  • properties
  • typeAliases
  • plugins
  • ...

在這裏插入圖片描述

然後,再進入 看 settingsElement

每一個 settings 標籤 能進行的設置,都能看到了(下圖) 🐶🐶🐶

❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️

🌻🌻🌻🌻🌻🌻🌻🌟🌟🌟🌟🌟🌟🌟🌟🌟

⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️

在這裏插入圖片描述
同樣的 ,看看 mapperElement
在這裏插入圖片描述
各種判斷,看看 <mapper> 註解上,我們用了什麼方式 引用的 mapper.xml 文件
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

分析完成後,(綠框) , 調用相應的解析方法
在這裏插入圖片描述

這裏的 parse 是什麼? XPathParser

在這裏插入圖片描述
點進去 看 parse() 方法 , 看怎麼解析 mapper 文件的

在這裏插入圖片描述
(下圖)先拿到 namespace
在這裏插入圖片描述

在這裏插入圖片描述
在這裏插入圖片描述
然後 分析:

  • parameterMap
  • resultMap
  • sql
  • select / insert / update / delete

進入 解析 增刪改查方法

在這裏插入圖片描述

在這裏插入圖片描述
可以看到,(結合上下面),context 就是我們 的每一條 包含 sql 語句的標籤。

statementParser 就是對這每一條的標籤的 解析類

在這裏插入圖片描述
在這裏插入圖片描述

這個類 調用了 parseStatementNode 方法,點進去看

在這裏插入圖片描述

🍻🍻🍻🍻🍻🍻🍻🍻🍻🍻🍻🍻🍻🍻🍻
😻😻😻😻😻😻😻😻😻
🌹🌹

(下面) 對 select / insert / delete / update 標籤的 各種屬性 的解析

🌹🌹
😻😻😻😻😻😻😻😻😻😻
🍻🍻🍻🍻🍻🍻🍻🍻🍻🍻🍻🍻🍻🍻🍻
在這裏插入圖片描述
拿出 這些標籤的 詳細屬性 幹什麼?

在方法最後,看到 ,

用這些屬性 構造了 一個 MappedStatement ( 下圖 )

在這裏插入圖片描述

在這裏插入圖片描述

自然的 , 我們呢 進去 看 addMapStatement

在這裏插入圖片描述
最終返回的 是一個 封裝好的 statement

🍁🍁🍁🍁🍁🍁🍁🍁🍁
🍃🍃🍃🍃🍃🍃

不難總結出:mybatis配置裏面,一個MappedStatement就代表一個增刪改查標籤的詳細信息
🌴🌴🌴🌴🌴🌴🌴🌴
在這裏插入圖片描述

好,經過這樣 的 各種循環之後,配置的解析就ok了。

回到 XMLConfigBuilder的 parse 方法,看返回 的 configuration

裏面包含 了 所有

在這裏插入圖片描述

在這裏插入圖片描述
。。。
接上面

。。。
在這裏插入圖片描述

這裏 還有 一個重要的 屬性

mapperRegistry
(下圖)可以看到,這個屬性記錄的 每個 mapper 都有一個對應的 代理工廠
在這裏插入圖片描述

再 return 會 builder
在這裏插入圖片描述

builder 用 這個 configuration 最終創建 工廠實例
在這裏插入圖片描述

## 總結

根據配置,創建SQLSessionFactory

(把配置文件的信息解析並保存在 Configuration 對象中,返回 DefaultSqlSession 對象)
在這裏插入圖片描述

# 源碼 - 2 :factory ⇒ session 全流程

上面 看到了 SqlSessionFactory 的獲取過程

下面 看 openSession , 看 factory 獲取 session 的全過程
在這裏插入圖片描述

這裏,sqlsessionFactory 的 實現類 是 DefaultSqlSessionFactory

opensession() 方法裏面 調用的 是 openSessionFromDataSource ,

這裏 傳進去的 第一個參數 是 configuration.getDefaultExecutorType() (執行器的類型)

在這裏插入圖片描述

值是 SIMPLE , 什麼意思?
在這裏插入圖片描述
官方文檔有講,
值有三
+ SIMPLE (默認) ==》 簡單的執行器
+ RESUSE - 重用 ==》 可以重用的執行器
+ BATCH - 批量 ==》 做批量操作的執行器
在這裏插入圖片描述

在這裏插入圖片描述

(下圖)可見,事務在這裏開啓
在這裏插入圖片描述

關鍵,看 Executor

在這裏插入圖片描述

點進 Executor 接口,定義了 增刪查改 、事務、緩存 三部分的 方法
在這裏插入圖片描述

進入 newExecutor 方法

這裏(下圖),就會根據之前說的 type 不同,創建不同的 Executor
在這裏插入圖片描述

然後 判斷是否開啓二級緩存。

如果開啓, 創建 CachingExecutor

在這裏插入圖片描述

點進去看 CachingExecutor

(下圖) , 構造方法,對傳入的 Executor 進行包裝

實際上,還是 傳入的 Executor 執行 Execute 操作

在這裏插入圖片描述

這樣做的作用?
就 query 查詢爲例,用 CacheExecutor 的 query 會 在 查詢前 查 緩存,如果有,直接返回 查詢結果。
在這裏插入圖片描述

下面看 interceptorChain.pluginAll

攔截器鏈的 插入 全部 插件 方法

在這裏插入圖片描述
在這裏插入圖片描述

可以看到(下圖),就是遍歷所有的 攔截器(interceptor) , 對executor 進行包裝

在這裏插入圖片描述
newExecutor 方法看完。

退回到 外面

(下圖)創建 DefaultSqlSession ,裏面包含了 configuration 和 executor 的信息

最終,返回 DefaultSqlSession
在這裏插入圖片描述

## 總結

獲取 SqlSession 對象
返回一個 DefaultSqlSession 對象,包含 Executor 和 Configuration
這一步會創建 Executor

在這裏插入圖片描述

# 源碼 - 3 :session ⇒ mapper 全流程

在這裏插入圖片描述

DefaulttSqlSession 的 getMapper 調用的是 configuration 的 getMapper

在這裏插入圖片描述
兩個參數

type 是 mapper 接口 的類型,調用的 用戶指定

在這裏插入圖片描述
this 是 調用 configuration 的 DefaultSqlSession

在這裏插入圖片描述

進入 configuration 的 getMapper 方法
可以看到(下圖), configuration 調用的 是 mapperRegistry 的 getMapper 方法
在這裏插入圖片描述

雷豐陽

mapperRegistry 是什麼?
configuration 的 一個屬性
在這裏插入圖片描述
其實之前 也見過
在這裏插入圖片描述
講 獲取 SqlSessionFactoryBuilder 時候 見到過
registry 裏面包含了 配置的 mapper 和 對應的 代理 工廠 MapperProxyFactory

進去 mapperRegistry 的 getMapper 看(下圖)

首先,從 registry >> knownMappers 下 獲取 代理工廠(根據接口類型)

在這裏插入圖片描述
在這裏插入圖片描述
(如果拿不到,就報錯)
如果拿到了 ,就獲取實例

在這裏插入圖片描述

進入 newInstance

可以看到 是對 一些 屬性的封裝

  • sqlSesson
  • mapperInterface - 當前的接口
  • methodCache - 當前方法對應的 緩存 map

在這裏插入圖片描述
點進去
在這裏插入圖片描述

⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️
⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️
⭐️⭐️⭐️

這裏 看到 MapperProxy 實現了 InvocationHandler !!!

⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️
⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️
⭐️⭐️⭐️

InvocationHandler 是什麼?
Proxy 的 newProxyInstance 方法
在這裏插入圖片描述
在這裏插入圖片描述
他這裏說了
invocationHandler 是用來做method dispatch 的
也就是 ,指定代理的增強 具體做什麼

繼續看 newInstance,(下圖)
其實 , 就是調用了 java.lang.reflect.Proxy 的 newProxyInstance ,創建一個 代理對象。
(其中,傳入 mapperProxy 指定 代理的具體操作)
在這裏插入圖片描述

最後,創建出來的 代理對象,被傳出去 給用戶使用
在這裏插入圖片描述

## 總結

獲取接口的代理對象(MapperProxy)
getMapper 使用 MapperProxyFactory 創建一個 MapperProxy 的代理對象
代理對象裏面包含了, DefaultSqlSession (這裏 包含了 執行增刪改查的 Executor)

在這裏插入圖片描述

# 源碼 - 4 : mapper ⇒ 查詢

在這裏插入圖片描述
在這裏插入圖片描述

在這裏插入圖片描述
在這裏插入圖片描述

(下圖)看是不是 jdk 默認的方法
在這裏插入圖片描述
如果不是 默認的方法(是用戶指定的方法) , 就把 method 封裝成 MapperMethod (mapper 能認識的 method 類)
然後 execute 執行方法

在這裏插入圖片描述

然後 進入 execute 方法

(下圖) 判斷 是

  • insert
  • update
  • delete
  • select

的哪一種
在這裏插入圖片描述
以 select 爲例

根據返回值類型的不同 ,調用不同 方法 獲取 result 值

在這裏插入圖片描述

如果 只返回一個 對象,就會來到最後的判斷

首先 ,執行 method.convertArgsToSqlCommandParam 分析 參數 , 獲得 param

在這裏插入圖片描述
在這裏插入圖片描述
(下圖)如果參數時單個,返回單個,如果參數時多個,包裝成一個 map 返回

在這裏插入圖片描述

返回到 execute 方法,下一步 執行 sqlSession 的 selectOne (下一步)

在這裏插入圖片描述
在這裏插入圖片描述

上面一直看下來,我們知道 sqlsession 的 實現類是 defaultSqlSession
看 defaultSqlSession 的 selectOne(下圖)
在這裏插入圖片描述
(上圖)可以看到,儘管是 selectone ,調用的 還是 selectList (查詢多個)

看 selectList 是怎麼查詢的

在這裏插入圖片描述

(上圖) 先從 configuration 裏面 拿到 statement 的 映射集

裏面映射了方法的詳細信息

在這裏插入圖片描述

在這裏插入圖片描述
然後,把映射的 方法詳細信息傳遞給 executor 的 query 查詢方法

在這裏插入圖片描述

executor.query 方法 參數裏面還調用一個方法
wrapCollection 分析傳入參數 是 集合的情況
點進去 看看

在這裏插入圖片描述

再看 query

在這裏插入圖片描述

(上圖)getBoundSql 獲取綁定的 sql

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

在這裏插入圖片描述

然後 後面 算出 cache 的key

key: hashCode + 方法id + sql + 參數 xxx 等。。。

特別長 ↓

在這裏插入圖片描述
在這裏插入圖片描述

在這裏插入圖片描述

在這裏插入圖片描述

然後是 重寫 的 query (下圖)

在這裏插入圖片描述
判斷有沒有緩存,
有 就 緩存取值 直接返回

沒有,就 調用 delegate 的 query 方法
在這裏插入圖片描述

(上圖) executor 的 實例 這裏是 SimpleExecutor (具體是什麼,在前面說了,根據配置創建)

所以,這裏,就點進 SimpleExecutor 的 query 方法

(我們發現,SimpleExecutor 繼承 BaseExecutor , query 在 BaseExecutor 中 就已經寫好了)

在這裏插入圖片描述

可以看到,這裏是會 嘗試 從一級緩衝(localCache)中取數據

在這裏插入圖片描述
在這裏插入圖片描述
(上圖)但是,因爲是第一次查,所以 沒有數據

在這裏插入圖片描述

我們進入 queryFromDatabase

在這裏插入圖片描述

首先,在緩存中 在 命名空間中 先 放一個佔位符(下面)

再 查到數據 (list) 之後,再放list入緩存

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

然後是 , doQuery ,下面 看看 傳入的 參數

ms mappedStatement 這次查詢的 詳細 標籤信息
在這裏插入圖片描述
參數
在這裏插入圖片描述
rowBounds 做邏輯分頁(不分頁 下面就默認值)
在這裏插入圖片描述
resultHandler 結果處理
在這裏插入圖片描述
boundSQL 就是 sql 語句
在這裏插入圖片描述

點進去 doQuery

在這裏插入圖片描述
statement 是 原生jdk 的 statement哦
在這裏插入圖片描述
(下圖)拿到配置信息

在這裏插入圖片描述

下面 是 new statementHandler 對象(四大 對象之一哦)

作用?
可以創建出 statement 對象。

在這裏插入圖片描述
(下圖) 首先 new 一個 RoutingStatementHandler
在這裏插入圖片描述

看 RoutingStatementHandler 的構造 (下圖)

判斷 statementType , 創建響應的 statementHandler

在這裏插入圖片描述
在這裏插入圖片描述

StatementType 是在哪裏定義的?
mapper.xml 裏面
在這裏插入圖片描述
默認值? Prepared
在這裏插入圖片描述

所以 這裏 new 的是 默認的 PrepareStatementHandler (下圖)

在這裏插入圖片描述

調用的 PreparedStatementHandler 父類 BaseStatementHandler (下圖)

在這裏插入圖片描述

BaseStatementHandler 的構造 對傳入 參數 進行封裝 (下圖)

參數:

  • executor - 執行器
  • mappedStatement - 詳細信息(最強)
  • parameterObject - 參數
  • rowBounds - 分頁信息(這裏沒用)
  • resultHandler - 結果處理器
  • boundSql - sql 語句

在這裏插入圖片描述
(上圖) 需要留意,這裏new 了兩個 處理器
一個 是 參數的
一個是 結果的

注意: 兩個末尾都調用了 pluginAll
在這裏插入圖片描述

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

(下圖) 然後 , 調用 interceptorChain 的 所以插件 方法 pluginAll (這和 前面的 executor 一樣)
在這裏插入圖片描述

然後 繼續返回
在這裏插入圖片描述
在這裏插入圖片描述

然後用 prepareStatement 創建 jdk 原生的 statement 對象 (下圖)

在這裏插入圖片描述
創建 原生 statement 的過程也很簡單(下圖)

在這裏插入圖片描述

(下圖) 打開 連接

通過 handler 的 prepare 方法 創建 statement

在這裏插入圖片描述
在這裏插入圖片描述

(下圖) handler.parameterize 參數預編譯賦值

預編譯 sql 產生 PreparedStatement 對象(下圖)

在這裏插入圖片描述
在這裏插入圖片描述

RoutingStatementHandler幹嘛的??


RoutingStatementHandler 類似路由器,
其構造函數中會根據Mapper文件中設置的StatementType來選擇
使用
SimpleStatementHandler、
PreparedStatementHandler
或是CallableStatementHandler

其實現的接口StatementHandler的方法也是由這三個具體實現類來實現。
《Mybatis源碼之Statement處理器RoutingStatementHandler(三)》

到 RoutingStatementHandler 的 parameterize 方法(下面)

Routing 路由

在這裏插入圖片描述

注意:這裏的 delegate 是 preparedStatementHandler

在之前的代碼 看到 ,選擇了 preparedStatement

在這裏插入圖片描述

再 進去 parameterize
在這裏插入圖片描述

這裏的 parameterHandler 是在 構造 BaseStatementHandler 時候 賦值的

然後,setParameters (這裏是 DefaultParameterHandler)

setParameters 幹嘛?


之前不是預編譯了嘛。
這裏就把 預編譯時候的 “?” 填上值

在這裏插入圖片描述
首先,是獲取 參數映射
在這裏插入圖片描述
對參數值 做獲取(下圖)
在這裏插入圖片描述
typeHandler 根據 類型 對 參數值 和參數名做一一映射(下圖)

在這裏插入圖片描述
在這裏插入圖片描述
類型解析處理器(下圖)
在這裏插入圖片描述

這裏,(我們的po屬性就是 long) (下圖)

在這裏插入圖片描述
這裏,就有專門的 long 類型的處理器(下圖)

在這裏插入圖片描述

設置好了,就一直返回

(這裏 logger 是因爲 我開了 aop 和 日誌)
在這裏插入圖片描述
繼續看 handler 的 query 方法(下圖)
(前面三個代碼1、準備配置,2、準備處理器,3、準備statement 和預編譯。現在是真的查了!!!)
點進去
在這裏插入圖片描述

在這裏插入圖片描述

preparedStatement 執行 execute 方法(這裏 是 java.sql.PreparedStatement

在這裏插入圖片描述

(完)

另外,還可以看看 execute 的調用
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

## 總結

總結的流程如下:

  1. 根據配置文件(全局,sql映射)初始化出 Configuration 對象
  2. 創建一個 DefaultSqlSession 對象
    他裏面包含 Configuration
    以及 Executor (根據 全局 配置文件中的 defaultExecutorType 創建出對應的 Executor )
  3. DefaultSqlSession.getMapper() 拿到 Mapper 接口對應的 MapperProxy
  4. MapperProxy 裏面 有 (DefaultSqlSession)
  5. 執行增刪改查方法
    1. 調用 DefaultSqlSession 的增刪改查 (Executor)
    2. ·會創建一個 StatementHandler 對象。
      (同時也會創建 ParameterHandlerResultSetHandler
    3. 調用 StatementHandler 的預編譯策略和 參數設置值 (使用 ParameterHandler
    4. 調用 StatementHandler 的 增刪改查
    5. ResultSetHandler 封裝 結果

即:

代理對象 =》 DefaultSqlSession =》 Executor =》 StatementHandler

設置參數
處理結果
參數類型轉換
結果類型轉換
StatementHandler
ParameterHandler
ResultSetHandler
TypeHandler
JDBC:Statement:PreparedStatement

在這裏插入圖片描述
在這裏插入圖片描述


在這裏插入圖片描述


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章