Mybatis源码分析——如何执行一条sql

本文详细分析了Mybatis中SqlSession的创建过程,从SqlSessionFactoryBuilder到DefaultSqlSessionFactory,再到SqlSession的获取和执行sql的步骤。通过DefaultSqlSession的openSession方法,结合Executor和Transaction,最终解释了如何通过MapperProxy调用MapperMethod执行查询操作,涉及到DynamicSqlSource的解析、BoundSql的构建以及StatementHandler和ResultHandler的处理。
摘要由CSDN通过智能技术生成

Mybatis源码分析——如何为dao类生成代理对象

从上面这篇文章中,我们知道,mybatis为dao类生成了一个代理对象,最终调用SqlSession的方法来执行sql。那SqlSession是如何产生的呢,当然是通过SqlSessionFactory创建的啦,那SqlSessionFactory又是如何产生的呢,当然是通过SqlSessionFactoryBuilder创建的啦。

SqlSessionFactory有2个实现类,DefaultSqlSessionFactory和SqlSessionManager。SqlSessionManager比较特殊,它同时还实现SqlSession,后面再分析它。

在分析解析mybatis-config.xml的时候,我们知道SqlSessionFactoryBuilder返回的是DefaultSqlSessionFactory实例。

SqlSession有3个实现类,分别是DefaultSqlSession、SqlSessionManager、SqlSessionTemplate,后2者其实也是通过代理的方式,最终由DefaultSqlSession实现功能。

那么,SqlSession是如何得到的呢?答案是DefaultSqlSessionFactory.openSession,如下

  @Override
  public SqlSession openSession() {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }

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();
    }
  }
  private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
    try {
      boolean autoCommit;
      try {
        autoCommit = connection.getAutoCommit();
      } catch (SQLException e) {
        // Failover to true, as most poor drivers
        // or databases won't support transactions
        autoCommit = true;
      }      
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      final Transaction tx = transactionFactory.newTransaction(connection);
      final Executor executor = configuration.newExecutor(tx, execType);
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

 

openSession有很多重写的方法,最根本还是openSessionFromDataSource和openSessionFromConnection,一个是通过数据源打开,另一个是通过连接打开。

从上面代码可以看到,首先通过environment获取事务管理器,在解析mybatis-config.xml的时候,已经设置了事务管理器为SpringManagedTransactionFactory,然后获取Transaction,从下面代码可以看到,通过connection获取事务的方法是不支持的,所以走的还是openSessionFromDataSource。这里返回的是SpringManagedTransaction。


  @Override
  public Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit) {
    return new SpringManagedTransaction(dataSource);
  }


  @Override
  public Transaction newTransaction(Connection conn) {
    throw new UnsupportedOperationException("New Spring transactions require a DataSource");
  }

接着,通过事务Transaction和ExecutorType创建Executor,如下

  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction);
    }
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

可以看到,不同的ExecutorType对应不用的Executor,这里用的是策略模式。同时,还会把Executor绑定到拦截器的链条里。

最后 ,把Executor绑定到DefaultSqlSession,并设置是否自动提交,然后返回。

 

好了,获取到了DefaultSqlSession,那怎么执行sql呢,我们回到MapperProxy.invoke看起。如下

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

  private MapperMethod cachedMapperMethod(Method method) {
    MapperMethod mapperMethod = methodCache.get(method);
    if (mapperMethod == null) {
      mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
      methodCache.put(method, mapperMethod);
    }
    return mapperMethod;
  }

重点在new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());,这里的缓存也很重要,因为new MapperMethod做了很多事情,如下

  public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
    this.command = new SqlCommand(config, mapperInterface, method);
    this.method = new MethodSignature(config, mapperInterface, method);
  }

这里初始化SqlCommand和MethodSignature。

SqlCommand是MapperMethod的一个内部类,2个属性,name和type,如下

public static class SqlCommand {

    private final String name;
    private final SqlCommandType type;

    public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
      String statementName = mapperInterface.getName() + "." + method.getName();
      MappedStatement ms = null;
      if (configuration.hasStatement(statementName)) {
        ms = configuration.getMappedStatement(statementName);
      } else if (!mapperInterface.equals(method.getDeclaringClass())) { // issue #35
        String parentStatementName = method.getDeclaringClass().getName() + "." + method.getName();
        if (configuration.hasStatement(parentStatementName)) {
          ms = configuration.getMappedStatement(parentStatementName);
        }
      }
      if (ms == null) {
        if(method.getAnnotation(Flush.class) != null){
          name = null;
          typ
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值