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++;
      }
    }
  }
相關文章
相關標籤/搜索