• mybatis源码之我见


    以前一直想看mybatis的源代码,但是一直没找到入口(傻),最近看教程,有些感悟。

    和起以前一样,关键代码我会用红色标记。

    首先,先贴下我的dao和mapper,代码很简单,和平时写的hello world 差不多。代码如下:

      <select id="select" parameterType="INTEGER" resultMap="BaseResultMap">
        SELECT  * from t_user where id=#{id}
      </select>
    @Mapper
    public interface UserMapper {
        int insert(User record);
    
        int insertSelective(User record);
    
        int update(User user);
    
        User select(int id);
    }

    service层代码:

    @Service
    public class UserService {
    
        @Autowired
        private UserMapper userMapper;
    
    
        public User getUser(int id){
            return userMapper.select(id);           
        }
    }

    这个 userMapper.select(id) 就是入口了。以前一直没有在运行的时候调试过这个,还以为是直接就调用了 User select(int id),傻逼了。跟踪进入代码:

      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
          if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
          } else if (isDefaultMethod(method)) {          //是否是构造函数
            return invokeDefaultMethod(proxy, method, args);
          }
        } catch (Throwable t) {
          throw ExceptionUtil.unwrapThrowable(t);
        }
        final MapperMethod mapperMethod = cachedMapperMethod(method);
        return mapperMethod.execute(sqlSession, args);
      }

    从代码可以看出,这里使用了MapperProxy 代理(启动程序的时候已经生成),这是一个jdk代理(实现了InvocationHandler 接口),因为这个调用的userMapper是一个接口,,接口的默认实现是jdk代理。继续跟进

    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()) {         //针对集合list进行处理
              result = executeForMany(sqlSession, args);
            } else if (method.returnsMap()) {          //针对ResultMap处理
              result = executeForMap(sqlSession, args);
            } else if (method.returnsCursor()) {       //针对游标处理(这是什么?)
              result = executeForCursor(sqlSession, args);
            } else {
              Object param = method.convertArgsToSqlCommandParam(args);
              result = sqlSession.selectOne(command.getName(), param);
            }
            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;
      }

    上面进的代码,主要是区分了sql的类型:insert、update、delete、select、flush。继续跟进代码,下面的代码进入SqlSession模块:

    #orgmybatismybatis-spring1.3.1mybatis-spring-1.3.1.jar!orgmybatisspringSqlSessionTemplate.class

    private
    class SqlSessionInterceptor implements InvocationHandler { private SqlSessionInterceptor() { } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator); Object unwrapped; try { Object result = method.invoke(sqlSession, args); if (!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) { sqlSession.commit(true); } unwrapped = result; } catch (Throwable var11) { unwrapped = ExceptionUtil.unwrapThrowable(var11); if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) { SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); sqlSession = null; Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException)unwrapped); if (translated != null) { unwrapped = translated; } } throw (Throwable)unwrapped; } finally { if (sqlSession != null) { SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); } } return unwrapped; } }
    #orgmybatismybatis3.4.5mybatis-3.4.5-sources.jar!orgapacheibatissessiondefaultsDefaultSqlSession.java  
    
    @Override
      public <T> T selectOne(String statement, Object parameter) {
        // Popular vote was to return null on 0 results and throw exception on too many.
        List<T> list = this.<T>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;
        }
      }

    @Override
    public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
    MappedStatement ms = configuration.getMappedStatement(statement);
    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();
    }
    }
     #orgmybatismybatis3.4.5mybatis-3.4.5-sources.jar!orgapacheibatispluginPlugin.java
    @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
          Set<Method> methods = signatureMap.get(method.getDeclaringClass());
          if (methods != null && methods.contains(method)) {      //注意这里,我们自定义插件的时候,就是在这里起作用的。如果有自定义插件,则会被拦截
            return interceptor.intercept(new Invocation(target, method, args));
          }
          return method.invoke(target, args);
        } catch (Exception e) {
          throw ExceptionUtil.unwrapThrowable(e);
        }
      }

    下面开始进入Executor模块,代码如下:

    @Override
      public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
        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, parameterObject, boundSql);
    @SuppressWarnings("unchecked")
    List<E> list = (List<E>) tcm.getObject(cache, key);
    if (list == null) {
    list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
    tcm.putObject(cache, key, list); // issue #578 and #116
    }
    return list;
    }
    }
    return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
    }
     

    这个类是执行器类。可以看到,这里是一个装饰器模式,通过重写Excutor的方法,进行了自己的方法的一些内容的添加(装饰器模式,保留核心功能,同时又新加了一些功能)。

    #orgmybatismybatis3.4.5mybatis-3.4.5-sources.jar!orgapacheibatisexecutorBaseExecutor.java
    @SuppressWarnings("unchecked") @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++; 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(); } // issue #601 deferredLoads.clear(); if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) { // issue #482 clearLocalCache(); } } return list; }

    private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) 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;
    }
     
    #orgmybatismybatis3.4.5mybatis-3.4.5-sources.jar!orgapacheibatisexecutorSimpleExecutor.java 

    @Override
    public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); stmt = prepareStatement(handler, ms.getStatementLog()); return handler.<E>query(stmt, resultHandler); } finally { closeStatement(stmt); } }

    从SimpleExecutor.doQuery()方法可以看出,这个是流程是:1、获取链接Connection  2、调用StateMentHandler执行

    #orgmybatismybatis3.4.5mybatis-3.4.5-sources.jar!orgapacheibatisexecutorstatementRoutingStatementHandler.java

    public
    <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { return delegate.<E>query(statement, resultHandler); }
    orgmybatismybatis3.4.5mybatis-3.4.5-sources.jar!orgapacheibatisexecutorstatementPreparedStatementHandler.java
    
      @Override
      public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        PreparedStatement ps = (PreparedStatement) statement;
        ps.execute();
        return resultSetHandler.<E> handleResultSets(ps);
      }

    从上面可以看出,mybatis最终调用的是jdbc的PrepareStatement 的execute方法执行sql,并且进行结果映射。再看下PrepareStatement

    的execute()方法

    #mysqlmysql-connector-java5.1.46mysql-connector-java-5.1.46.jar!commysqljdbcPreparedStatement.class
    public
    boolean execute() throws SQLException { synchronized(this.checkClosed().getConnectionMutex()) { MySQLConnection locallyScopedConn = this.connection; if (!this.doPingInstead && !this.checkReadOnlySafeStatement()) { throw SQLError.createSQLException(Messages.getString("PreparedStatement.20") + Messages.getString("PreparedStatement.21"), "S1009", this.getExceptionInterceptor()); } else { ResultSetInternalMethods rs = null; this.lastQueryIsOnDupKeyUpdate = false; if (this.retrieveGeneratedKeys) { this.lastQueryIsOnDupKeyUpdate = this.containsOnDuplicateKeyUpdateInSQL(); } this.batchedGeneratedKeys = null; this.resetCancelledState(); this.implicitlyCloseAllOpenResults(); this.clearWarnings(); if (this.doPingInstead) { this.doPingInstead(); return true; } else { this.setupStreamingTimeout(locallyScopedConn); Buffer sendPacket = this.fillSendPacket(); String oldCatalog = null; if (!locallyScopedConn.getCatalog().equals(this.currentCatalog)) { oldCatalog = locallyScopedConn.getCatalog(); locallyScopedConn.setCatalog(this.currentCatalog); } CachedResultSetMetaData cachedMetadata = null; if (locallyScopedConn.getCacheResultSetMetadata()) { cachedMetadata = locallyScopedConn.getCachedMetaData(this.originalSql); } Field[] metadataFromCache = null; if (cachedMetadata != null) { metadataFromCache = cachedMetadata.fields; } boolean oldInfoMsgState = false; if (this.retrieveGeneratedKeys) { oldInfoMsgState = locallyScopedConn.isReadInfoMsgEnabled(); locallyScopedConn.setReadInfoMsgEnabled(true); } locallyScopedConn.setSessionMaxRows(this.firstCharOfStmt == 'S' ? this.maxRows : -1); rs = this.executeInternal(this.maxRows, sendPacket, this.createStreamingResultSet(), this.firstCharOfStmt == 'S', metadataFromCache, false); //最终实际执行sql语句的地方 if (cachedMetadata != null) { locallyScopedConn.initializeResultsMetadataFromCache(this.originalSql, cachedMetadata, rs); } else if (rs.reallyResult() && locallyScopedConn.getCacheResultSetMetadata()) { locallyScopedConn.initializeResultsMetadataFromCache(this.originalSql, (CachedResultSetMetaData)null, rs); } if (this.retrieveGeneratedKeys) { locallyScopedConn.setReadInfoMsgEnabled(oldInfoMsgState); rs.setFirstCharOfQuery(this.firstCharOfStmt); } if (oldCatalog != null) { locallyScopedConn.setCatalog(oldCatalog); } if (rs != null) { this.lastInsertId = rs.getUpdateID(); this.results = rs; } return rs != null && rs.reallyResult(); } } } }

    下面是结果映射的代码:

    #orgmybatismybatis3.4.5mybatis-3.4.5-sources.jar!orgapacheibatisexecutor
    esultsetDefaultResultSetHandler.java
    @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); }

    注意:一个有意思的地方是,调用insert()的时候,最终调用的还是update(),代码在DefaultSqlSession中展现,如下:

      @Override
      public int insert(String statement) {
        return insert(statement, null);
      }
    
      @Override
      public int insert(String statement, Object parameter) {
        return update(statement, parameter);
      }

     

    ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    总结:

    mybatis的执行过程:

    1、调用代理对象的execute方法执行

    2、判断sql类型,分别处理增、删、改、查操作

    3、转换参数成为sql参数。如果是单个参数,则直接返回,如果是多个参数,则用param1、param2 这样数组返回

    4、如果一级缓存已经有缓存了这个方法,调用缓存的方法;如果没有则调用查询数据库的方法

    5、获取connection

    6、调用PrepareStateMent的execute方法

    7、结果集映射

    8、最后,调用sqlsession.commit()提交

    select语句的类调用顺序:SqlSession 相关类->Executor 相关类 -> PrepareStatement 相关类

  • 相关阅读:
    poj 1182:食物链(种类并查集,食物链问题)
    hdu 1556:Color the ball(第二类树状数组 —— 区间更新,点求和)
    南阳理工 题目9:posters(离散化+线段树)
    poj 1195:Mobile phones(二维树状数组,矩阵求和)
    poj 3984:迷宫问题(广搜,入门题)
    poj 3278:Catch That Cow(简单一维广搜)
    《重构与模式》简化(策略模式)-积木系列
    builder模式-积木系列
    spring事务管理-Spring 源码系列(6)
    思考如何阅读
  • 原文地址:https://www.cnblogs.com/drafire/p/9778053.html
Copyright © 2020-2023  润新知