MyBatis-Plus基于MyBatis封裝 BaseMapper的流程步驟
引言
MyBatis-Plus(簡稱 MP)作為 MyBatis 的增強(qiáng)框架,通過 BaseMapper 提供了通用的 CRUD 操作,極大地提升了開發(fā)效率。為了更透徹地理解其封裝機(jī)制,本文將采用鏈路追蹤的思維,從開發(fā)者調(diào)用接口開始,逐步深入到 MyBatis-Plus 的核心實(shí)現(xiàn),分析其如何基于 MyBatis 完成對 BaseMapper 的封裝。
一、從調(diào)用開始:BaseMapper 的使用場景
假設(shè)我們有一個簡單的實(shí)體類 User 和對應(yīng)的 Mapper 接口:
@TableName("t_user")
public class User {
@TableId
private Long id;
private String name;
private Integer age;
// getter 和 setter 省略
}
public interface UserMapper extends BaseMapper<User> {
}
開發(fā)者只需這樣調(diào)用:
UserMapper userMapper = sqlSession.getMapper(UserMapper.class); User user = userMapper.selectById(1L);
表面上看,selectById 是 BaseMapper 提供的方法,但其背后是如何實(shí)現(xiàn)的呢?讓我們沿著調(diào)用鏈路逐步追蹤。
二、鏈路追蹤:從接口調(diào)用到代理執(zhí)行
1. 獲取 Mapper 代理對象
當(dāng)調(diào)用 sqlSession.getMapper(UserMapper.class) 時,MyBatis 的 SqlSession 會委托給 Configuration 的 getMapper 方法:
// MyBatis: org.apache.ibatis.session.Configuration
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
這里的 mapperRegistry 是 MyBatis 的 MapperRegistry 類,但在 MyBatis-Plus 中被替換為 MybatisMapperRegistry。鏈路進(jìn)入 MyBatis-Plus 的自定義實(shí)現(xiàn):
// MyBatis-Plus: com.baomidou.mybatisplus.core.MybatisMapperRegistry
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
return mapperProxyFactory.newInstance(sqlSession);
}
- 關(guān)鍵點(diǎn):MyBatis-Plus 重寫了
MapperRegistry,并在啟動時通過掃描將所有繼承BaseMapper的接口注冊到knownMappers中。 - 代理生成:
newInstance方法創(chuàng)建了一個MapperProxy對象,這是 JDK 動態(tài)代理的實(shí)現(xiàn)。
2. 方法調(diào)用攔截
當(dāng)調(diào)用 userMapper.selectById(1L) 時,代理對象 MapperProxy 的 invoke 方法被觸發(fā):
// MyBatis-Plus: com.baomidou.mybatisplus.core.override.MybatisMapperProxy
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
MybatisMapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
- 鏈路分叉:
cachedMapperMethod會根據(jù)方法簽名緩存并返回一個MybatisMapperMethod對象。 - 執(zhí)行邏輯:
execute方法根據(jù)方法名和參數(shù)決定具體的執(zhí)行路徑。
對于 selectById,鏈路進(jìn)入 MybatisMapperMethod.execute:
// MyBatis-Plus: com.baomidou.mybatisplus.core.override.MybatisMapperMethod
public Object execute(SqlSession sqlSession, Object[] args) {
if (SqlCommandType.SELECT == command.getType()) {
if (method.getReturnType().isAssignableFrom(List.class)) {
return sqlSession.selectList(command.getName(), args);
}
return sqlSession.selectOne(command.getName(), args[0]);
}
// 其他類型如 INSERT、UPDATE 等略
}
- 關(guān)鍵點(diǎn):
command.getName()返回的是一個全局唯一的 SQL ID,例如com.baomidou.mybatisplus.core.mapper.BaseMapper.selectById。 - 執(zhí)行 SQL:最終調(diào)用 MyBatis 的
selectOne方法執(zhí)行查詢。
三、SQL 注入:BaseMapper 方法的實(shí)現(xiàn)來源
問題來了:selectById 的 SQL 是從哪里來的?答案在于 MyBatis-Plus 的 SQL 注入機(jī)制。
1. 啟動時的 SQL 注入
MyBatis-Plus 在 Spring 容器初始化時,通過 MapperScannerConfigurer 掃描 Mapper 接口,并調(diào)用 ISqlInjector 注入通用 SQL:
// MyBatis-Plus: com.baomidou.mybatisplus.core.injector.DefaultSqlInjector
public void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {
List<AbstractMethod> methodList = getMethodList(mapperClass);
for (AbstractMethod method : methodList) {
method.inject(builderAssistant, mapperClass);
}
}
- 方法列表:
getMethodList返回BaseMapper中定義的所有方法(如selectById、insert等)的實(shí)現(xiàn)類,例如SelectById。 - 注入過程:以
SelectById為例:
// MyBatis-Plus: com.baomidou.mybatisplus.core.injector.methods.SelectById
public void injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
String sql = String.format("<script>SELECT %s FROM %s WHERE %s = #{id}</script>",
sqlSelectColumns(), tableInfo.getTableName(), tableInfo.getKeyColumn());
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
addSelectMappedStatement(mapperClass, "selectById", sqlSource, modelClass, tableInfo);
}
- SQL 生成:根據(jù)
TableInfo(通過反射解析實(shí)體類)動態(tài)生成SELECT * FROM t_user WHERE id = #{id}。 - 注冊:將生成的
MappedStatement注冊到 MyBatis 的Configuration中。
2. TableInfo 的作用
TableInfo 是 MyBatis-Plus 的核心元數(shù)據(jù)類,通過 TableInfoHelper 在啟動時解析實(shí)體類:
@TableName("t_user")→ 表名t_user。@TableId→ 主鍵字段id。- 字段映射 → 自動推斷
name、age等列名。
這些信息為 SQL 注入提供了基礎(chǔ)數(shù)據(jù)。
四、鏈路總結(jié):從調(diào)用到執(zhí)行的全流程
- 開發(fā)者調(diào)用:
userMapper.selectById(1L)。 - 代理攔截:
MybatisMapperProxy.invoke→MybatisMapperMethod.execute。 - SQL 執(zhí)行:
sqlSession.selectOne→ MyBatis 執(zhí)行預(yù)注入的MappedStatement。 - 結(jié)果映射:MyBatis 將查詢結(jié)果映射為
User對象返回。
背后支持:
- 啟動時:掃描 Mapper → 注入通用 SQL → 注冊
MappedStatement。 - 運(yùn)行時:動態(tài)代理 → 方法路由 → SQL 執(zhí)行。
五、與 MyBatis 的協(xié)作與增強(qiáng)
- 復(fù)用 MyBatis:動態(tài)代理、
SqlSession、結(jié)果映射等核心機(jī)制完全繼承自 MyBatis。 - 增強(qiáng)點(diǎn):
SQL 自動化:通過
ISqlInjector注入通用 SQL。元數(shù)據(jù)管理:
TableInfo實(shí)現(xiàn)實(shí)體與表的自動映射。條件構(gòu)造:
Wrapper擴(kuò)展了動態(tài)查詢能力。
六、結(jié)論
MyBatis-Plus 對 BaseMapper 的封裝,是在 MyBatis 動態(tài)代理和 SQL 執(zhí)行框架上的巧妙擴(kuò)展。通過啟動時的 SQL 注入和運(yùn)行時的代理攔截,它實(shí)現(xiàn)了通用 CRUD 的零配置使用。鏈路追蹤顯示,這種設(shè)計既保留了 MyBatis 的靈活性,又通過自動化大幅提升了開發(fā)效率,堪稱對 MyBatis 的“完美補(bǔ)完”。
以上就是MyBatis-Plus基于MyBatis封裝 BaseMapper的流程步驟的詳細(xì)內(nèi)容,更多關(guān)于MyBatis-Plus封裝 BaseMapper的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
淺談springboot內(nèi)置tomcat和外部獨(dú)立部署tomcat的區(qū)別
這篇文章主要介紹了淺談springboot內(nèi)置tomcat和外部獨(dú)立部署tomcat的區(qū)別,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-10-10
java使用apache.poi導(dǎo)出word文件的示例代碼
這篇文章主要介紹了java使用apache.poi導(dǎo)出word文件,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-07-07
springcloud如何獲取網(wǎng)關(guān)封裝的頭部信息
這篇文章主要介紹了springcloud獲取網(wǎng)關(guān)封裝的頭部信息,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-06-06
Maven編譯Fatal?error?compiling:無效的目標(biāo)發(fā)行版:11問題及解決
在Java11中編譯Springboot工程時遇到問題,解決方法是在pom.xml文件中指定Maven的Java編譯器版本,可以使用MavenJava編譯器屬性或插件,在Java9及以后的版本中,也要使用插件并設(shè)置release屬性2024-12-12
Java運(yùn)行Python腳本的幾種方式小結(jié)
在跨語言編程中,有時候我們需要在 Java 應(yīng)用程序中執(zhí)行 Python 腳本,這可能是為了利用 Python 豐富的庫生態(tài)系統(tǒng),或者是為了在已有 Java 系統(tǒng)中集成一些 Python 特有的功能,本文給大家介紹了實(shí)現(xiàn)這幾種目標(biāo)的方法,需要的朋友可以參考下2024-12-12

