由於在建立數據源的時候,對象是一個很是複雜的對象,因此採用了的是工廠模式html
在Mybatis框架中,涉及到的數據源,經常使用的是兩個,PooledDataSource(數據源鏈接池),UnpooledDataSource(非鏈接池數據源)數據庫
來源:PooledDataSource 是經過 PooledDataSourceFactory建立的,UnPooledDataSource 是經過UnPooledDataSourceFactory建立的;設計模式
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); } } }
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++; } } }