Mybatis源码简析

参加了GitChat一个关于Mybatis活动:《一步步学习 Mybatis:缓存的使用及源码分析》,文中着重讲解了Mybatis缓存配置及使用,对Mybatis源码也稍有讲解。虽自工作以来MyBatis一直有在使用,然源码很少有阅读,随借此机会把部分源码大概看了下,主要从系统启动mapper实例的创建到一条语句的执行。

示例代码:

public interface UserMapper {
    User findUserById(long id);
}

此处UserMapper为一个接口,其中定义了我们要对实体bean User的数据库持久化操作,此处仅为一个根据id查询实例的方法。接口UserMapper 的实例是通过MapperFactoryBean 来创建的, MapperFactoryBean 实现了Spring的FactoryBean接口:

public class MapperFactoryBean extends SqlSessionDaoSupport 
implements FactoryBean 

其实例是通过Java的动态代理来实现的,实现代码见下:

public class MapperProxyFactory {
...
...
  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

  public T newInstance(SqlSession sqlSession) {
    final MapperProxy mapperProxy = new MapperProxy(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }
}

从上面代码可以看到 userMapper实例是通过Java动态代理Proxy来实现的,其InvocationHandler 为MapperProxy 实例,而在MapperProxy实例中包含了一个SqlSession实例:SqlsessionTemplate,而在SqlSessionTemplate实例中则包含了初始化创建的SqlSessionFactory:DefaultSqlSessionFactory, 在通过userMapper调用findUserById时,也即是执行mapperProxy的invoke方法:

public class MapperProxy implements InvocationHandler, Serializable {

  private static final long serialVersionUID = -6424540398559729838L;
  private final SqlSession sqlSession;
  private final Class mapperInterface;
  private final Map methodCache;

  public MapperProxy(SqlSession sqlSession, Class mapperInterface, Map methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }

  @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);
  }

从代码mapperMethod.execute(sqlSession, args)追踪下去看到:

  Object param = method.convertArgsToSqlCommandParam(args);
          result = sqlSession.selectOne(command.getName(), param);

这里的sqlSession 其实是SqlSession的实例:SqlSessionTemplate,下面是SqlSessionTemplate中的selectOne方法:

@Override
  public  T selectOne(String statement, Object parameter) {
    return this.sqlSessionProxy. selectOne(statement, parameter);
  }

这里的sqlSessionProxy是 SqlSession的一个代理实现:SqlSessionInterceptor(SqlSessionTemplate的内部类)

private class SqlSessionInterceptor implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      SqlSession sqlSession = getSqlSession(
          SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType,
          SqlSessionTemplate.this.exceptionTranslator);
      try {
        Object result = method.invoke(sqlSession, args);
        if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
          // force commit even on non-dirty sessions because some databases require
          // a commit/rollback before calling close()
          sqlSession.commit(true);
        }
        return result;
        ...

跟踪上面代码,查看getSqlSession,最终在DefaultSqlSessionFactory类中看到创建的是一个DefaultSqlSession:

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      final Executor executor = configuration.newExecutor(tx, execType);
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

从上面两段代码可以看到mybatis为mapper里的每一次执行创建了独立的sqlSession实例。
继续追踪代码往下看,在DefaultSqlSession类中看到他的查询方法:

 @Override
  public  List 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();
    }
  }

其中:
MappedStatement: 对发往数据库执行的指令的抽象表示。
Executor: 用来和数据库交互的执行器,其接受MappedStatement作为参数。
如果继续往下看就可以在BaseExecutor看到其在查询之前先从缓存中查询,如果未命中则前往数据库查询:

List list;
    try {
      queryStack++;
      list = resultHandler == null ? (List) localCache.getObject(key) : null;
      if (list != null) {
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }

你可能感兴趣的:(Mybatis源码简析)