框架源碼私享筆記(02)Mybatis核心框架原理 | 一條SQL透析核心組件功能特性

最近在思考一個問題:如何能夠更好的分享主流框架源碼學習筆記(主要是源碼部分)?讓有緣刷到的同學既可以有所收獲,還能保持對相關技術架構探討學習熱情和興趣。以及自己也保持較高的分享熱情和動力。

今天嘗試用一個SQL查詢作為引子,去解構Mybatis的核心原理和關鍵源碼處理流程。這種更加貼合工作實踐方式,相信可以降低探索核心源碼門檻。


一、前言背景

二、Mybatis概述

三、Mybatis的核心原理

3.1 Mybatis核心功能處理流程

3.1.1 解析配置-加載并解析Mapper配置文件

3.1.2 創建連接-創建SqlSessionFactory并獲取sqlSession連接

3.1.3 執行SQL語句-Executor

3.1.4 結果數據封裝 MapperStatement & ResultSetHandler

3.1.5 關閉連接

四、核心功能特性

4.1 支持動態靈活的SQL

4.2 詳解一級、二級緩存機制

4.2.1 二級緩存為什么默認不開啟?

4.3 支持插件擴展

4.4 延遲加載

4.5 SQL注解


【公眾號搜索:拉丁解牛說技術】歡迎一起交流討論。

一、前言背景

在10多年前,那時候剛開始工作,移動互聯網還沒發展起來,Mybatis還沒流行,后端應用開發,主流用的是SSH框架。回想那時候的Hibernate、Spring,配置多又雜,其實對新手并不友好。然而相比手寫JDBC連接管理、繁瑣的結果數據轉換,讓SSH當年也是火了好幾年。

隨著Hibernate和Mybatis的不斷發展,研發人員成功解放手寫JDBC數據庫連接查詢相關研發工作。他們都是優秀的ORM對象關系映射管理框架,也就是持久層框架。但是Hibernate存在對復雜sql關系支持弱、不支持存儲過程、性能差、調優難、全表映射復雜等問題,用的人越來越少。

而后起之秀Mybatis,當今最經典ORM框架,由于其靈活易用、好擴展、支持復雜SQL、出色的性能,較好的平衡對象關系映射管理和SQL編寫支持,稱為半ORM框架,已經成功替代Hibernate。

今天我們梳理Mybatis的核心原理和工作流程,以及重點分析它的一些核心功能特性。

二、Mybatis概述

Mybatis是一個持久層框架,具體就是用來操作數據庫數據,并轉換成目標對象的技術框架。它的核心在于將表數據和對象實例進行關聯映射,也就是ORM(object relation Mapping)。

Mybatis之所以可以替換Hibernate,主要是支持SQL定制、高級的映射管理功能、緩存機制、還有存儲過程(由于大數據技術發展,目前存儲過程用的人也越來越少,但是在那個年代支持存儲過程非常實用)。Mybatis靈活可擴展高性能的特性,讓我們開發讀寫數據,幾乎不需要編程,主要做的工作就是編寫Mapper配置文件,把SQL和對象關系映射管理好,就可以實現CRUD。而多類型數據庫的切換遷移,對系統應用來說,簡單到只需要替換JDBC的驅動。

三、Mybatis的核心原理

如上所述,Mybatis核心工作就是幫助研發人員對數據庫的讀寫操作,簡化成面向對象操作。

對于一個完全不懂Mybatis或者ORM的人來說,如果要實現讀寫數據庫,該怎么實現?這個相信很多人都能回答:通過jdbc連接數據庫、執行SQL、解析sql數據結果,就三個步驟完成。

而Mybatis的核心原理邏輯更加細化,但整體也是類似以上三個步驟。畢竟大家目標一致,只是實現過程不同而已。

接下來我們用一個非常簡單的demo,就是通過Mybatis去讀數據庫數據,demo就只有幾行代碼,然后循序漸進了解Mybatis的核心工作原理,以及核心源碼組件功能。


package com.lading.mybaties;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;import java.io.IOException;
import java.io.InputStream;public class MyBatisDemo {public static void main(String[] args) throws IOException {InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);try (SqlSession sqlSession = sqlSessionFactory.openSession()) {sqlSession.selectOne("com.lading.mapper.UserMapper.getUserById", 1);}}
}

3.1 Mybatis核心功能處理流程

Mybatis整體框架,基于面向對象思想去實現與數據庫表數據交互,它的核心步驟按順序處理有以下五個。

3.1.1 解析配置-加載并解析Mapper配置文件

Mybatis通過SqlSessionFactoryBuilder來加載解析配置文件,并生SqlSessionFactory。

SqlSessionFactoryBuilder是Mybatis的入口類,類似tomcat的org.apache.catalina.startup.Bootstrap 啟動類。

比如我們項目只有Mybatis包、jdbc驅動,想要基于Mybatis去讀寫數據,首先需要通過以下三行代碼去解析你的Mybatis相關配置文件,以及構建一個SqlSessionFactory,為后續創建數據庫連接和讀寫做準備。

String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

這三行代碼里最后一行,看源碼會發現,里面做了非常多的工作,細無巨細的把Mybatis管理配置文件、還有對象sql映射Mapper文件一一解析,包括properties、settings、environment、mapper等標簽數據,以及業務相關的mapper文件里的resultMap、CURD 標簽都會轉成configration對象屬性。

最后利用解析結果數據存放的對象configration去實例化構建sql 會話工廠:

new DefaultSqlSessionFactory(config)。

3.1.2 創建連接-創建SqlSessionFactory并獲取sqlSession連接

解析完成配置文件后,Mybatis框架已經清晰知道Mybatis的基礎配置信息、連接數據庫的類型、用戶密碼信息,還有相關表映射關系。

通過SqlSessionFactoryBuilder構建了session工廠實例SqlSessionFactory,看名字可以知道是通過建造者模式去實例化該對象。

SqlSessionFactory是Mybatis的核心接口,它就是用來負責實現管理會話連接。

在應用啟動或者需要用到Mybatis讀寫數據時候,就生成一個實例DefaultSqlSessionFactory(它是session工廠接口唯一實現類)。

SqlSessionFactory,通常在應用里是全局唯一并共享。

然后通過會話工廠實例sqlSessionFactory去開啟一個session連接:

SqlSession sqlSession = sqlSessionFactory.openSession();

SqlSession就是真正負責執行sql、并且管理事務的核心功能類,它底層是jdbc的連接。

session每次用完就關閉,需要用的時候再次新建。但是Mybatis也有實現對應的連接池,如果配置了連接池就不會關閉。 Mybatis的連接池,后面出一篇文章專門分享Mybatis如何管理連接池。

比如,通過session去查詢用戶ID=1的用戶數據:

SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User ladingUser = userMapper.selectUserById(1);
System.out.println(ladingUser);
3.1.3 執行SQL語句-Executor

在3.1.2里,看起來sql session執行了sql查詢,但是DefaultSqlSession里面封裝了一個Executor執行器。

Executor執行器,它是真正負責執行sql的打工人,里面有一個抽象類BaseExecutor,通過模板方法模式去共享自己的模板方法能力。另外三個子類去繼承實現不同的db操作。

這三個執行器的主要區別在于:

SimpleExecutor,是一個最簡單的執行器,每次執行sql都新建一個Statement對象。

比如它里面的query方法源碼:

@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, 
RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {// JDBC中 Statement接口Statement stmt = null;try {// 獲取到Configuration對象Configuration configuration = ms.getConfiguration();StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);// 里面創建Connection代理對象,新增這個Statementstmt = prepareStatement(handler, ms.getStatementLog());// 執行查詢,封裝結果集return handler.query(stmt, resultHandler);} finally {closeStatement(stmt);}
}

ReuseExecutor,顧名思義是可復用執行器。特點是,復用預處器PreparedStatement。

比如獲取預處理Statement的方法源碼:

而BatchExcutor,叫做批量執行器,支持批量處理SQL語句。

3.1.4 結果數據封裝 MapperStatement & ResultSetHandler

結果數據封裝邏輯里面相對繁瑣。實際上在Excutor執行sql之前,也依賴mapperStatement去封裝sql語句、sql的入參。底層通過jdbc connection執行sql后,通過ResultSetHandler和TypeHandler去解析封裝結果數據。

3.1.5 關閉連接

最后是關閉sqlsession,釋放資源。如果是配置應用連接池,就是歸還連接操作。

四、核心功能特性

4.1 支持動態靈活的SQL

直接允許在mapper xml文件配置動態的sql。包括if、where、choose、when、foreach等多種動態條件。這個能力讓Mybatis成功支持復雜關聯sql處理。

4.2 詳解一級、二級緩存機制

Mybatis支持緩存,大幅提升數據庫查詢性能。默認開啟一級緩存,關閉二級緩存。

一級緩存:是基于sqlsession去實現,同一個sql session多次查詢,會復用相同sql 的緩存結果。底層是通過把一個SQL的id、名稱、入參計算得到一個唯一key,并和結果數據存入一個map里。

當同一個sql session后面重復的查詢,就會判斷緩存是否有數據,如果有就直接返回,不再繼續從數據庫里查詢數據,大幅提升單個sql session里的重復查詢效率。

如果在一個sql session里出現了update、delete、insert、或者commit、close session操作,就會自動去清空緩存,確保沒有臟數據在Mybatis一級緩存里。

該緩存默認開啟。如果要關閉,可以通過flushCache=true去關閉。

二級緩存:是基于mapper,也就是命名空間級別的緩存。相當于sqlSessionFactory級別,比一級緩存sqlSession更高一個層級。在同一個mapper下,所有session會話產生的緩存數據統一在mapper里命名空間管理。多個mapper的二級緩存互不干擾。

二級緩存,默認是關閉的。可以在mapper里,新增cache標簽就可以開啟。

<mapper namespace="com.lading.mapper.UserMapper">
<!--啟用二級緩存-->
<cache eviction="FIFO" flushInterval="30000" readOnly="true"/>
</mapper>

4.2.1 二級緩存為什么默認不開啟?

之所以默認關閉,主要因為二級緩存可能有臟讀。

正因為所有session會話產生的緩存數據,統一在mapper里命名空間管理,多個mapper的二級緩存互不干擾。這里就可能導致研發人員如果在另一個mapper新增或者修改了數據,其他地方mapper緩存的數據就沒有被自動更新,就會造成生產故障。

當然這個Mybatis有提供相關配置,支持關聯mapper同步被動去清空二級緩存,避免干擾。

4.3 支持插件擴展

Mybatis允許研發人員在核心組件中插入自定義的邏輯,比如分頁、攔截器、性能監控插件功能 。

插件的開發,可以通過實現 org.apache.ibatis.pluginInterceptor接口,然后實現里面 intercept、plugin 和 setProperties方法來新增插件功能。

比如增加一個執行sql監控功能。

package com.lading.mybaties;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;import java.util.Properties;public class TimeMonitorPlugin implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {long startTime = System.currentTimeMillis();Object result = invocation.proceed(); // 執行目標方法long endTime = System.currentTimeMillis();System.out.println("SQL 執行耗時: " + (endTime - startTime) + "ms");return result;}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {// 可以設置攔截器的屬性}
}

在 mybatis-config.xml 文件中注冊自定義攔截器:

<plugins>
<plugin interceptor="lading.mybaties.TimeMonitorPlugin"/>
</plugins>

sql查詢的時候就會打印sql執行時間。

4.4 延遲加載

可以控制關聯對象,在需要用到的時候才去加載。比如1-n的場景,一個學生有多門課程信息。查詢一個學生基本信息的時候,如果沒有用到課程列表,就不需要在關聯查詢里把課程列表頁拉出去,提升了查詢效率。

//fetchType=lazy開啟延遲加載
<association property="xxx" fetchType="lazy">

這個延遲加載功能,和Springboot通過@lazy注解去解決循環依賴問題,有類似異曲同工的作用。

4.5 SQL注解

MyBatis 也支持使用注解來配置 SQL 映射,從而簡化 XML 配置,可以實現零xml配置文件去操作數據庫數據。常用的注解包括常規的CURD:

@Select

@Insert

@Update

@Delete

還有,@Results結果映射注解。

比如通過名稱去查詢用戶:

@Select("SELECT * FROM user WHERE name = #{name}")
@Results({@Result(property = "id", column = "id"),@Result(property = "name", column = "name")
})
User selectUserByName(String name);

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

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

相關文章

UNI-APP uts插件 支持ANDROID 監聽手機狀態

插件地址 https://ext.dcloud.net.cn/plugin?id22646 模塊 import {startPhoneListener,stopPhoneListener,checkIsAutoRecord,toCallAutoRecorderPage,navigateToCallRecordingSettings,jumpToPermissionPage,makePhoneCall,allRecorderFilesAction,registerSmsReceiver,} f…

windows協議不再續簽,華為再無windows可用,將于四月發布鴻蒙PC

大家好&#xff0c;我是國貨系創始人張云澤&#xff0c;最近不少小伙伴在后臺問&#xff1a;“聽說Windows協議要到期了&#xff1f;我的電腦會不會變磚&#xff1f;”還有人說&#xff1a;“華為筆記本以后用不了Windows了&#xff1f;鴻蒙系統能用嗎&#xff1f;”今天咱們就…

Stable Diffusion API /sdapi/v1/txt2img的完整參數列表及其說明

基本參數 {"prompt": "高質量&#xff0c;精細的恐龍", // 主提示詞"negative_prompt": "模糊&#xff0c;低質量", // 負面提示詞"styles": ["photorealistic", "detailed"], // 應用的風格預設&q…

TK矩陣:提高多賬號管理效率的利器

隨著TikTok的火爆&#xff0c;越來越多的人開始利用這個平臺進行內容創作和社交互動。無論是個人創作者、品牌方&#xff0c;還是營銷公司&#xff0c;TikTok都提供了巨大的機會&#xff0c;但同時也帶來了運營上的挑戰&#xff0c;尤其是在管理多個賬戶時。每個賬號的維護、內…

關于Redis的集群(上)

目錄 基本概念 數據分片算法 哈希求余 ?編輯一致性哈希算法 哈希槽分區算法 搭建集群環境 創建目錄和配置 編寫 docker-compose.yml 啟動容器 構建集群 基本概念 廣義的集群&#xff0c;只要是多個機器構成了分布式系統&#xff0c;都可以成為是一個“集群”。 但…

【CSS3】化神篇

目錄 平面轉換平移旋轉改變旋轉原點多重轉換縮放傾斜 漸變線性漸變徑向漸變 空間轉換平移視距旋轉立體呈現縮放 動畫使現步驟animation 復合屬性animation 屬性拆分逐幀動畫多組動畫 平面轉換 作用&#xff1a;為元素添加動態效果&#xff0c;一般與過渡配合使用 概念&#x…

Java 線程創建全解析:五種方式詳細對比與實戰示例

目錄 Java 線程創建全解析&#xff1a;五種方式詳細對比與實戰示例一、引言二、創建線程的五種方式1. 繼承 Thread 類2. 實現 Runnable 接口3. 匿名內部類實現 Thread4. 匿名內部類實現 Runnable5. 使用 Lambda 表達式&#xff08;Java 8及以上&#xff09; 三、線程創建方式對…

Dify平臺離線鏡像部署

Dify 是一款開源的大語言模型(LLM) 應用開發平臺。它融合了后端即服務&#xff08;Backend as Service&#xff09;和 LLMOps 的理念&#xff0c;使開發者可以快速搭建生產級的生成式 AI 應用。即使你是非技術人員&#xff0c;也能參與到 AI 應用的定義和數據運營過程中。 前提…

Unitest和pytest區別

1?? 設計理念 ?? unittest &#xff1a; 是 Python 標準庫的一部分&#xff0c;設計靈感來自 Java 的 JUnit&#xff0c;強調面向對象的測試模式。 測試用例需要繼承 unittest.TestCase 類&#xff0c;測試方法以 test_ 開頭。 更適合傳統、結構化的測試開發方式。 ? pyte…

GIT標簽(Tag)操作

在Git中&#xff0c;標簽&#xff08;Tag&#xff09;用于標記特定的提交點&#xff0c;通常用于發布版本。 切換到需要打標簽的分支&#xff1a; git checkout <branch-name>創建標簽 git tag v1.0.0 git tag -a v1.0.0 -m "Release version 1.0.0"查看所…

玩轉github

me github 可以給倉庫添加開發人員嗎 4o 是的&#xff0c;GitHub允許倉庫管理員為倉庫添加開發人員&#xff0c;并設置這些開發人員的角色和權限。這里是一個簡單的步驟指導&#xff0c;教你如何給一個 GitHub 倉庫添加開發人員&#xff1a; 前提條件 你必須有這個倉庫的權限&…

EDAS:投稿經驗-word版本-問題解決

1. 字體不對&#xff0c;字體未嵌入問題 問題&#xff1a;word轉PDF后&#xff0c;總是顯示有字體格式不對&#xff08;忘記截圖了&#xff09;。 辦法&#xff1a;1. EDAS投稿PDF格式問題-CSDN博客-PDF上修改 IEEE論文檢測的字體未嵌入問題Times New Ro…

如何在Django中有效地使用Celery進行定時任務?

當我們談到Web開發時&#xff0c;Django無疑是一個非常流行的框架。而Celery則是與Django配合使用的強大任務隊列工具。今天&#xff0c;我們來聊聊如何在Django中使用Celery來實現定時任務。定時任務在很多場景下都非常有用&#xff0c;比如定期發送郵件、清理數據庫、執行數據…

聲學建模中用于構音障礙語音識別的特征選擇意義

聲學建模中用于構音障礙語音識別的特征選擇意義 原文:Significance of Feature Selection for Acoustic Modeling in Dysarthric Speech Recognition 引言 背景 構音障礙是由運動言語系統的神經損傷引起的,導致發音不清晰。自動語音識別系統對構音障礙語音無效,因其聲學差…

【遞歸與動態規劃(DP) C/C++】(1)遞歸 與 動態規劃(DP)

- 第 82 篇 - Date: 2025 - 03 - 17 Author: 鄭龍浩/仟濹 【遞歸與動態規劃(DP) C/C】 文章目錄 一 遞歸1基本介紹2 遞歸技巧**(1) 遞歸三步法****(2) 思維小技巧** 3 例題(1) 階乘 (純遞歸 or DP)(2) 斐波那契數列 (純遞歸 or DP)(3) 漢諾塔 (純遞歸 or DP)**① 英文打印過程…

eclipse運行配置,希望帶參數該怎么配置

java -Dparam 在eclipse如何配置 在Eclipse中配置-Dparam這樣的JVM參數&#xff0c;你可以按照以下步驟進行&#xff1a; 打開Eclipse。 選擇菜單欄的"Run" -> "Run Configurations..."。 在彈出的"Run Configurations"窗口左側&#xff0…

什么是 Fisher 信息矩陣

什么是 Fisher 信息矩陣 Fisher 信息矩陣是統計學和機器學習中一個重要的概念,它用于衡量樣本數據所包含的關于模型參數的信息量。 伯努利分布示例 問題描述 假設我們有一個服從伯努利分布的隨機變量 X X X,其概率質量函數為 P ( X 

[C++面試] 標準容器面試點

一、入門 1、vector和list的區別 [C面試] vector 面試點總結 vector 是動態數組&#xff0c;它將元素存儲在連續的內存空間中。支持隨機訪問&#xff0c;即可以通過下標快速訪問任意位置的元素&#xff0c;時間復雜度為 O(1)&#xff0c;準確點是均攤O(1)。但在中間或開頭插…

C++抽象與類的核心概念解析

在C中&#xff0c;抽象&#xff08;Abstraction&#xff09; 是面向對象編程&#xff08;OOP&#xff09;的核心概念之一&#xff0c;它通過隱藏復雜的實現細節&#xff0c;僅暴露必要的接口來實現對現實世界的簡化建模。類&#xff08;Class&#xff09; 是實現抽象的核心工具…

C# NX二次開發:拉伸UFUN函數避坑指南

大家好&#xff0c;今天想說一下拉伸相關UFUN函數的使用&#xff0c;盡量讓大家別踩坑。 官方給出的拉伸UFUN函數有如下幾個&#xff1a; &#xff08;1&#xff09;UF_MODL_create_extruded2 (view source) uf_list_p_tobjectsInputList of objects to be extruded.char *ta…