mybatis-plus查詢源碼詳解
配置詳情
pom.xml
dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.1</version> </dependency>
mapper
public interface GenTableMapper extends BaseMapper<GenTable> { }
測試類
@RunWith(SpringRunner.class) @SpringBootTest(classes = GendemoApplication.class) public class BlockqueueTestDemo { @Autowired GenTableMapper genTableMapper; @Test public void test(){ List<GenTable> genTables = genTableMapper.selectList(new QueryWrapper<>()); } }
debug流程
1.發現 genTableMapper
是一個代理對象類型。
2.進入代理對象MybatisMapperProxy
, 調用其invoke
方法,方法的Class
類型為BaseMapper.selectList()
3.其中cachedInvoker()
方法會返回一個PlainMethodInvoker
,它重寫瞭MapperMethodInvoker
接口的invoke()
方法
4.最終會調用MybatisMapperMethod
的execute()
方法
public class MybatisMapperMethod { public Object execute(SqlSession sqlSession, Object[] args) { Object result; switch (command.getType()) { case INSERT: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.insert(command.getName(), param)); break; } case UPDATE: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.update(command.getName(), param)); break; } case DELETE: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.delete(command.getName(), param)); break; } case SELECT: if (method.returnsVoid() && method.hasResultHandler()) { executeWithResultHandler(sqlSession, args); result = null; } else if (method.returnsMany()) { result = executeForMany(sqlSession, args); } else if (method.returnsMap()) { result = executeForMap(sqlSession, args); } else if (method.returnsCursor()) { result = executeForCursor(sqlSession, args); } else { Object param = method.convertArgsToSqlCommandParam(args); // TODO 這裡下面改瞭 if (IPage.class.isAssignableFrom(method.getReturnType())) { result = executeForIPage(sqlSession, args); // TODO 這裡上面改瞭 } else { result = sqlSession.selectOne(command.getName(), param); if (method.returnsOptional() && (result == null || !method.getReturnType().equals(result.getClass()))) { result = Optional.ofNullable(result); } } } break; case FLUSH: result = sqlSession.flushStatements(); break; default: throw new BindingException("Unknown execution method for: " + command.getName()); } if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) { throw new BindingException("Mapper method '" + command.getName() + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ")."); } return result; } }
5.這是經過判斷會進入executeForMany(sqlSession, args)
方法,此時方法和參數都顯示出來瞭。sqlSession
的類型是SqlSessionTemplate
, 為什麼要註意這個 sqlSession
的類型?因為SqlSession
是一個接口,有很多實現類,有時候我們並不知道到底調用瞭哪個實現類的selectList()
方法,這個時候我們看類型就知道瞭,就可以進入SqlSessionTemplate
類,找到selectList()
打上斷點,debug就過來瞭。
6.利用同樣的方法,又調用瞭DefaultSqlSession
的selectList()
方法。
7.來到DefaultSqlSession
的selectList()
方法中,此時已經進入到mybatis
的源碼范圍瞭。executor
的類型是MybatisCachingExecutor
8.此時要註意MybatisCachingExecutor
代理類的handler
是一個Plugin
9.因為我使用到瞭分頁插件,所以會來到com.github.pagehelperPageInterceptor
中
10.由MybatisCachingExecutor
來執行查詢
11.MybatisCachingExecutor
委派 BaseExecutor
執行查詢
12.最終委派到PreparedStatementHandler
來處理
13.最後由DefaultResultSetHandler
來封裝結果集
@Override public List<Object> handleResultSets(Statement stmt) throws SQLException { ErrorContext.instance().activity("handling results").object(mappedStatement.getId()); final List<Object> multipleResults = new ArrayList<>(); int resultSetCount = 0; ResultSetWrapper rsw = getFirstResultSet(stmt); List<ResultMap> resultMaps = mappedStatement.getResultMaps(); int resultMapCount = resultMaps.size(); validateResultMapsCount(rsw, resultMapCount); while (rsw != null && resultMapCount > resultSetCount) { ResultMap resultMap = resultMaps.get(resultSetCount); handleResultSet(rsw, resultMap, multipleResults, null); rsw = getNextResultSet(stmt); cleanUpAfterHandlingResultSet(); resultSetCount++; } String[] resultSets = mappedStatement.getResultSets(); if (resultSets != null) { while (rsw != null && resultSetCount < resultSets.length) { ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]); if (parentMapping != null) { String nestedResultMapId = parentMapping.getNestedResultMapId(); ResultMap resultMap = configuration.getResultMap(nestedResultMapId); handleResultSet(rsw, resultMap, null, parentMapping); } rsw = getNextResultSet(stmt); cleanUpAfterHandlingResultSet(); resultSetCount++; } return collapseSingleResultList(multipleResults); }
總結
為什麼要一層一層的封裝?一層一層的委派?
這裡面和緩存有關,有興趣的可以自己瞭解一下。
下一篇打算記錄一下 mybatis-plus
的自動配置過程,有興趣的可以持續關註一下。
文中有錯誤的地方不吝賜教謝謝。
到此這篇關於mybatis-plus查詢源碼走讀的文章就介紹到這瞭,更多相關mybatis-plus查詢內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- MyBatis核心源碼深度剖析SQL語句執行過程
- mybatis plus框架@TableField註解不生效問題及解決方案
- 分析mybatis運行原理
- Mybatis結果集映射與生命周期詳細介紹
- mybatis plus動態數據源切換及查詢過程淺析