Spring boot 4 搞懂MyBatis-Plus的用法解析
MyBatis-Plus 是一個 MyBatis 的增強工具,在 MyBatis 的基礎(chǔ)上只做增強不做改變,為簡化開發(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
- 核心點:業(yè)務(wù)模塊的Mapper 接口繼承
BaseMapper<T>,即可擁有insert、selectById、updateById、deleteBatchIds等 17 個常用方法。 - Service CRUD:除了 Mapper 層,MP 還提供了
IService和ServiceImpl,封裝了更多的業(yè)務(wù)邏輯方法(如saveBatch批量插入)。 - 提供SqlHelper工具類實現(xiàn)批量寫入或更新操作
- 核心點:業(yè)務(wù)模塊的Mapper 接口繼承
- 條件構(gòu)造器(Wrapper)
- 三板斧:
QueryWrapper(用于查詢,支持lambda避免字段名寫錯)、UpdateWrapper(用于更新,支持鏈式設(shè)置 set 值)。 - Lambda 表達式:推薦使用
LambdaQueryWrapper,利用方法引用(如User::getName)來指定字段,編譯期檢查,防止 SQL 拼寫錯誤。
- 三板斧:
- 常用注解
@TableName:指定表名(如果類名與表名不一致)。@TableId:指定主鍵策略(如IdType.AUTO自增,IdType.ASSIGN_ID雪花算法)。@TableField:填充字段(如創(chuàng)建時間、更新時間自動填充FieldFill.INSERT_UPDATE),或者邏輯刪除字段@TableLogic
增強功能
- 自動填充
- 自動填充:實現(xiàn)
MetaObjectHandler接口,重寫insertFill和updateFill方法,統(tǒng)一處理create_time、update_time等字段。
- 自動填充:實現(xiàn)
- 邏輯刪除:配置后,
delete語句會自動轉(zhuǎn)化為UPDATE語句(設(shè)置刪除標記),查詢時自動追加WHERE deleted = 0條件。 【在實體類中,對應(yīng)數(shù)據(jù)庫表的邏輯刪除字段上添加@TableLogic注解】- 插入:邏輯刪除字段的值不受限制。
- 查找:自動添加條件,過濾掉標記為已刪除的記錄。
- 更新:防止更新已刪除的記錄。
- 刪除:將刪除操作轉(zhuǎn)換為更新操作,標記記錄為已刪除
- 自定義ID生成器
IdentifierGenerator主要用于生成數(shù)據(jù)庫表的主鍵IDKeyGenerator是MyBatis框架中的一個接口,用于在執(zhí)行SQL語句時生成鍵值,通常用于生成自增主鍵或者在執(zhí)行INSERT語句后獲取新生成的ID- MyBatis-Plus 內(nèi)置支持多種數(shù)據(jù)庫的主鍵生成策略,如:
- H2KeyGenerator
- OracleKeyGenerator
- PostgreKeyGenerator
- 多數(shù)據(jù)源支持:詳細使用參見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 使用
插件機制
常用插件
- 分頁插件(PaginationInnerInterceptor)
- 原理:MP 的分頁不是內(nèi)存分頁,而是物理分頁。它通過 MyBatis 的攔截器(Interceptor)機制,在 SQL 執(zhí)行前重寫 SQL(如加上
LIMIT)。 - 使用:配置
MybatisPlusInterceptor,注入PaginationInnerInterceptor。代碼中使用Page<T>對象接收結(jié)果。
- 原理:MP 的分頁不是內(nèi)存分頁,而是物理分頁。它通過 MyBatis 的攔截器(Interceptor)機制,在 SQL 執(zhí)行前重寫 SQL(如加上
- 樂觀鎖插件
OptimisticLockerInnerInterceptor- 讀取記錄時,獲取當前的版本號(version)。
- 在更新記錄時,將這個版本號一同傳遞。
- 執(zhí)行更新操作時,設(shè)置
version = newVersion的條件為version = oldVersion。 - 如果版本號不匹配,則更新失敗。
- 多租戶插件
TenantLineInnerInterceptor- 是 MyBatis-Plus 提供的一個插件,用于實現(xiàn)多租戶的數(shù)據(jù)隔離。通過這個插件,可以確保每個租戶只能訪問自己的數(shù)據(jù),從而實現(xiàn)數(shù)據(jù)的安全隔離
- 默認插入 SQL 是需要判斷租戶條件,因此需要配合自動填充字段功能填充租戶字段,否則租戶字段不會自動保存到數(shù)據(jù)庫
- 非法SQL攔截插件
IllegalSQLInnerInterceptor- 用于攔截和檢查非法SQL語句。該插件旨在幫助開發(fā)者在SQL執(zhí)行前發(fā)現(xiàn)并解決潛在的安全問題,如全表更新、刪除操作,以及對索引的檢查等
- 防全表更新與刪除插件
BlockAttackInnerInterceptor- 專門用于防止惡意的全表更新和刪除操作。該插件通過攔截
update和delete語句,確保這些操作不會無意中影響到整個數(shù)據(jù)表,從而保護數(shù)據(jù)的完整性和安全性
- 專門用于防止惡意的全表更新和刪除操作。該插件通過攔截
執(zhí)行流程
MyBatis-Plus(MP)插件的執(zhí)行流程,本質(zhì)上是基于 MyBatis 的插件(Plugin)機制實現(xiàn)的。MP 利用這一機制,在 MyBatis 的核心執(zhí)行流程中“插入”自己的邏輯,從而實現(xiàn)分頁、性能分析、SQL 注入防護等功能。
MybatisPlusInterceptor 是“包工頭”,它實現(xiàn)了 MyBatis 的接口并攔下所有活;而 List<InnerInterceptor>是“工人”,實現(xiàn)具體的功能
| 組件層級 | 類/接口名稱 | 職責 | 實現(xiàn)關(guān)系 |
|---|---|---|---|
| 頂層門面 | MybatisPlusInterceptor | 實現(xiàn) MyBatis 原生 Interceptor,作為唯一入口,管理插件列表。 | 實現(xiàn)org.apache.ibatis.plugin.Interceptor |
| 插件容器 | List<InnerInterceptor> | 存儲具體的增強邏輯(如分頁、樂觀鎖),由頂層門面調(diào)用。 | 內(nèi)部持有 |
| 具體邏輯 | PaginationInnerInterceptor等 | 實現(xiàn)具體的業(yè)務(wù)邏輯(如重寫 SQL、計算耗時)。 | 實現(xiàn)InnerInterceptor |
如何自定義SQL
通過如下注解,可以實現(xiàn)自定義SQL
@Select: 查詢@Insert: 插入@Update: 更新@Delete: 刪除
@Mapper
public interface UserMapper extends BaseMapper<User> {
// 簡單查詢
@Select("SELECT * FROM user WHERE email = #{email}")
User selectByEmail(@Param("email") String email);
// 復(fù)雜一點的條件查詢
@Select("SELECT * FROM user WHERE status = #{status} AND age > #{minAge}")
List<User> selectByStatusAndAge(@Param("status") Integer status, @Param("minAge") Integer minAge);
}mybatis知識
# {}和${}的區(qū)別
#{}:是預(yù)編譯處理(PreparedStatement)。MyBatis 會將它替換為?,能有效防止 SQL 注入${}:是字符串替換。直接將變量拼接到 SQL 中,存在注入風險,通常用于傳入數(shù)據(jù)庫表名或排序字段(ORDER BY)
MyBatis 的一級緩存和二級緩存
- 一級緩存: 作用域為
SqlSession。在同一個會話中,相同的查詢不再查數(shù)據(jù)庫。默認開啟。 - 二級緩存: 作用域為
Namespace(多個會話間共享)。需要手動開啟,且實體類必須實現(xiàn)序列化接口。 - 失效場景: 任何的
INSERT,UPDATE,DELETE操作都會刷新緩存
實體類屬性名和表中字段名不一致怎么辦?
- 在 SQL 語句中使用別名(
AS)。 - 使用
ResultMap進行映射(最常用)。 - 開啟駝峰命名自動轉(zhuǎn)換配置(
mapUnderscoreToCamelCase)
Mybatis的緩存機制
一級緩存建議保持默認(開啟),而二級緩存通常不建議開啟(默認也是關(guān)閉的),尤其是在復(fù)雜的業(yè)務(wù)或分布式系統(tǒng)中
一級緩存
一級緩存的實現(xiàn)參見:BaseExecutor、PerpetualCache
- 一級緩存的生命周期由
Executor(執(zhí)行器)管理。在BaseExecutor(所有執(zhí)行器的基類)中,定義了本地緩存對象 SqlSession每次創(chuàng)建都會生成一個新的Executor,所以一級緩存是線程隔離的【參見DefaultSqlSessionFactory】
二級緩存
二級緩存的實現(xiàn)參見:CachingExecutor、SynchronizedCache
當開啟二級緩存后,MyBatis 會使用 CachingExecutor 包裝原來的 Executor
裝飾器模式: 二級緩存的實現(xiàn)非常優(yōu)雅,PerpetualCache 是基礎(chǔ),通過 SynchronizedCache(同步)、LoggingCache(日志)、ScheduledCache(定時)、SerializedCache(序列化)、LruCache(回收策略)等裝飾器層層包裝,組合出最終需要的功能
| 特性 | 一級緩存 (Local) | 二級緩存 (Global) |
|---|---|---|
| 作用范圍 | 單個 SqlSession 內(nèi) | 同一個 Mapper 命名空間內(nèi) |
| 數(shù)據(jù)共享 | 獨享,不跨會話 | 共享,跨 SqlSession |
| 默認狀態(tài) | 開啟 | 關(guān)閉 (需手動配置) |
| 生命周期 | 伴隨 SqlSession 的創(chuàng)建與關(guān)閉 | 伴隨 SqlSessionFactory 的生命周期 |
| 清空時機 | 增刪改操作、close()、commit() | 對應(yīng) Mapper 的增刪改操作、commit() |
是否開啟
| 緩存類型 | 建議配置 | 理由 | 適用場景 |
|---|---|---|---|
| 一級緩存 | 開啟 (默認) | 安全、自動管理、提升單次會話性能 | 所有場景(無需干預(yù)) |
| 二級緩存 | 關(guān)閉 (默認) | 避免臟讀、避免分布式不一致 | 復(fù)雜業(yè)務(wù)、分布式系統(tǒng) |
| 二級緩存 | 開啟 (需評估) | 需處理好序列化和跨表失效問題 | 單機應(yīng)用、數(shù)據(jù)字典、純讀場景 |
一句話建議: 用好一級緩存,忘掉二級緩存,用 Redis 來做全局緩存
加載流程
MapperImpl方法調(diào)用過程
- 代理攔截(MybatisMapperProxy)
MybatisMapperProxy是在 Spring 容器進行依賴注入(@Autowired)時,由MapperProxyFactory調(diào)用getObject方法實例化的
- 命令執(zhí)行(MapperMethod):
MybatisMapperProxy會拿到一個MapperMethod實例。- 調(diào)用
mapperMethod.execute(sqlSession, args)。 - 此時,控制權(quán)從 “代理層” 轉(zhuǎn)移到了 “命令層”
- SQL 執(zhí)行(SqlSession/Executor):
MapperMethod內(nèi)部會根據(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啟動時初始化過程
- SqlSessionFactoryp初始化
MybatisAutoConfiguration->MybatisSqlSessionFactoryBean->SqlSessionFactory
- Mapper 注冊與掃描
AutoConfiguredMapperScannerRegistrar->AutoConfiguredMapperScannerRegistrar->MapperScannerConfigurer->ClassPathMapperScanner->ClassPathBeanDefinitionScanner->MapperFactoryBean
- MybatisMapperProxy 實例化
- 獲取 Mapper Bean:
- 當 Spring 容器嘗試填充
@Autowired private UserMapper userMapper;時。 - 容器發(fā)現(xiàn)這是一個
FactoryBean,于是調(diào)用MapperFactoryBean.getObject()。
- 當 Spring 容器嘗試填充
- 獲取 SqlSession:
MapperFactoryBean.getObject()內(nèi)部會調(diào)用this.getSqlSession().getMapper(this.mapperInterface);。
- 最終實例化 (核心點):
sqlSession.getMapper()會委托給(T)this.getConfiguration().getMapper(type, this);。Configuration持有一個this.mapperRegistry.getMapper(type, sqlSession)(注冊中心)。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)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot如何導(dǎo)出Jar包并測試(使用IDEA)
這篇文章主要介紹了SpringBoot如何導(dǎo)出Jar包并測試(使用IDEA),具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-07-07
IDEA如何實現(xiàn)遠程斷點調(diào)試jar包
這篇文章主要介紹了IDEA如何實現(xiàn)遠程斷點調(diào)試jar包的問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2025-06-06
PostConstruct注解標記類ApplicationContext未加載空指針
這篇文章主要為大家介紹了@PostConstruct注解標記類ApplicationContext未加載空指針示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-11-11

