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


    1.应用的设计模式

    因为在创建数据源的时候,对象是一个非常复杂的对象,所以采用了的是工厂模式

    2.数据源的创建分类

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

    来源:PooledDataSource 是通过 PooledDataSourceFactory创建的,UnPooledDataSource 是通过UnPooledDataSourceFactory创建的;

    3.特殊点

    3.1 UnpooledDataSource每次都会创建一个新的连接

      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 ,带有连接池的数据源

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

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

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

    下面我们看一看连接池中在获取连接的时候流程:

      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.");
                }
              }
            }
          }
    
        }

    下面我们看看回收连接的时候的情况

      //回收连接资源
      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++;
          }
        }
      }
  • 相关阅读:
    前端
    小程序开发
    mpvue开发小程序
    (33)Vue购物车
    Vue的使用你学会了吗?
    (32)Vue模板语法
    (31)Vue安装
    (3)Angular的开发
    (2)Angular的开发
    (1)Angular的开发
  • 原文地址:https://www.cnblogs.com/lys-lyy/p/11498860.html
Copyright © 2020-2023  润新知