MyBatis连接管理(1)

MySQL连接管理

在调用SqlSessionFactory的openSession函数时,只是创建了一个DefaultSqlSession实例,并没有真正去连接MySQL:

    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,最终通过Transaction调用DataSource的popConnection:

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

流程图如下:

这里需要注意的是,最终返回的是利用JAVA动态代理创建的实际连接的代理连接PooledConnection(具体原理会在另外一篇博文里详细解释):

    public Connection getConnection() throws SQLException {        return this.popConnection(this.dataSource.getUsername(), this.dataSource.getPassword()).getProxyConnection();    }

当调用实际连接的方法时,就会进入PooledConnection的invoke方法:

    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方法:

    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图如下: