Mybatis數據源

在描述mybatis數據源以前,先拋出幾個問題,這幾個問題都能在本文獲得解答java

1.mybatis是如何獲取到mysql鏈接的?

2.mybatis的Connection是怎麼被建立的?

  

1.Datasource的分類


咱們已一段mybatis的配置文件爲例mysql

<environments default="development">
  <environment id="development">
    <transactionManager type="JDBC">
      <property name="..." value="..."/>
    </transactionManager>
    <dataSource type="POOLED">
      <property name="driver" value="${driver}"/>
      <property name="url" value="${url}"/>
      <property name="username" value="${username}"/>
      <property name="password" value="${password}"/>
    </dataSource>
  </environment>
</environments>

datasource的type共有三個選項sql

UNPOOLED    不使用鏈接池的數據源

POOLED      使用鏈接池的數據源

JNDI        使用JNDI實現的數據源

 

2.Datasource的配置加載與建立


 mybatis在項目啓動階段會加載配置文件,讀取xml中的配置信息到Configuration中。咱們看下datasource是怎麼加載進來的。這段代碼在org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration數據庫

 點進去,找到這個方法。apache

 

 這裏經過工廠模式,反射生成一個DatasourceFactory。數組

這裏DataSourceFactory有三個子類,也就是上述三個datasource的類型。mybatis

 

3.Connection的建立


 當Datasource的配置加載到configuration後。每一次執行sql都須要Datasource對象建立Connection去鏈接數據庫。併發

咱們以一次查詢爲例,看下這個connection究竟是怎麼生成的。ui

transaction對象持有connection和datasource,咱們再點進去this

 

終於看到了真正獲取connection是datasource,那datasource是怎麼獲取connection的呢?

 

4.鏈接池


 datasource有三種類型,相對應的每種datasource建立connection各不相同。常見的是POOLEDA和UNPOOLED兩種類型。

 UNPOOLED顧名思義就是不用鏈接池的方式,每次用到一個就生產一個

POOLEDA類型採用了鏈接池的方式,內部經過Poolstate對象來維護鏈接池對象

 Poolstate內部有兩個數組,idleConnections用來存放空閒的connection,activeConnections用來存放鏈接中的connection。

具體的獲取一個Connection鏈接以下

private final PoolState state = new PoolState(this);
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) {
      //這個對象鎖鎖的範圍有點大,不過這也是由於PoolState內部的兩個集合是ArrayList會產生併發問題
      synchronized (state) {
        //有空閒鏈接
        if (state.idleConnections.size() > 0) {
          // Pool has available connection
          conn = state.idleConnections.remove(0);
          if (log.isDebugEnabled()) {
            log.debug("Checked out connection " + conn.getRealHashCode() + " from pool.");
          }
        } else {
          // Pool does not have available connection
          if (state.activeConnections.size() < poolMaximumActiveConnections) {
            // Can create new connection
            conn = new PooledConnection(dataSource.getConnection(), this);
            @SuppressWarnings("unused")
            //used in logging, if enabled
            Connection realConn = conn.getRealConnection();
            if (log.isDebugEnabled()) {
              log.debug("Created connection " + conn.getRealHashCode() + ".");
            }
          } else {
            // Cannot create new connection
            PooledConnection oldestActiveConnection = state.activeConnections.get(0);
            long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
            if (longestCheckoutTime > poolMaximumCheckoutTime) {
              // Can claim overdue connection
              state.claimedOverdueConnectionCount++;
              state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;
              state.accumulatedCheckoutTime += longestCheckoutTime;
              state.activeConnections.remove(oldestActiveConnection);
              if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {
                oldestActiveConnection.getRealConnection().rollback();
              }
              conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);
              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();
                state.wait(poolTimeToWait);
                state.accumulatedWaitTime += System.currentTimeMillis() - wt;
              } catch (InterruptedException e) {
                break;
              }
            }
          }
        }
        if (conn != null) {
          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 + 3)) {
              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;
  }

1.   先看是否有空閒(idle)狀態下的PooledConnection對象,若是有,就直接返回一個可用的PooledConnection對象;不然進行第2步。

2.  查看活動狀態的PooledConnection池activeConnections是否已滿;若是沒有滿,則建立一個新的PooledConnection對象,而後放到activeConnections池中,而後返回此PooledConnection對象;不然進行第三步;

3.  看最早進入activeConnections池中的PooledConnection對象是否已通過期:若是已通過期,從activeConnections池中移除此對象,而後建立一個新的PooledConnection對象,添加到activeConnections中,而後將此對象返回;不然進行第4步。

4.  線程等待,循環2步

 

以上描述的是經過鏈接池獲取connection,那在關閉connection後,參照獲取鏈接的步驟,首先從activeConnections中移除,再判斷idle數組是否已經滿了,若是滿了再判斷下數組中第一個鏈接是否已經任然可用,若是可用再把這個這個鏈接銷燬。

上文咱們提到了PoolState

 兩個集合裏面存放着PooledConnection對象,PooledConnection對象是Connection的代理類

PooledConnection相比Connection多了什麼呢,答案就在invoke方法中,當connection對象調用close方法時,會調用datasource的pushConnection方法,pushConnection的方法大體和上面的猜測一致

相關文章
相關標籤/搜索