Mybatis源碼解讀(四)--數據源模塊(工廠模式)

Mybatis的數據源模塊位於org.apache.ibatis.datasource。

常見的數據源組件都實現了javax.sql.DataSource接口。在Mybatis內不但集成第三方的數據源組件,自身也提供了數據源的實現。而一般情況下數據源初始化較複雜,參數較多,因此這裏採用了工廠模式。

 

工廠模式

工廠模式(Factory Pattern)屬於創建者模式,它提供了一種創建對象的最佳方式。定義了一個創建對象的接口,讓其子類自己覺得實例化哪個工廠類,工廠模式使其延遲到子類進行。

工廠接口(Factory):工廠接口是工廠模式的核心接口,調用者通過直接和工廠接口交互獲取具體的實現類。
具體工廠類(ConcreteFactory):工廠接口的實現類,用於實例化產品對象,不同的具體工廠類會根據不同需求實例化不同的產品實現類。
產品接口(Product):產品接口用於定義產品類的功能,具體工廠類產生的產品都必須實現這個接口。
具體產品類(ConcreteProduct):產品接口的實現類,具體產品類中定義了具體的業務邏輯。

 

數據源模塊

DataSourceFactory:Mybatis數據源的工廠接口。Mybatis提供了兩種具體工廠類,PooledDataSourceFactory(使用連接池的數據源工廠),UnpooledDataSourceFactory(不用連接池的數據源工廠),分別實例了PooledDataSource和UnpooledDataSource。

public interface DataSourceFactory {

  //設置DataSource的相關屬性
  void setProperties(Properties props);

  //獲取數據源
  DataSource getDataSource();

}
public class UnpooledDataSourceFactory implements DataSourceFactory {

  protected DataSource dataSource;

  public UnpooledDataSourceFactory() {
    this.dataSource = new UnpooledDataSource();
  }
  
  ...

}
public class PooledDataSourceFactory extends UnpooledDataSourceFactory {

  public PooledDataSourceFactory() {
    this.dataSource = new PooledDataSource();
  }

}

 

UnpooledDataSource:不帶連接池的數據源,這裏獲取連接的方式和jdbc手動獲取連接的方式是一樣的。

  private Connection doGetConnection(Properties properties) throws SQLException {
    initializeDriver();
    Connection connection = DriverManager.getConnection(url, properties);
    //設置事務是否自動提交,事務的隔離級別
    configureConnection(connection);
    return connection;
  }

 

PooledDataSource:帶連接池的數據源。PooledDataSource通過PoolState管理PooledConnection對象狀態的組件,通過兩個list分別管理空閒狀態的連接資源和活躍狀態的連接資源。

public class PoolState {
  protected PooledDataSource dataSource;
  //空閒的連接池資源集合
  protected final List<PooledConnection> idleConnections = new ArrayList<>();
  //活躍的連接池資源集合
  protected final List<PooledConnection> activeConnections = new ArrayList<>();
  //請求的次數
  protected long requestCount = 0;
  //累計的獲得連接的時間
  protected long accumulatedRequestTime = 0;
  //累計的使用連接的時間。從連接取出到歸還,算一次使用的時間;
  protected long accumulatedCheckoutTime = 0;
  //使用連接超時的次數
  protected long claimedOverdueConnectionCount = 0;
  //累計超時時間
  protected long accumulatedCheckoutTimeOfOverdueConnections = 0;
  //累計等待時間
  protected long accumulatedWaitTime = 0;
  //等待次數 
  protected long hadToWaitCount = 0;
  //無效的連接次數 
  protected long badConnectionCount = 0;
  
  ...

}

PooledDataSource:帶連接池的數據源。PooledDataSource內通過PoolStatus來管理連接池,通過popConnection和pushConnection實現線程池的拿和放操作,getConnection調用popConnection。

  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++;
      }
    }
  }

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

    }

    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;
  }

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章