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-10java使用apache.poi導(dǎo)出word文件的示例代碼
這篇文章主要介紹了java使用apache.poi導(dǎo)出word文件,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-07-07springcloud如何獲取網(wǎng)關(guān)封裝的頭部信息
這篇文章主要介紹了springcloud獲取網(wǎng)關(guān)封裝的頭部信息,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-06-06Maven編譯Fatal?error?compiling:無效的目標(biāo)發(fā)行版:11問題及解決
在Java11中編譯Springboot工程時遇到問題,解決方法是在pom.xml文件中指定Maven的Java編譯器版本,可以使用MavenJava編譯器屬性或插件,在Java9及以后的版本中,也要使用插件并設(shè)置release屬性2024-12-12Java運(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