Mybatis源码阅读6 --- 数据库连接池

mybatis自带数据库连接池功能,当然也支持使用c3p0,druid等,在配置Environment-dataSource-type时,可以配置POOLED和UNPOOLED,分别对应PooledDataSourceFactory和UnPooledDataSourceFactory,前者继承后者,只是构造函数中设置dataSource为PooledDataSource,UnPooledDataSourceFactory也很简单,设置dataSource为UnPooledDataSource,并把该对象属性设置为Properties包含的key-value,这里又出现了MetaObject。DataSource主要功能就是getConnection,主要看下PooledDataSource获取connection,核心就是popConnection方法。

先看下一个辅助类PoolState:


存放了空闲连接和使用中的连接及其它参数,下面说一下popConnection流程:

1:若有空闲连接,则返回第一个(返回的是PooledConnection,不是Connection哦)

2:若当前连接数小于最大连接数,则创建PooledConnection

3:第一个使用的连接,检查是否大于最大使用时间

    3.1 满足条件,收回那个连接

    3.2 不满足,wait

4:后续校验

代码就不贴了,这里返回的PooledConnection,和Connection有什么关系呢?构造函数:

  public PooledConnection(Connection connection, PooledDataSource dataSource) {
    this.hashCode = connection.hashCode();
    this.realConnection = connection;
    this.dataSource = dataSource;
    this.createdTimestamp = System.currentTimeMillis();
    this.lastUsedTimestamp = System.currentTimeMillis();
    this.valid = true;
    this.proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), IFACES, this);
  }

在调用构造方法时,connection是dataSource.getConnection(),就是realConnection,同时又生成代理类proxyConnection,InvokeHandler就是PooledConnection,就是说PooledConnection有俩connection,一个真实的,一个代理的,代理类如何invoke呢?


如果调用close(proxyConnection的close,因为返回是proxyConnection),代理类就拦截掉了,直接把connection放入池子中。否则就让realConnection干活吧。

放入池子:空闲连接小于最大连接则放入,并notifyAll,否则拿到realConnection并close掉

protected void pushConnection(PooledConnection conn) throws SQLException {

    synchronized (state) {
      state.activeConnections.remove(conn);
      if (conn.isValid()) {
        if (state.idleConnections.size() < poolMaximumIdleConnections && conn.getConnectionTypeCode() == expectedConnectionTypeCode) {
          state.accumulatedCheckoutTime += conn.getCheckoutTime();
          if (!conn.getRealConnection().getAutoCommit()) {
            conn.getRealConnection().rollback();
          }
          PooledConnection newConn = new PooledConnection(conn.getRealConnection(), this);
          state.idleConnections.add(newConn);
          newConn.setCreatedTimestamp(conn.getCreatedTimestamp());
          newConn.setLastUsedTimestamp(conn.getLastUsedTimestamp());
          conn.invalidate();
          if (log.isDebugEnabled()) {
            log.debug("Returned connection " + newConn.getRealHashCode() + " to pool.");
          }
          state.notifyAll();
        } else {
          state.accumulatedCheckoutTime += conn.getCheckoutTime();
          if (!conn.getRealConnection().getAutoCommit()) {
            conn.getRealConnection().rollback();
          }
          conn.getRealConnection().close();
          if (log.isDebugEnabled()) {
            log.debug("Closed connection " + conn.getRealHashCode() + ".");
          }
          conn.invalidate();
        }
      } else {
        if (log.isDebugEnabled()) {
          log.debug("A bad connection (" + conn.getRealHashCode() + ") attempted to return to the pool, discarding connection.");
        }
        state.badConnectionCount++;
      }
    }
  }

这就是mybatis的线程池,思考下,Session和connection的关系,connection何时close?

如果想用其他连接池,这里以druid为例,

1、pom添加druid的依赖,创建一个工厂类,继承UnPooledDataSource,构造函数将dataSource设置为DruidDataSource

public class DruidDataSourceFactory extends UnpooledDataSourceFactory {
    public DruidDataSourceFactory(){
        this.dataSource = new DruidDataSource();
    }
}

2、添加environment,并将default指向这个id


运行,结束,整合还算方便。

注意配置文件添加了typeAliases,为了配置、使用方便,mybatis提供类型别名机制,相关代码很简单,大家自行查看一下吧

debug次数也不少了,可sql是啥样,再控制台可以显示不?下一篇我们看看mybatis的日志

猜你喜欢

转载自blog.csdn.net/lnlllnn/article/details/80723756