mybatis源码系列(五)—— DataSource模块

本文详细剖析了PooledDataSource中的PooledConnection、PoolState和连接获取、归还流程。重点讲解了数据库连接池的配置、初始化过程,以及如何通过mybatis-config.xml进行配置。了解了关键类的作用和连接池策略,有助于理解数据库连接优化。
摘要由CSDN通过智能技术生成

首先我们看一下几个关键的类
PooledConnection:使用动态代理封装了真正的数据库连接对象;
PoolState:用于管理PooledConnection对象状态的组件,通过两个list分别 管理空闲状态的连接资源和活跃状态的连接资源
PooledDataSource:一个简单,同步的、线程安全的数据库连接池

PooledDataSource的配置和初始化
在示例代码中mybatis-config.xml中如下配置,采用PooledDataSource
在这里插入图片描述
mybatis在启动过程中会解析environments节点并创建PooledDataSource
在这里插入图片描述
PooledDataSource 获取和归还连接过程
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
JdbcTransaction.getConnection:

@Override
public Connection getConnection() throws SQLException {
  if (connection == null) {
    openConnection();
  }
  return connection;
}

在这里插入图片描述
PooledDataSource:

@Override
public Connection getConnection() throws SQLException {
  return popConnection(dataSource.getUsername(), dataSource.getPassword()).getProxyConnection();
}

PooledDataSource获取一个的连接的大致流程为:首先判断空闲连接列表是否有值,如果有,则从空闲列表中取出连接进行复用。如果没有,则从活动连接列表中取出第一个连接,判断此连接是否已经超时,如果已经超时,则移除该链接并且复用,如果没有超时,则等待一段时间后继续重新走一遍流程。
流程图如下:
在这里插入图片描述
具体代码如下:

private PooledConnection popConnection(String username, String password) throws SQLException {
  boolean countedWait = false;
  PooledConnection conn = null;
  long t = System.currentTimeMillis();
  int localBadConnectionCount = 0;

  while (conn == null) {
    // 加锁
    synchronized (state) {
      // 如果闲置连接列表不为空
      if (!state.idleConnections.isEmpty()) {
        // 从闲置连接列表中取出连接
        conn = state.idleConnections.remove(0);
        if (log.isDebugEnabled()) {
          log.debug("Checked out connection " + conn.getRealHashCode() + " from pool.");
        }
      } else {
        // 判断活动连接列表的size是否小于连接池最大的活动连接数量,poolMaximumActiveConnections默认为10
        if (state.activeConnections.size() < poolMaximumActiveConnections) {
          // 如果小于则创建新的连接
          conn = new PooledConnection(dataSource.getConnection(), this);
          if (log.isDebugEnabled()) {
            log.debug("Created connection " + conn.getRealHashCode() + ".");
          }
        } else {
          // 如果大于则从活动连接列表中获取第一个连接
          PooledConnection oldestActiveConnection = state.activeConnections.get(0);
          long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
          // 判读此连接是否已经超时
          if (longestCheckoutTime > poolMaximumCheckoutTime) {
            // 已经超时则更新超时时间
            state.claimedOverdueConnectionCount++;
            state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;
            state.accumulatedCheckoutTime += longestCheckoutTime;
            // 从活动连接列表中移除此超时连接
            state.activeConnections.remove(oldestActiveConnection);
            if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {
              try {
                // 进行回滚操作 确保数据正确
                oldestActiveConnection.getRealConnection().rollback();
              } catch (SQLException e) {
                log.debug("Bad connection. Could not roll back");
              }
            }
            // 用超时连接对象中的真实连接对象Connection创建新的连接PooledConnection
            conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);
            conn.setCreatedTimestamp(oldestActiveConnection.getCreatedTimestamp());
            conn.setLastUsedTimestamp(oldestActiveConnection.getLastUsedTimestamp());
            // 将超时连接对象设置为失效状态
            oldestActiveConnection.invalidate();
            if (log.isDebugEnabled()) {
              log.debug("Claimed overdue connection " + conn.getRealHashCode() + ".");
            }
          } else {
            // 没有超时则更新超时时间
            // Must wait
            try {
              if (!countedWait) {
                //记录等待获取超时连接的个数
                state.hadToWaitCount++;
                countedWait = true;
              }
              if (log.isDebugEnabled()) {
                log.debug("Waiting as long as " + poolTimeToWait + " milliseconds for connection.");
              }
              long wt = System.currentTimeMillis();
              //等待poolTimeToWait时间后继续走while方法逻辑
              state.wait(poolTimeToWait);
              state.accumulatedWaitTime += System.currentTimeMillis() - wt;
            } catch (InterruptedException e) {
              break;
            }
          }
        }
      }
      if (conn != null) {
        // 通过conn.isValid()Ping服务器,检查连接是否有效
        if (conn.isValid()) {
          if (!conn.getRealConnection().getAutoCommit()) {
            // 进行回滚操作 确保数据正确
            conn.getRealConnection().rollback();
          }
          conn.setConnectionTypeCode(assembleConnectionTypeCode(dataSource.getUrl(), username, password));
          conn.setCheckoutTimestamp(System.currentTimeMillis());
          conn.setLastUsedTimestamp(System.currentTimeMillis());
          //将连接添加到活动连接列表中
          state.activeConnections.add(conn);
          state.requestCount++;
          state.accumulatedRequestTime += System.currentTimeMillis() - t;
        } else {
          if (log.isDebugEnabled()) {
            log.debug("A bad connection (" + conn.getRealHashCode() + ") was returned from the pool, getting another connection.");
          }
          state.badConnectionCount++;
          localBadConnectionCount++;
          conn = null;
          if (localBadConnectionCount > (poolMaximumIdleConnections + poolMaximumLocalBadConnectionTolerance)) {
            if (log.isDebugEnabled()) {
              log.debug("PooledDataSource: Could not get a good connection to the database.");
            }
            throw new SQLException("PooledDataSource: Could not get a good connection to the database.");
          }
        }
      }
    }
  }

  if (conn == null) {
    if (log.isDebugEnabled()) {
      log.debug("PooledDataSource: Unknown severe error condition.  The connection pool returned a null connection.");
    }
    throw new SQLException("PooledDataSource: Unknown severe error condition.  The connection pool returned a null connection.");
  }

  return conn;
}

PooledDataSource归还连接的流程:先从活动连接线程列表中移除,然后判断连接是否可用,如果可用的情况下再判断空闲列表个数是否小于最大空闲连接个数,且连接是相同的数据库连接,则将连接加入到空闲连接列表中,并进行唤醒操作。
流程图如下:
在这里插入图片描述
具体代码如下:

protected void pushConnection(PooledConnection conn) throws SQLException {
  // 加锁
  synchronized (state) {
    // 从活动连接列表中移除此连接      
    state.activeConnections.remove(conn);
    //ping一下服务器确保连接有效
    if (conn.isValid()) {
      //判断空闲连接列表的个数是否小于最大空闲连接个数 && 判断连接是否是相同数据库的连接 expectedConnectionTypeCode = ("" + url + username + password).hashCode()
      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++;
    }
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值