MyBatis运行原理解析

1 回顾jdbc

1.1 jdbc执行流程

创建连接 connection -> 预编译SQL prepareStatement -> 执行SQL -> 获取结果集

1.2 SqlSessionFactory & SqlSession

1.2.1 获取SqlSession

SqlSessionFactoryBuilder -> build(configuration) -> defaultSqlSessionFactory -> openSQLSessionFromDataSource -> defaultSqlSession

读取配置文件,由配置文件生成sqlSessionFactory;

1.2.1.1 源码解析
  1. SqlSessionFactoryBuilder.build();
public class SqlSessionFactoryBuilder {

  public SqlSessionFactory build(Reader reader) {
    /**调用内部build方法*/
    return build(reader, null, null);
  }

  public SqlSessionFactory build(Reader reader, String environment) {
    return build(reader, environment, null);
  }

  public SqlSessionFactory build(Reader reader, Properties properties) {
    return build(reader, null, properties);
  }
}
  1. SqlSessionFactoryBuilder内部build()一个DefaultSqlSessionFactory

从配置文件中解析出configuration,生成DefaultSqlSessionFactory

public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
      /**解析XML解析器*/
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      /**parser.parse() 获取Configuration,build一个DefaultSqlSessionFactory*/ 
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        reader.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

  // 使用解析出来的configuration,生成factory
  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }
} 
  1. 获取到SqlSessionFactory后通过SqlSessionFactory获取SqlSession对象

    openSessionFromDataSource,结合DataSource,生成sqlSession

/**SqlSessionFactory对象中的openSession方法最终都会调用openSessionFromDataSource方法*/
 private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      //通过Configuration获取mybatis的配置信息
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      //结合JDBC的执行流程来看 与数据库相互是statement对象,实际上executor是对于statement的封装,也就是说executor是statement的一个执行器
      final Executor executor = configuration.newExecutor(tx, execType);
      // 重点!!   创建了一个DefaultSqlSession对象
      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();
    }
  }
  1. 通过以上步骤,我们已经获取到了一个SqlSession,按照JDBC的步骤来说我们应该去执行sql了。上面步骤可以转化为如下常用的代码:
SqlSessionFactory sqlSessionFactory = new SqlSessionFactory();
String resource = "classpath:mybatis-config.xml"
try{
	//SqlSessionFactoryBuilder读取配置文件
	sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader(resource));
} catch(IOException e){
	e.printStackTrace();  
}
//获取sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession()

//使用sqlSession获取dao,返回的是一个代理对象
TestDao dao = sqlSession.getMapper(TestDao.class);

1.3 MapperProxy

到目前为止我们写的.mapper文件还没有使用!下面介绍 MapperPorxy

getMapper -> 传入defaultSqlSession - Configuration - MapperRegistry - MapperProxyFactory -> 生成MapperProxy

在mybatis中我的写的dao层的接口其实是MapperProxy在代理,也就是说我们在执行dao层中的方法是,其实是在执行MapperProxy

1.3.1 getMapper

  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  	// mapperRegistry是什么?见名知意 应该是mapper注册机之类的东西,接着往下走看看这个mapperRegister是什么
    return mapperRegistry.getMapper(type, sqlSession);
  }

1.3.2 mapperRegistry

MapperRegistry实际上就是一个注册机用来调用MapperProxyFactory工厂的

public class MapperRegistry {
	
  private final Configuration config;
  //
  private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();

  public MapperRegistry(Configuration config) {
    this.config = config;
  }

  @SuppressWarnings("unchecked")
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    // 这里定了一个MapperProxyFactory的工厂
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      //代理工厂的实例 返回了一个mapper的代理对象工厂的实例,这是不是就是我们想要的dao层对象呢?我们继续往下看MapperProxyFactory
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }
}

1.3.3 mapperProxyFactory

/**部分代码*/
public class MapperProxyFactory<T> {
  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap<>();

  public MapperProxyFactory(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }

  public Class<T> getMapperInterface() {
    return mapperInterface;
  }

  public Map<Method, MapperMethodInvoker> getMethodCache() {
    return methodCache;
  }

  //  mapperProxyFactory.newInstance(sqlSession),返回实际的mapper接口的实现类
  public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

  protected T newInstance(MapperProxy<T> mapperProxy) {
    //重点来了,这里代理到了我们写的Dao层接口,返回了mapperProxy
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

}

通过以上代理就可以获取到dao层,用到了jdk动态代理:

TestDao dao = sqlSession.getMapper(TestDao.class);

1.4 Excutor

1.4.1 执行流程

到这里获取到了SqlSession 和 mapper接口,那接下就是去执行SQL ,我们去看下真正的SQL执行流程

invoke - MapperProxy - execute MapperMethod - crud操作 defaultSqlSession - simple executor - prepareStatement - resultSetHandler

1.4.2 MapperProxy

上面提到通过MapperProxyFactory拿到了MapperProxy,每一个MapperProxy都是对应的dao层接口:

  //MapperProxy在执行的时候会触发此方法
  interface MapperMethodInvoker {
    Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable;
  }

  // ===
  private static class PlainMethodInvoker implements MapperMethodInvoker {
    private final MapperMethod mapperMethod;

    @Override
    public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
      // 这里交给mapperMethod去处理
      return mapperMethod.execute(sqlSession, args);
    }
  }

映射器就是一个动态代理进入到了mapperMethod的execute方法,经过判断进入到了sqlSession的delete、update、insert、select等方法。

sqlSession的执行过程是通过四大对象来完成的:

  • Executor,执行器,有它调度StatementHandler、ParameterHandler、ResultHandler等来执行对应的SQL
  • StatementHandler,数据库会话器,使用Statement执行操作(PrepareStatement),是四大对象的核心。根据configuration来构建statementHandler,然后使用prepare方法,对sql进行编译和参数初始化。
  • ParameterHandler,用来处理sql参数。**getParameterObject()返回参数对象,再使用typeHandler(MyBatis初始化时注册在configuration中的)进行转换参数。然后setParameters()**设置参数
  • ResultHandler,handleResultSets()方法,进行数据集ResultSet的封装返回的,比较复杂,一般不常用。
//这里是对数据库操作类型的判断,最终还是返回了SqlSession,那我们去看看SqlSession的CRUD方法
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);
          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;
  }

一条查询SQL的运行过程:

  • executor先调用statementHandler的prepare方法预编译sql,同时设置一些基本运行的参数。
  • 然后用paremeterize方法启用parameterHandler设置参数,完成预编译
  • 使用ResultHandler封装结果返回给调用者

进入到SqlSession的实现类DefaultSqlSession随便找一个方法进行查看,这里选择了SelectOne:

  @Override
  public <T> T selectOne(String statement, Object parameter) {
    // 我们点击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;
    }
  }

随着this.selectList()方法一直查看最终会在SimpleExecutor这个类看到这块内容

 @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.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

  // 实际查询的地方
  @Override
  protected <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql) throws SQLException {
    Configuration configuration = ms.getConfiguration();
    StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, null, boundSql);
    // 预编译
    Statement stmt = prepareStatement(handler, ms.getStatementLog());
    // 执行查询
    Cursor<E> cursor = handler.queryCursor(stmt);
    stmt.closeOnCompletion();
    return cursor;
  }

  @Override
  public List<BatchResult> doFlushStatements(boolean isRollback) {
    return Collections.emptyList();
  }

  //prepareStatement,设置参数
  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection, transaction.getTimeout());
    // parameterize调用ParemeterHandler去完成
    handler.parameterize(stmt);
    return stmt;
  }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值