Mybatis内部模块之---数据源模块

1.应用的设计模式

由于在建立数据源的时候,对象是一个很是复杂的对象,因此采用了的是工厂模式html

2.数据源的建立分类

在Mybatis框架中,涉及到的数据源,经常使用的是两个,PooledDataSource(数据源链接池),UnpooledDataSource(非链接池数据源)数据库

来源:PooledDataSource 是经过 PooledDataSourceFactory建立的,UnPooledDataSource 是经过UnPooledDataSourceFactory建立的;设计模式

3.特殊点

3.1 UnpooledDataSource每次都会建立一个新的链接app

  private Connection doGetConnection(String username, String password) throws SQLException {
    Properties props = new Properties();
    if (driverProperties != null) {
      props.putAll(driverProperties);
    }
    if (username != null) {
      props.setProperty("user", username);
    }
    if (password != null) {
      props.setProperty("password", password);
    }
    return doGetConnection(props);
  }

  //从这个代码能够看出,unpooledDatasource获取链接的方式和手动获取链接的方式是同样的
  private Connection doGetConnection(Properties properties) throws SQLException {
    initializeDriver();
    Connection connection = DriverManager.getConnection(url, properties);
    //设置事务是否自动提交,事务的隔离级别
    configureConnection(connection);
    return connection;
  }

  private synchronized void initializeDriver() throws SQLException {
    if (!registeredDrivers.containsKey(driver)) {
      Class<?> driverType;
      try {
        if (driverClassLoader != null) {
          driverType = Class.forName(driver, true, driverClassLoader);
        } else {
          driverType = Resources.classForName(driver);
        }
        // DriverManager requires the driver to be loaded via the system ClassLoader.
        // http://www.kfu.com/~nsayer/Java/dyn-jdbc.html
        Driver driverInstance = (Driver)driverType.newInstance();
        DriverManager.registerDriver(new DriverProxy(driverInstance));
        registeredDrivers.put(driver, driverInstance);
      } catch (Exception e) {
        throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e);
      }
    }
  }
View Code

3.2 下面咱们主要的看一下PooledDataSource ,带有链接池的数据源框架

在过程当中,咱们涉及到了两个重要的参数ide

PooledConnection 使用动态代理封装了真正的数据库链接对象,加强的是关闭方法;oop

PoolState 管理的是 PooledConnection 对象组件的状态,其中有两个变量  idleConnections(空闲的链接池资源集合) 和 activeConnections(活跃的链接池资源集合),测试

下面咱们看一看链接池中在获取链接的时候流程:ui

  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()) {//检测是否有空闲链接
          // Pool has available connection
          //有空闲链接直接使用
          conn = state.idleConnections.remove(0);
          if (log.isDebugEnabled()) {
            log.debug("Checked out connection " + conn.getRealHashCode() + " from pool.");
          }
        } else {// 没有空闲链接
          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++;//超时链接次数+1
              state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;//累计超时时间增长
              state.accumulatedCheckoutTime += longestCheckoutTime;//累计的使用链接的时间增长
              state.activeConnections.remove(oldestActiveConnection);//从活跃队列中删除
              if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {//若是超时链接未提交,则手动回滚
                try {
                  oldestActiveConnection.getRealConnection().rollback();
                } catch (SQLException e) {//发生异常仅仅记录日志
                  /*
                     Just log a message for debug and continue to execute the following
                     statement like nothing happend.
                     Wrap the bad connection with a new PooledConnection, this will help
                     to not intterupt current executing thread and give current thread a
                     chance to join the next competion for another valid/good database
                     connection. At the end of this loop, bad {@link @conn} will be set as null.
                   */
                  log.debug("Bad connection. Could not roll back");
                }  
              }
              //在链接池中建立新的链接,注意对于数据库来讲,并无建立新链接;
              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 {
              // 无空闲链接,最先建立的链接没有失效,没法建立新链接,只能阻塞
              try {
                if (!countedWait) {
                  state.hadToWaitCount++;//链接池累计等待次数加1
                  countedWait = true;
                }
                if (log.isDebugEnabled()) {
                  log.debug("Waiting as long as " + poolTimeToWait + " milliseconds for connection.");
                }
                long wt = System.currentTimeMillis();
                state.wait(poolTimeToWait);//阻塞等待指定时间
                state.accumulatedWaitTime += System.currentTimeMillis() - wt;//累计等待时间增长
              } catch (InterruptedException e) {
                break;
              }
            }
          }
        }
        if (conn != null) {//获取链接成功的,要测试链接是否有效,同时更新统计数据
          // ping to server and check the connection is valid or not
          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++;//累计的获取无效链接次数+1
            localBadConnectionCount++;//当前获取无效链接次数+1
            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.");
            }
          }
        }
      }

    }

下面咱们看看回收链接的时候的状况this

  //回收链接资源
  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++;
      }
    }
  }
相关文章
相关标签/搜索