java源码学习-Mybatis(2)与数据库建立连接

前文:Mybatis加载mapper流程

由于mybatis是在jdbc的基础上进行封装的, 所以jdbc执行流程 获取连接->创建statements->resultSet这些步骤mybatis都是存在的, 本篇学习一下Mybatis获取Connection的步骤

jdbc执行流程图

在这里插入图片描述

Mybatis初始化

由于我这里是在springboot中整合使用的mybatis并且在springboot启动类中加了@MapperScan, 所以Mybatis的配置是springboot通过yml并且自动配置的,
在这里插入图片描述
我们可以看到在MybatisAutoConfiguration这个类中, 伴随着springboot的启动, 这个类中第一个执行的方法就是sqlSessionFactory, 顾名思义, 这个方法是创建一个工厂类来管理sql会话, 源码如下

@Bean
  @ConditionalOnMissingBean
  public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
    SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
    // 这里就是我们这次的重点, 配置DataSource
    factory.setDataSource(dataSource);
    /**
    * 这里我们省略其他的配置
    */
    return factory.getObject();
  }

这里我们追踪一下java的方法栈, 找一下这个dataSource是从哪里得到的
首先追踪到了ConstructorResolver这个类中的531行
这个argsToUse是一路传下去的我们使用的DataSource在这里定义
在这里插入图片描述
我们继续追踪一下这个argsHolder, 发现在这个类中的509行会进入一个createArgumentArray得到arguments属性, 到了最后我们会进入到spring的AbstractBeanFactory中
在这里插入图片描述
由于是自动装配的, 我们并没有指定数据库的连接池, 所以springboot会给根据dataSource这个name来自动装配一个数据库连接池Hikari

这样一来我们就清楚的明白了以下几点:

  1. 我们并没有配置数据库的连接池类型, 可是为什么用到了连接池
  2. Mybatis的SqlSessionFactory是什么时候生成的(在Builder中92行打断点之后可以查看java的方法栈, 可以看见是在第一次springboot @Resouce mapper的时候调用的)

Hikari连接池的启动

既然我们知道springboot的默认配置是使用Hikari连接池来实现数据库的连接的, 那么问题就很好解决了, 直接上一个断点
(ps: 其实细心的朋友应该很早就发现了, 因为每次在第一次执行sql的时候console面板都会弹出来下图这么一句话)
在这里插入图片描述
下面是Hikari的getConnection方法

private volatile HikariPool pool;
// xxxx
      HikariPool result = pool;
      // 不难发现这个线程池采用的是懒汉的单例模式, 并且使用的是双检锁
      // 第一次获取的时候, pool为空就实例化一次
      if (result == null) {
         synchronized (this) {
            result = pool;
            if (result == null) {
               validate();
               LOGGER.info("{} - Starting...", getPoolName());
               try {
                  pool = result = new HikariPool(this);
                  this.seal();
               }
               catch (PoolInitializationException pie) {
                  if (pie.getCause() instanceof SQLException) {
                     throw (SQLException) pie.getCause();
                  }
                  else {
                     throw pie;
                  }
               }
               LOGGER.info("{} - Start completed.", getPoolName());
            }
         }
      }

每次都从线程池中取出一个数据库连接, 就不需要每次都创建, 从而节省时间提高性能

Mybatis获取数据库连接

在jdbc的流程中, 创建数据库连接是先于创建statement的, 所以我们找到SImpleExecutor中的prepareStatement方法

  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    // 可以看见在这里我们获取了connection, 而一路下去则会发现, 这个连接是在连接池中取出的
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection, transaction.getTimeout());
    handler.parameterize(stmt);
    return stmt;
  }

后记

学会善用idea在调试时左下角的java方法调用栈
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/Cia_zibo/article/details/106863703