Spring boot 4 搞懂MyBatis-Plus的用法解析
MyBatis-Plus 是一個(gè) MyBatis 的增強(qiáng)工具,在 MyBatis 的基礎(chǔ)上只做增強(qiáng)不做改變,為簡(jiǎn)化開發(fā)、提高效率而生
Spring boot 4如何集成
增加依賴
Add MyBatis-Plus dependency
<mybatisplus.version>3.5.15</mybatisplus.version>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot4-starter</artifactId>
<version>${mybatisplus.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-jsqlparser</artifactId>
<version>${mybatisplus.version}</version>
</dependency>配置
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
// 數(shù)據(jù)權(quán)限
mybatisPlusInterceptor.addInnerInterceptor(new DataFilterInterceptor());
// 分頁插件
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
// 樂觀鎖
mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
// 防止全表更新與刪除
mybatisPlusInterceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
return mybatisPlusInterceptor;
}
}基礎(chǔ)特性
- “零SQL” CRUD
- 核心點(diǎn):業(yè)務(wù)模塊的Mapper 接口繼承
BaseMapper<T>,即可擁有insert、selectById、updateById、deleteBatchIds等 17 個(gè)常用方法。 - Service CRUD:除了 Mapper 層,MP 還提供了
IService和ServiceImpl,封裝了更多的業(yè)務(wù)邏輯方法(如saveBatch批量插入)。 - 提供SqlHelper工具類實(shí)現(xiàn)批量寫入或更新操作
- 核心點(diǎn):業(yè)務(wù)模塊的Mapper 接口繼承
- 條件構(gòu)造器(Wrapper)
- 三板斧:
QueryWrapper(用于查詢,支持lambda避免字段名寫錯(cuò))、UpdateWrapper(用于更新,支持鏈?zhǔn)皆O(shè)置 set 值)。 - Lambda 表達(dá)式:推薦使用
LambdaQueryWrapper,利用方法引用(如User::getName)來指定字段,編譯期檢查,防止 SQL 拼寫錯(cuò)誤。
- 三板斧:
- 常用注解
@TableName:指定表名(如果類名與表名不一致)。@TableId:指定主鍵策略(如IdType.AUTO自增,IdType.ASSIGN_ID雪花算法)。@TableField:填充字段(如創(chuàng)建時(shí)間、更新時(shí)間自動(dòng)填充FieldFill.INSERT_UPDATE),或者邏輯刪除字段@TableLogic
增強(qiáng)功能
- 自動(dòng)填充
- 自動(dòng)填充:實(shí)現(xiàn)
MetaObjectHandler接口,重寫insertFill和updateFill方法,統(tǒng)一處理create_time、update_time等字段。
- 自動(dòng)填充:實(shí)現(xiàn)
- 邏輯刪除:配置后,
delete語句會(huì)自動(dòng)轉(zhuǎn)化為UPDATE語句(設(shè)置刪除標(biāo)記),查詢時(shí)自動(dòng)追加WHERE deleted = 0條件。 【在實(shí)體類中,對(duì)應(yīng)數(shù)據(jù)庫(kù)表的邏輯刪除字段上添加@TableLogic注解】- 插入:邏輯刪除字段的值不受限制。
- 查找:自動(dòng)添加條件,過濾掉標(biāo)記為已刪除的記錄。
- 更新:防止更新已刪除的記錄。
- 刪除:將刪除操作轉(zhuǎn)換為更新操作,標(biāo)記記錄為已刪除
- 自定義ID生成器
IdentifierGenerator主要用于生成數(shù)據(jù)庫(kù)表的主鍵IDKeyGenerator是MyBatis框架中的一個(gè)接口,用于在執(zhí)行SQL語句時(shí)生成鍵值,通常用于生成自增主鍵或者在執(zhí)行INSERT語句后獲取新生成的ID- MyBatis-Plus 內(nèi)置支持多種數(shù)據(jù)庫(kù)的主鍵生成策略,如:
- H2KeyGenerator
- OracleKeyGenerator
- PostgreKeyGenerator
- 多數(shù)據(jù)源支持:詳細(xì)使用參見https://github.com/baomidou/dynamic-datasource
- Spring Boot 1.5.x ~ 2.x.x 使用
dynamic-datasource-spring-boot-starter,支持 JDK 8 及以上版本 - Spring Boot 3.x.x 使用
dynamic-datasource-spring-boot3-starter,要求 JDK 17 及以上 - Spring Boot 4.x.x 使用
dynamic-datasource-spring-boot4-starter,要求 JDK 17 及以上
- Spring Boot 1.5.x ~ 2.x.x 使用
插件機(jī)制
常用插件
- 分頁插件(PaginationInnerInterceptor)
- 原理:MP 的分頁不是內(nèi)存分頁,而是物理分頁。它通過 MyBatis 的攔截器(Interceptor)機(jī)制,在 SQL 執(zhí)行前重寫 SQL(如加上
LIMIT)。 - 使用:配置
MybatisPlusInterceptor,注入PaginationInnerInterceptor。代碼中使用Page<T>對(duì)象接收結(jié)果。
- 原理:MP 的分頁不是內(nèi)存分頁,而是物理分頁。它通過 MyBatis 的攔截器(Interceptor)機(jī)制,在 SQL 執(zhí)行前重寫 SQL(如加上
- 樂觀鎖插件
OptimisticLockerInnerInterceptor- 讀取記錄時(shí),獲取當(dāng)前的版本號(hào)(version)。
- 在更新記錄時(shí),將這個(gè)版本號(hào)一同傳遞。
- 執(zhí)行更新操作時(shí),設(shè)置
version = newVersion的條件為version = oldVersion。 - 如果版本號(hào)不匹配,則更新失敗。
- 多租戶插件
TenantLineInnerInterceptor- 是 MyBatis-Plus 提供的一個(gè)插件,用于實(shí)現(xiàn)多租戶的數(shù)據(jù)隔離。通過這個(gè)插件,可以確保每個(gè)租戶只能訪問自己的數(shù)據(jù),從而實(shí)現(xiàn)數(shù)據(jù)的安全隔離
- 默認(rèn)插入 SQL 是需要判斷租戶條件,因此需要配合自動(dòng)填充字段功能填充租戶字段,否則租戶字段不會(huì)自動(dòng)保存到數(shù)據(jù)庫(kù)
- 非法SQL攔截插件
IllegalSQLInnerInterceptor- 用于攔截和檢查非法SQL語句。該插件旨在幫助開發(fā)者在SQL執(zhí)行前發(fā)現(xiàn)并解決潛在的安全問題,如全表更新、刪除操作,以及對(duì)索引的檢查等
- 防全表更新與刪除插件
BlockAttackInnerInterceptor- 專門用于防止惡意的全表更新和刪除操作。該插件通過攔截
update和delete語句,確保這些操作不會(huì)無意中影響到整個(gè)數(shù)據(jù)表,從而保護(hù)數(shù)據(jù)的完整性和安全性
- 專門用于防止惡意的全表更新和刪除操作。該插件通過攔截
執(zhí)行流程
MyBatis-Plus(MP)插件的執(zhí)行流程,本質(zhì)上是基于 MyBatis 的插件(Plugin)機(jī)制實(shí)現(xiàn)的。MP 利用這一機(jī)制,在 MyBatis 的核心執(zhí)行流程中“插入”自己的邏輯,從而實(shí)現(xiàn)分頁、性能分析、SQL 注入防護(hù)等功能。
MybatisPlusInterceptor 是“包工頭”,它實(shí)現(xiàn)了 MyBatis 的接口并攔下所有活;而 List<InnerInterceptor>是“工人”,實(shí)現(xiàn)具體的功能
| 組件層級(jí) | 類/接口名稱 | 職責(zé) | 實(shí)現(xiàn)關(guān)系 |
|---|---|---|---|
| 頂層門面 | MybatisPlusInterceptor | 實(shí)現(xiàn) MyBatis 原生 Interceptor,作為唯一入口,管理插件列表。 | 實(shí)現(xiàn)org.apache.ibatis.plugin.Interceptor |
| 插件容器 | List<InnerInterceptor> | 存儲(chǔ)具體的增強(qiáng)邏輯(如分頁、樂觀鎖),由頂層門面調(diào)用。 | 內(nèi)部持有 |
| 具體邏輯 | PaginationInnerInterceptor等 | 實(shí)現(xiàn)具體的業(yè)務(wù)邏輯(如重寫 SQL、計(jì)算耗時(shí))。 | 實(shí)現(xiàn)InnerInterceptor |
如何自定義SQL
通過如下注解,可以實(shí)現(xiàn)自定義SQL
@Select: 查詢@Insert: 插入@Update: 更新@Delete: 刪除
@Mapper
public interface UserMapper extends BaseMapper<User> {
// 簡(jiǎn)單查詢
@Select("SELECT * FROM user WHERE email = #{email}")
User selectByEmail(@Param("email") String email);
// 復(fù)雜一點(diǎn)的條件查詢
@Select("SELECT * FROM user WHERE status = #{status} AND age > #{minAge}")
List<User> selectByStatusAndAge(@Param("status") Integer status, @Param("minAge") Integer minAge);
}mybatis知識(shí)
# {}和${}的區(qū)別
#{}:是預(yù)編譯處理(PreparedStatement)。MyBatis 會(huì)將它替換為?,能有效防止 SQL 注入${}:是字符串替換。直接將變量拼接到 SQL 中,存在注入風(fēng)險(xiǎn),通常用于傳入數(shù)據(jù)庫(kù)表名或排序字段(ORDER BY)
MyBatis 的一級(jí)緩存和二級(jí)緩存
- 一級(jí)緩存: 作用域?yàn)?
SqlSession。在同一個(gè)會(huì)話中,相同的查詢不再查數(shù)據(jù)庫(kù)。默認(rèn)開啟。 - 二級(jí)緩存: 作用域?yàn)?
Namespace(多個(gè)會(huì)話間共享)。需要手動(dòng)開啟,且實(shí)體類必須實(shí)現(xiàn)序列化接口。 - 失效場(chǎng)景: 任何的
INSERT,UPDATE,DELETE操作都會(huì)刷新緩存
實(shí)體類屬性名和表中字段名不一致怎么辦?
- 在 SQL 語句中使用別名(
AS)。 - 使用
ResultMap進(jìn)行映射(最常用)。 - 開啟駝峰命名自動(dòng)轉(zhuǎn)換配置(
mapUnderscoreToCamelCase)
Mybatis的緩存機(jī)制
一級(jí)緩存建議保持默認(rèn)(開啟),而二級(jí)緩存通常不建議開啟(默認(rèn)也是關(guān)閉的),尤其是在復(fù)雜的業(yè)務(wù)或分布式系統(tǒng)中
一級(jí)緩存
一級(jí)緩存的實(shí)現(xiàn)參見:BaseExecutor、PerpetualCache
- 一級(jí)緩存的生命周期由
Executor(執(zhí)行器)管理。在BaseExecutor(所有執(zhí)行器的基類)中,定義了本地緩存對(duì)象 SqlSession每次創(chuàng)建都會(huì)生成一個(gè)新的Executor,所以一級(jí)緩存是線程隔離的【參見DefaultSqlSessionFactory】
二級(jí)緩存
二級(jí)緩存的實(shí)現(xiàn)參見:CachingExecutor、SynchronizedCache
當(dāng)開啟二級(jí)緩存后,MyBatis 會(huì)使用 CachingExecutor 包裝原來的 Executor
裝飾器模式: 二級(jí)緩存的實(shí)現(xiàn)非常優(yōu)雅,PerpetualCache 是基礎(chǔ),通過 SynchronizedCache(同步)、LoggingCache(日志)、ScheduledCache(定時(shí))、SerializedCache(序列化)、LruCache(回收策略)等裝飾器層層包裝,組合出最終需要的功能
| 特性 | 一級(jí)緩存 (Local) | 二級(jí)緩存 (Global) |
|---|---|---|
| 作用范圍 | 單個(gè) SqlSession 內(nèi) | 同一個(gè) Mapper 命名空間內(nèi) |
| 數(shù)據(jù)共享 | 獨(dú)享,不跨會(huì)話 | 共享,跨 SqlSession |
| 默認(rèn)狀態(tài) | 開啟 | 關(guān)閉 (需手動(dòng)配置) |
| 生命周期 | 伴隨 SqlSession 的創(chuàng)建與關(guān)閉 | 伴隨 SqlSessionFactory 的生命周期 |
| 清空時(shí)機(jī) | 增刪改操作、close()、commit() | 對(duì)應(yīng) Mapper 的增刪改操作、commit() |
是否開啟
| 緩存類型 | 建議配置 | 理由 | 適用場(chǎng)景 |
|---|---|---|---|
| 一級(jí)緩存 | 開啟 (默認(rèn)) | 安全、自動(dòng)管理、提升單次會(huì)話性能 | 所有場(chǎng)景(無需干預(yù)) |
| 二級(jí)緩存 | 關(guān)閉 (默認(rèn)) | 避免臟讀、避免分布式不一致 | 復(fù)雜業(yè)務(wù)、分布式系統(tǒng) |
| 二級(jí)緩存 | 開啟 (需評(píng)估) | 需處理好序列化和跨表失效問題 | 單機(jī)應(yīng)用、數(shù)據(jù)字典、純讀場(chǎng)景 |
一句話建議: 用好一級(jí)緩存,忘掉二級(jí)緩存,用 Redis 來做全局緩存
加載流程
MapperImpl方法調(diào)用過程
- 代理攔截(MybatisMapperProxy)
MybatisMapperProxy是在 Spring 容器進(jìn)行依賴注入(@Autowired)時(shí),由MapperProxyFactory調(diào)用getObject方法實(shí)例化的
- 命令執(zhí)行(MapperMethod):
MybatisMapperProxy會(huì)拿到一個(gè)MapperMethod實(shí)例。- 調(diào)用
mapperMethod.execute(sqlSession, args)。 - 此時(shí),控制權(quán)從 “代理層” 轉(zhuǎn)移到了 “命令層”
- SQL 執(zhí)行(SqlSession/Executor):
MapperMethod內(nèi)部會(huì)根據(jù) SQL 類型,調(diào)用sqlSession.selectOne()或insert()等方法。- 最終由
Executor去 JDBC 層面執(zhí)行 SQL
public class MapperRegistry {
private final Configuration config;
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new ConcurrentHashMap();
public MapperRegistry(Configuration config) {
this.config = config;
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
} else {
try {
return (T)mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
}
Spring boot啟動(dòng)時(shí)初始化過程
- SqlSessionFactoryp初始化
MybatisAutoConfiguration->MybatisSqlSessionFactoryBean->SqlSessionFactory
- Mapper 注冊(cè)與掃描
AutoConfiguredMapperScannerRegistrar->AutoConfiguredMapperScannerRegistrar->MapperScannerConfigurer->ClassPathMapperScanner->ClassPathBeanDefinitionScanner->MapperFactoryBean
- MybatisMapperProxy 實(shí)例化
- 獲取 Mapper Bean:
- 當(dāng) Spring 容器嘗試填充
@Autowired private UserMapper userMapper;時(shí)。 - 容器發(fā)現(xiàn)這是一個(gè)
FactoryBean,于是調(diào)用MapperFactoryBean.getObject()。
- 當(dāng) Spring 容器嘗試填充
- 獲取 SqlSession:
MapperFactoryBean.getObject()內(nèi)部會(huì)調(diào)用this.getSqlSession().getMapper(this.mapperInterface);。
- 最終實(shí)例化 (核心點(diǎn)):
sqlSession.getMapper()會(huì)委托給(T)this.getConfiguration().getMapper(type, this);。Configuration持有一個(gè)this.mapperRegistry.getMapper(type, sqlSession)(注冊(cè)中心)。MapperRegistry里存著Class -> MapperProxyFactory的映射
- 獲取 Mapper Bean:
public class MapperRegistry {
private final Configuration config;
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new ConcurrentHashMap();
public MapperRegistry(Configuration config) {
this.config = config;
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
} else {
try {
return (T)mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
}
到此這篇關(guān)于Spring boot 4 搞懂MyBatis-Plus的用法解析的文章就介紹到這了,更多相關(guān)Spring boot mybatis-plus用法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java設(shè)計(jì)模式之備忘錄模式_動(dòng)力節(jié)點(diǎn)Java學(xué)院
我們?cè)诰幊痰臅r(shí)候,經(jīng)常需要保存對(duì)象的中間狀態(tài),當(dāng)需要的時(shí)候,可以恢復(fù)到這個(gè)狀態(tài)。接下來通過本文給大家分享java設(shè)計(jì)模式之備忘錄模式,感興趣的的朋友一起看看吧2017-08-08
避免Java內(nèi)存泄漏的10個(gè)黃金法則詳細(xì)指南
在Java開發(fā)領(lǐng)域,內(nèi)存泄漏是一個(gè)經(jīng)久不衰的話題,也是導(dǎo)致應(yīng)用程序性能下降、崩潰甚至系統(tǒng)癱瘓的常見原因,下面我們就來看看避免Java內(nèi)存泄漏的10個(gè)黃金法則吧2025-07-07
SpringMVC請(qǐng)求/響應(yīng)亂碼問題解決方案解析
這篇文章主要介紹了SpringMVC請(qǐng)求/響應(yīng)亂碼問題解決方案解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12
springboot RESTful以及參數(shù)注解的使用方式
這篇文章主要介紹了springboot RESTful以及參數(shù)注解的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10
MyBatis?連接緩慢問題排查與解決實(shí)戰(zhàn)技巧
文章介紹了在維護(hù)一個(gè)老項(xiàng)目時(shí)遇到的MyBatis連接緩慢問題的排查過程和解決方案,本文分階段給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2025-12-12
java編程實(shí)現(xiàn)并查集的路徑壓縮代碼詳解
這篇文章主要介紹了java編程實(shí)現(xiàn)并查集的路徑壓縮代碼詳解,具有一定借鑒價(jià)值,需要的朋友可以參考。2017-11-11
詳解JAVA使用Comparator接口實(shí)現(xiàn)自定義排序
這篇文章主要介紹了JAVA使用Comparator接口實(shí)現(xiàn)自定義排序,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03
Spring Boot全局異常處理機(jī)制中DispatcherServlet的處理流程和作用
本文深入解析Spring Boot全局異常處理機(jī)制,重點(diǎn)剖析DispatcherServlet的核心作用及其異常處理流程,詳細(xì)介紹了HandlerExceptionResolver接口及其三個(gè)默認(rèn)實(shí)現(xiàn)類的執(zhí)行順序和工作原理,通過源碼分析,展示了Spring MVC如何將異常轉(zhuǎn)換為響應(yīng)2026-01-01

