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 相关类

原文地址:https://www.cnblogs.com/drafire/p/9778053.html