MyBatis核心源碼深度剖析SQL語句執行過程
1 SQL語句的執行過程介紹
MyBatis核心執行組件:
2 SQL執行的入口分析
2.1 為Mapper接口創建代理對象
// 方式1: User user = session.selectOne("com.oldlu.dao.UserMapper.findUserById", 101); // 方式2: UserMapper mapper = session.getMapper(UserMapper.class); List<User> userList = mapper.findAll();
2.2 執行代理邏輯
方式1入口分析:
session是DefaultSqlSession類型的,因為sqlSessionFactory默認生成的SqlSession是
DefaultSqlSession類型。
selectOne()會調用selectList()。
// DefaultSqlSession類 public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { try { MappedStatement ms = configuration.getMappedStatement(statement); // CURD操作是交給Excetor去處理的 return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
方式2入口分析:
獲取代理對象:
//DefaultSqlSession類 ====================> @Override public <T> T getMapper(Class<T> type) { return configuration.getMapper(type, this); } // Configuration類 ====================> public <T> T getMapper(Class<T> type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); } //MapperRegistry ----> apperProxyFactory.newInstance ====================> public <T> T getMapper(Class<T> type, SqlSession sqlSession) { //從緩存中獲取該Mapper接口的代理工廠對象 final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type); //如果該Mapper接口沒有註冊過,則拋異常 if (mapperProxyFactory == null) { throw new BindingException("Type " + type + " is not known to the MapperRegistry."); } try { //【使用代理工廠創建Mapper接口的代理對象】 return mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) { throw new BindingException("Error getting mapper instance. Cause: " + e, e); } } //MapperProxyFactory --->此時生成代理對象 ====================> protected T newInstance(MapperProxy<T> mapperProxy) { //Mybatis底層是調用JDK的Proxy類來創建代理實例 return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } public T newInstance(SqlSession sqlSession) { final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); }
代理對象執行邏輯:
//MapperProxy ====================> /**代理對象執行的方法,代理以後,所有Mapper的方法調用時,都會調用這個invoke方法*/ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { if (Object.class.equals(method.getDeclaringClass())) { //如果是Object方法,則調用方法本身 return method.invoke(this, args); } else { //調用接口方法:根據被調用接口的Method對象,從緩存中獲取MapperMethodInvoker對象 //apper接口中的每一個方法都對應一個MapperMethodInvoker對象,而MapperMethodInvoker 對象裡面的MapperMethod保存著對應的SQL信息和返回類型以完成SQL調用 ... return cachedInvoker(method).invoke(proxy, method, args, sqlSession); } } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } /** 獲取緩存中MapperMethodInvoker,如果沒有則創建一個,而MapperMethodInvoker內部封裝這一 個MethodHandler */ private MapperMethodInvoker cachedInvoker(Method method) throws Throwable { try { return methodCache.computeIfAbsent(method, m -> { if (m.isDefault()) { //如果調用接口的是默認方法(default方法) try { if (privateLookupInMethod == null) { return new DefaultMethodInvoker(getMethodHandleJava8(method)); } else { return new DefaultMethodInvoker(getMethodHandleJava9(method)); } } catch (IllegalAccessException | InstantiationException | InvocationTargetException | NoSuchMethodException e) { throw new RuntimeException(e); } } else { //如果調用的普通方法(非default方法),則創建一個PlainMethodInvoker並放 入緩存,其中MapperMethod保存對應接口方法的SQL以及入參和出參的數據類型等信息 return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration())); } }); } catch (RuntimeException re) { Throwable cause = re.getCause(); throw cause == null ? re : cause; } } // MapperProxy內部類: PainMethodInvoker ====================> // 當cacheInvoker返回瞭PalinMethodInvoker實例之後,緊接著調用瞭這個實例的 PlainMethodInvoker:invoke方法 @Override public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable { //Mybatis實現接口方法的核心: MapperMethod::execute方法: return mapperMethod.execute(sqlSession, args); } // MapperMethod ====================> public Object execute(SqlSession sqlSession, Object[] args) { Object result; switch (command.getType()) { case INSERT: { // 將args進行解析,如果是多個參數則,則根據@Param註解指定名稱將參數轉換為Map, 如果是封裝實體則不轉換 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 { //解析參數,因為SqlSession::selectOne方法參數隻能傳入一個,但是我們 Mapper中可能傳入多個參數, //有可能是通過@Param註解指定參數名,所以這裡需要將Mapper接口方法中的多個參 數轉化為一個ParamMap, //也就是說如果是傳入的單個封裝實體,那麼直接返回出來;如果傳入的是多個參數, 實際上都轉換成瞭Map Object param = method.convertArgsToSqlCommandParam(args); //可以看到動態代理最後還是使用SqlSession操作數據庫的 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; } // 此時我們發現: 回到瞭sqlsession中 private <E> Object executeForMany(SqlSession sqlSession, Object[] args) { List<E> result; Object param = method.convertArgsToSqlCommandParam(args); if (method.hasRowBounds()) { RowBounds rowBounds = method.extractRowBounds(args); result = sqlSession.selectList(command.getName(), param, rowBounds); } else { result = sqlSession.selectList(command.getName(), param); } // ... return result; }
3 查詢語句的執行過程分析
3.1 selectOne方法分析
// DefaultSqlSession類 ===============> // selectOne @Override public <T> T selectOne(String statement, Object parameter) { // //selectOne()會調用selectList()。 List<T> list = this.selectList(statement, parameter); if (list.size() == 1) { return list.get(0); } else if (list.size() > 1) { throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size()); } else { return null; } } // selectList public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { try { MappedStatement ms = configuration.getMappedStatement(statement); // CURD操作是交給Excetor去處理的 return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
3.2 sql獲取
// CachingExecutor ===============> public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { // 獲取綁定的sql命令,比如"SELECT * FROM xxx" BoundSql boundSql = ms.getBoundSql(parameterObject); CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql); return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); } @Override public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { Cache cache = ms.getCache(); if (cache != null) { flushCacheIfRequired(ms); if (ms.isUseCache() && resultHandler == null) { ensureNoOutParams(ms, boundSql); @SuppressWarnings("unchecked") List<E> list = (List<E>) tcm.getObject(cache, key); if (list == null) { list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); tcm.putObject(cache, key, list); // issue #578 and #116 } return list; } } return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); } //真正執行query操作的是SimplyExecutor代理來完成的,SimplyExecutor的父類BaseExecutor的 query方法中: // BaseExecutor類:SimplyExecutor的父類 =================> @Override public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId()); if (closed) { throw new ExecutorException("Executor was closed."); } if (queryStack == 0 && ms.isFlushCacheRequired()) { clearLocalCache(); } List<E> list; try { queryStack++; //localCache是一級緩存,如果找不到就調用queryFromDatabase從數據庫中查找 list = resultHandler == null ? (List<E>) localCache.getObject(key) : null; if (list != null) { handleLocallyCachedOutputParameters(ms, key, parameter, boundSql); } else { list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); } } finally { queryStack--; } if (queryStack == 0) { for (DeferredLoad deferredLoad : deferredLoads) { deferredLoad.load(); } deferredLoads.clear(); if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) { clearLocalCache(); } } return list; } //第一次,沒有緩存,所以會調用queryFromDatabase方法來執行查詢。 private <E> List<E> queryFromDatabase(...) throws SQLException { List<E> list; localCache.putObject(key, EXECUTION_PLACEHOLDER); try { // 查詢 list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql); } finally { localCache.removeObject(key); } localCache.putObject(key, list); if (ms.getStatementType() == StatementType.CALLABLE) { localOutputParameterCache.putObject(key, parameter); } return list; } // SimpleExecutor類 ============================> public <E> List<E> doQuery(...) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); StatementHandler handler = configuration.newStatementHandler(....); // 1:SQL查詢參數的設置 stmt = prepareStatement(handler, ms.getStatementLog()); // StatementHandler封裝瞭Statement // 2:SQL查詢操作和結果集的封裝 return handler.<E>query(stmt); } finally { closeStatement(stmt); } }
3.3 參數設置
// SimplyExecutor類 ============================> // 【1】 參數設置: prepareStatement private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; // 通過getConnection方法來獲取一個Connection, Connection connection = getConnection(statementLog); // 調用prepare方法來獲取一個Statement stmt = handler.prepare(connection, transaction.getTimeout()); // 設置SQL查詢中的參數值 *** handler.parameterize(stmt); return stmt; } // RoutingStatementHandler ============================> // PreparedStatementHandler ============================> @Override public void parameterize(Statement statement) throws SQLException { parameterHandler.setParameters((PreparedStatement) statement); } // DefaultParameterHandler ============================> 此時參數設置成功 @Override public void setParameters(PreparedStatement ps) { ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId()); List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); if (parameterMappings != null) { for (int i = 0; i < parameterMappings.size(); i++) { ParameterMapping parameterMapping = parameterMappings.get(i); if (parameterMapping.getMode() != ParameterMode.OUT) { Object value; String propertyName = parameterMapping.getProperty(); if (boundSql.hasAdditionalParameter(propertyName)) { value = boundSql.getAdditionalParameter(propertyName); } else if (parameterObject == null) { value = null; } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { value = parameterObject; } else { MetaObject metaObject = configuration.newMetaObject(parameterObject); value = metaObject.getValue(propertyName); } TypeHandler typeHandler = parameterMapping.getTypeHandler(); JdbcType jdbcType = parameterMapping.getJdbcType(); if (value == null && jdbcType == null) { jdbcType = configuration.getJdbcTypeForNull(); } try { typeHandler.setParameter(ps, i + 1, value, jdbcType); } catch (TypeException | SQLException e) { throw new TypeException("Could not set parameters for mapping....."); } } } } }
3.4 SQL執行和結果集的封裝
// RoutingStatementHandler ============================> @Override public <E> List<E> query(Statement statement) throws SQLException { return delegate.<E>query(statement); } // PreparedStatementHandler ============================> @Override public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { // 這裡就到瞭熟悉的PreparedStatement瞭 PreparedStatement ps = (PreparedStatement) statement; // 執行SQL查詢操作 ps.execute(); // 結果交給ResultHandler來處理 return resultSetHandler.<E> handleResultSets(ps); } // DefaultResultSetHandler類(封裝返回值,將查詢結果封裝成Object對象) @Override public List<Object> handleResultSets(Statement stmt) throws SQLException { ErrorContext.instance().activity("handling results").object(mappedStatement.getId()); final List<Object> multipleResults = new ArrayList<Object>(); 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); }
4 更新語句的執行過程分析
- xecutor 的 update 方法分析
- insert、update 和 delete 操作都會清空一二級緩存
- doUpdate 方法
- PreparedStatementHandler 的 update 方法
- 默認是創建PreparedStatementHandler,然後執行prepareStatement方法。
- 執行結果為受影響行數
- 執行更新語句的SQL
4.1 sqlsession增刪改方法分析
// DefaultSqlSession ===============> @Override public int insert(...) { return update(statement, parameter); } @Override public int update(String statement) { return update(statement, null); } @Override public int delete(...) { return update(....); } // insert 、delete操作是通過調用update語句進行的相關邏輯 @Override public int update(String statement, Object parameter) { try { dirty = true; MappedStatement ms = configuration.getMappedStatement(statement); // 增刪改 最終底層都是 update return executor.update(ms, wrapCollection(parameter)); } catch (Exception e) { throw ExceptionFactory.wrapException("Error updating database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
4.2 sql獲取
// CachingExecutor ===============> @Override public int update(MappedStatement ms, Object parameterObject) throws SQLException { // 執行增刪改,清除緩存 flushCacheIfRequired(ms); // 跳轉BaseExecutor return delegate.update(ms, parameterObject); } // BaseExecutor ===============> @Override public int update(MappedStatement ms, Object parameter) throws SQLException { ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId()); if (closed) { throw new ExecutorException("Executor was closed."); } // 清除 LocalCache 一級緩存 clearLocalCache(); //執行 doUpdate return doUpdate(ms, parameter); } // SimpleExecutor ===============> // doUpdate @Override public int doUpdate(MappedStatement ms, Object parameter) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); StatementHandler handler = configuration.newStatementHandler(...); // 【1】.獲取statement,並進行參數映射 stmt = prepareStatement(handler, ms.getStatementLog()); // 【2】.handler.update()方法執行具體sql指令 return handler.update(stmt); } finally { closeStatement(stmt); } }
4.3 參數設置
// SimplyExecutor類 ============================> //【1】 prepareStatement private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; Connection connection = getConnection(statementLog); // 使用connection對象信息創建statement,並將超時時間綁定 stmt = handler.prepare(connection, transaction.getTimeout()); // parameterize方法設置sql執行時候需要的參數 handler.parameterize(stmt); return stmt; } // RoutingStatementHandler ============================> // PreparedStatementHandler ============================> @Override public void parameterize(Statement statement) throws SQLException { parameterHandler.setParameters((PreparedStatement) statement); } // DefaultParameterHandler ============================> 此時參數設置成功 @Override public void setParameters(PreparedStatement ps) { ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId()); List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); if (parameterMappings != null) { for (int i = 0; i < parameterMappings.size(); i++) { ParameterMapping parameterMapping = parameterMappings.get(i); if (parameterMapping.getMode() != ParameterMode.OUT) { Object value; String propertyName = parameterMapping.getProperty(); if (boundSql.hasAdditionalParameter(propertyName)) { value = boundSql.getAdditionalParameter(propertyName); } else if (parameterObject == null) { value = null; } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { value = parameterObject; } else { MetaObject metaObject = configuration.newMetaObject(parameterObject); value = metaObject.getValue(propertyName); } TypeHandler typeHandler = parameterMapping.getTypeHandler(); JdbcType jdbcType = parameterMapping.getJdbcType(); if (value == null && jdbcType == null) { jdbcType = configuration.getJdbcTypeForNull(); } try { typeHandler.setParameter(ps, i + 1, value, jdbcType); } catch (TypeException | SQLException e) { throw new TypeException("Could not set parameters for mapping....."); } } } } }
4.4 SQL執行
// RoutingStatementHandler ============================> @Override public int update(Statement statement) throws SQLException { return delegate.update(statement); } // PreparedStatementHandler ============================> @Override public int update(Statement statement) throws SQLException { // 這裡就是底層JDBC的PreparedStatement 操作瞭 PreparedStatement ps = (PreparedStatement) statement; // 執行SQL增刪改操作 ps.execute(); // 獲取影響的行數 int rows = ps.getUpdateCount(); Object parameterObject = boundSql.getParameterObject(); KeyGenerator keyGenerator = mappedStatement.getKeyGenerator(); keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject); // 返回影響的行數 return rows; }
5 小結
mybatis執行SQL的流程都是:
1.根據statement字符串從configuration中獲取對應的mappedStatement;
2.根據獲取的mappedStatement創建相應的Statement實例;
3.根據傳入的參數對statement實例進行參數設置;
4.執行statement並執行後置操作;
到此這篇關於MyBatis核心源碼深度剖析SQL執行過程的文章就介紹到這瞭,更多相關MyBatis核心源碼剖析SQL執行過程內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- MyBatis攔截器的實現原理
- 淺談Mybatis SqlSession執行流程
- java executor包參數處理功能
- Mybatis執行Update返回行數為負數的問題
- 詳解MyBatis工作原理