MyBatis鏈接管理(1)java
在調用SqlSessionFactory的openSession函數時,只是建立了一個DefaultSqlSession實例,並無真正去鏈接MySQL:sql
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; DefaultSqlSession var8; try { Environment environment = this.configuration.getEnvironment(); TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment); tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); Executor executor = this.configuration.newExecutor(tx, execType); var8 = new DefaultSqlSession(this.configuration, executor, autoCommit); } catch (Exception var12) { this.closeTransaction(tx); throw ExceptionFactory.wrapException("Error opening session. Cause: " + var12, var12); } finally { ErrorContext.instance().reset(); } return var8; }
只有當真正執行SQL語句時,好比query,才經過SimpleExecutor調用doQuery->prepareStatement進入父類BaseExecutor的getConnection,最終經過Transaction(JdbcTransaction)調用DataSource(PooledDataSource)的popConnection:segmentfault
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 var8 = this.state; synchronized(this.state) { if (!this.state.idleConnections.isEmpty()) { conn = (PooledConnection)this.state.idleConnections.remove(0); if (log.isDebugEnabled()) { log.debug("Checked out connection " + conn.getRealHashCode() + " from pool."); } } else if (this.state.activeConnections.size() < this.poolMaximumActiveConnections) { conn = new PooledConnection(this.dataSource.getConnection(), this); if (log.isDebugEnabled()) { log.debug("Created connection " + conn.getRealHashCode() + "."); } } else { PooledConnection oldestActiveConnection = (PooledConnection)this.state.activeConnections.get(0); long longestCheckoutTime = oldestActiveConnection.getCheckoutTime(); if (longestCheckoutTime > (long)this.poolMaximumCheckoutTime) { ++this.state.claimedOverdueConnectionCount; this.state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime; this.state.accumulatedCheckoutTime += longestCheckoutTime; this.state.activeConnections.remove(oldestActiveConnection); if (!oldestActiveConnection.getRealConnection().getAutoCommit()) { try { oldestActiveConnection.getRealConnection().rollback(); } catch (SQLException var16) { 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) { ++this.state.hadToWaitCount; countedWait = true; } if (log.isDebugEnabled()) { log.debug("Waiting as long as " + this.poolTimeToWait + " milliseconds for connection."); } long wt = System.currentTimeMillis(); this.state.wait((long)this.poolTimeToWait); this.state.accumulatedWaitTime += System.currentTimeMillis() - wt; } catch (InterruptedException var17) { break; } } } if (conn != null) { if (conn.isValid()) { if (!conn.getRealConnection().getAutoCommit()) { conn.getRealConnection().rollback(); } conn.setConnectionTypeCode(this.assembleConnectionTypeCode(this.dataSource.getUrl(), username, password)); conn.setCheckoutTimestamp(System.currentTimeMillis()); conn.setLastUsedTimestamp(System.currentTimeMillis()); this.state.activeConnections.add(conn); ++this.state.requestCount; this.state.accumulatedRequestTime += System.currentTimeMillis() - t; } else { if (log.isDebugEnabled()) { log.debug("A bad connection (" + conn.getRealHashCode() + ") was returned from the pool, getting another connection."); } ++this.state.badConnectionCount; ++localBadConnectionCount; conn = null; if (localBadConnectionCount > this.poolMaximumIdleConnections + this.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."); } else { return conn; } }
流程圖以下:
session
這裏須要注意的是,最終返回的是利用JAVA動態代理建立的實際鏈接的代理鏈接PooledConnection(具體實現原理見動態代理實現原理):函數
public Connection getConnection() throws SQLException { return this.popConnection(this.dataSource.getUsername(), this.dataSource.getPassword()).getProxyConnection(); }
當調用實際鏈接的方法時,就會進入PooledConnection的invoke方法:this
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); if ("close".hashCode() == methodName.hashCode() && "close".equals(methodName)) { this.dataSource.pushConnection(this); return null; } else { try { if (!Object.class.equals(method.getDeclaringClass())) { this.checkConnection(); } return method.invoke(this.realConnection, args); } catch (Throwable var6) { throw ExceptionUtil.unwrapThrowable(var6); } } }
該方法會攔截實際鏈接的close方法,實際調用DataSource的pushConnection方法:spa
protected void pushConnection(PooledConnection conn) throws SQLException { synchronized(this.state) { this.state.activeConnections.remove(conn); if (conn.isValid()) { PoolState var10000; if (this.state.idleConnections.size() < this.poolMaximumIdleConnections && conn.getConnectionTypeCode() == this.expectedConnectionTypeCode) { var10000 = this.state; var10000.accumulatedCheckoutTime += conn.getCheckoutTime(); if (!conn.getRealConnection().getAutoCommit()) { conn.getRealConnection().rollback(); } PooledConnection newConn = new PooledConnection(conn.getRealConnection(), this); 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."); } this.state.notifyAll(); } else { var10000 = this.state; var10000.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."); } ++this.state.badConnectionCount; } } }
該方法判斷若是空閒鏈接數小於上限則回收該鏈接(這裏沒有想明白爲何要從新建一個PooledConnection而不是直接複用原來的實例,但願有高手能夠在留言裏幫忙指點迷津)並喚醒等待獲取鏈接的線程,不然關閉鏈接。
這裏涉及到的主要類的UML圖以下:線程
在JdbcTransaction中利用PooledDataSource的popConnection獲取到鏈接後,會設置是否自動提交(Mysql默認是自動提交,但MyBatis默認是關閉自動提交)和事務隔離等級:debug
protected void openConnection() throws SQLException { if (log.isDebugEnabled()) { log.debug("Opening JDBC Connection"); } this.connection = this.dataSource.getConnection(); if (this.level != null) { this.connection.setTransactionIsolation(this.level.getLevel()); } this.setDesiredAutoCommit(this.autoCommmit); }