MyBatis 数据源原理 - litter-fish/ReadSource GitHub Wiki

配置连接池

MyBatis 支持三种数据源配置,分别为 UNPOOLED、POOLED 和 JNDI

  1. UNPOOLED - 每次请求时都会重新打开和关闭新的连接。
  2. POOLED - 利用“池”的概念将 JDBC 连接对象组织起来,避免创建新的连接实例时所必需的初始化和认证时间。
  3. JNDI - 能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。
<environments default="development">
  <environment id="development">
    <!-- 事务管理器的配置 -->
    <transactionManager type="JDBC">
      <property name="..." value="..."/>
    </transactionManager>
    <!-- 数据源的配置 -->
    <dataSource type="POOLED|UNPOOLED">
      <property name="driver" value="${driver}"/>
      <property name="url" value="${url}"/>
      <property name="username" value="${username}"/>
      <property name="password" value="${password}"/>
    </dataSource>
</environment></environments>

数据源类图

数据源初始化过程

开始解析environments节点

// XMLConfigBuilder
private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
        if (environment == null) {
            // 默认使用的环境 ID
            environment = context.getStringAttribute("default");
        }
        for (XNode child : context.getChildren()) {
            // 每个 environment 元素定义的环境 ID
            String id = child.getStringAttribute("id");
            if (isSpecifiedEnvironment(id)) {
                // 获取事务管理器的配置
                TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
                // 获取数据源工厂类实例
                DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
                DataSource dataSource = dsFactory.getDataSource();
                Environment.Builder environmentBuilder = new Environment.Builder(id)
                    .transactionFactory(txFactory)
                    .dataSource(dataSource);
                configuration.setEnvironment(environmentBuilder.build());
            }
        }
    }
}

初始化数据源工厂类实例

// XMLConfigBuilder
private DataSourceFactory dataSourceElement(XNode context) throws Exception {
    if (context != null) {
        // 获取数据源类型:类型,UNPOOLED|POOLED|JNDI
        // UNPOOLED– 这个数据源的实现只是每次被请求时打开和关闭连接
        // POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。
        // JNDI – 这个数据源的实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。
        String type = context.getStringAttribute("type");
        // 获取特定数据类型的属性
        Properties props = context.getChildrenAsProperties();
        // 获取别名,通过反射创建实例
        DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
        // 赋值数据库连接池工厂属性
        factory.setProperties(props);
        return factory;
    }
    throw new BuilderException("Environment declaration requires a DataSourceFactory.");
}

根据数据源类型创建数据源工厂类

public class PooledDataSourceFactory extends UnpooledDataSourceFactory {
  public PooledDataSourceFactory() {
    // 创建 PooledDataSource
    this.dataSource = new PooledDataSource();
  }
}

public class PooledDataSource implements DataSource {
	public PooledDataSource() {
		dataSource = new UnpooledDataSource();
	}
    // 省略其他代码
}

设置数据源工厂属性值

// org/apache/ibatis/datasource/unpooled/UnpooledDataSourceFactory.java
public void setProperties(Properties properties) {
    Properties driverProperties = new Properties();
    // 为 dataSource 创建元信息对象
    MetaObject metaDataSource = SystemMetaObject.forObject(dataSource);
    // 遍历 properties 键列表,properties 由配置文件解析器传入
    for (Object key : properties.keySet()) {
        String propertyName = (String) key;
        // 检测 propertyName 是否以 "driver." 开头
        if (propertyName.startsWith(DRIVER_PROPERTY_PREFIX)) {
            String value = properties.getProperty(propertyName);
            // 存储配置信息到 driverProperties 中
            driverProperties.setProperty(propertyName.substring(DRIVER_PROPERTY_PREFIX_LENGTH), value);
        } else if (metaDataSource.hasSetter(propertyName)) {
            String value = (String) properties.get(propertyName);
            // 按需转换 value 类型
            Object convertedValue = convertValue(metaDataSource, propertyName, value);
            // 设置转换后的值到 UnpooledDataSourceFactory 指定属性中
            metaDataSource.setValue(propertyName, convertedValue);
        } else {
            throw new DataSourceException("Unknown DataSource property: " + propertyName);
        }
    }
    if (driverProperties.size() > 0) {
        // 设置 driverProperties 到 UnpooledDataSourceFactory 的 driverProperties 属性中
        metaDataSource.setValue("driverProperties", driverProperties);
    }
}

按需转换 value 类型

// org/apache/ibatis/datasource/unpooled/UnpooledDataSourceFactory.java
private Object convertValue(MetaObject metaDataSource, String propertyName, String value) {
    Object convertedValue = value;
    // 获取属性对应的 setter 方法的参数类型
    Class<?> targetType = metaDataSource.getSetterType(propertyName);
    // 按照 setter 方法的参数类型进行类型转换
    if (targetType == Integer.class || targetType == int.class) {
        convertedValue = Integer.valueOf(value);
    } else if (targetType == Long.class || targetType == long.class) {
        convertedValue = Long.valueOf(value);
    } else if (targetType == Boolean.class || targetType == boolean.class) {
        convertedValue = Boolean.valueOf(value);
    }
    return convertedValue;
}

获取数据库连接

执行crud操作时,在创建 Statement 步骤时会获取JDBC连接

// org/apache/ibatis/executor/SimpleExecutor.java
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    // 获取JDBC连接
    Connection connection = getConnection(statementLog);
    // 调用语句处理器的prepare方法
    stmt = handler.prepare(connection, transaction.getTimeout());
    // 为 Statement 设置 IN 参数
    handler.parameterize(stmt);
    return stmt;
}

获取JDBC连接

protected Connection getConnection(Log statementLog) throws SQLException {
    // 通过jdbc获取数据库连接
    Connection connection = transaction.getConnection();
    if (statementLog.isDebugEnabled()) {
        return ConnectionLogger.newInstance(connection, statementLog, queryStack);
    } else {
        return connection;
    }
}

委托事务管理器获取连接

// org/apache/ibatis/transaction/jdbc/JdbcTransaction.java
public Connection getConnection() throws SQLException {
    if (connection == null) {
        openConnection();
    }
    return connection;
}

打开连接,设置自动提交标识

// org/apache/ibatis/transaction/jdbc/JdbcTransaction.java
protected void openConnection() throws SQLException {
    if (log.isDebugEnabled()) {
        log.debug("Opening JDBC Connection");
    }
    // 获取连接
    connection = dataSource.getConnection();
    if (level != null) {
        // 设置隔离级别
        connection.setTransactionIsolation(level.getLevel());
    }
    // 设置提交模式
    setDesiredAutoCommit(autoCommmit);
}

非池化数据源获取连接

获取非池化连接的时序图: 该种数据源不具有池化特性。该种数据源每次会返回一个新的数据库连接,而非复用旧的连接

//  org/apache/ibatis/datasource/unpooled/UnpooledDataSource.java
public Connection getConnection() throws SQLException {
    // 通过用户名和密码获取连接
    return doGetConnection(username, password);
}

通过用户名和密码获取连接

//  org/apache/ibatis/datasource/unpooled/UnpooledDataSource.java
private Connection doGetConnection(String username, String password) throws SQLException {
    Properties props = new Properties();
    if (driverProperties != null) {
        props.putAll(driverProperties);
    }
    if (username != null) {
        // 存储 user 配置
        props.setProperty("user", username);
    }
    if (password != null) {
        // 存储 password 配置
        props.setProperty("password", password);
    }
    // 调用重载方法
    return doGetConnection(props);
}

设置用户名和密码到Properties对象后调用重载方法获取链接

//  org/apache/ibatis/datasource/unpooled/UnpooledDataSource.java
private Connection doGetConnection(Properties properties) throws SQLException {
    // 初始化数据库驱动
    initializeDriver();
    // 获取连接
    Connection connection = DriverManager.getConnection(url, properties);
    // 配置连接,包括自动提交以及事务等级
    configureConnection(connection);
    return connection;
}

初始化数据库驱动

// org/apache/ibatis/datasource/unpooled/UnpooledDataSource.java
private synchronized void initializeDriver() throws SQLException {
    // 检测缓存中是否包含了与 driver 对应的驱动实例
    if (!registeredDrivers.containsKey(driver)) {
        Class<?> driverType;
        try {
            // 加载驱动类型
            if (driverClassLoader != null) {
                // 使用 driverClassLoader 加载驱动
                driverType = Class.forName(driver, true, driverClassLoader);
            } else {
                // 通过其他 ClassLoader 加载驱动
                driverType = Resources.classForName(driver);
            }
            // DriverManager requires the driver to be loaded via the system ClassLoader.
            // http://www.kfu.com/~nsayer/Java/dyn-jdbc.html
            // 通过其他 ClassLoader 加载驱动
            Driver driverInstance = (Driver)driverType.newInstance();
            /*
             * 注册驱动,注意这里是将 Driver 代理类 DriverProxy 对象注册到 DriverManager 中的,
             * 而非 Driver 对象本身。DriverProxy 中并没什么特别的逻辑,就不分析。
             */
            DriverManager.registerDriver(new DriverProxy(driverInstance));
            // 缓存驱动类名和实例
            registeredDrivers.put(driver, driverInstance);
        } catch (Exception e) {
            throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e);
        }
    }
}

配置连接,包括自动提交以及事务等级

// org/apache/ibatis/datasource/unpooled/UnpooledDataSource.java
private void configureConnection(Connection conn) throws SQLException {
    if (autoCommit != null && autoCommit != conn.getAutoCommit()) {
        // 设置自动提交
        conn.setAutoCommit(autoCommit);
    }
    if (defaultTransactionIsolationLevel != null) {
        // 设置事务隔离级别
        conn.setTransactionIsolation(defaultTransactionIsolationLevel);
    }
}

池化数据源 PooledDataSource

辅助类介绍

  1. PoolState 用于记录连接池运行时的状态,比如连接获取次数,无效连接数量等。同时 PoolState 内部定义了两个 PooledConnection 集合,用于存储空闲连接和活跃连接
public class PoolState {

  protected PooledDataSource dataSource;

  // 空闲连接列表
  protected final List<PooledConnection> idleConnections = new ArrayList<PooledConnection>();
  // 活跃连接列表
  protected final List<PooledConnection> activeConnections = new ArrayList<PooledConnection>();
  // 活跃连接列表
  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;

  public PoolState(PooledDataSource dataSource) {
    this.dataSource = dataSource;
  }
  // 省略部分代码
}
  1. PooledConnection 内部定义了一个 Connection 类型的变量,用于指向真实的数据库连接。以及一个 Connection 的代理类,用于对部分方法调用进行拦截。
class PooledConnection implements InvocationHandler {

  private static final String CLOSE = "close";
  private static final Class<?>[] IFACES = new Class<?>[] { Connection.class };

  private final int hashCode;
  private final PooledDataSource dataSource;
  // 真实的数据库连接
  private final Connection realConnection;
  // 数据库连接代理
  private final Connection proxyConnection;
  // 从连接池中取出连接时的时间戳
  private long checkoutTimestamp;
  // 数据库连接创建时间
  private long createdTimestamp;
  // 数据库连接最后使用时间
  private long lastUsedTimestamp;
  // connectionTypeCode = (url + username + password).hashCode()
  private int connectionTypeCode;
  // 表示连接是否有效
  private boolean valid;

  public PooledConnection(Connection connection, PooledDataSource dataSource) {
    this.hashCode = connection.hashCode();
    this.realConnection = connection;
    this.dataSource = dataSource;
    this.createdTimestamp = System.currentTimeMillis();
    this.lastUsedTimestamp = System.currentTimeMillis();
    this.valid = true;
    // 创建 Connection 的代理类对象
    this.proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), IFACES, this);
  }

  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {...}

  // 省略部分代码
}

池化数据连接的获取时序图:

时序图中只画出创建池连接的过程。

获取连接

// org/apache/ibatis/datasource/pooled/PooledDataSource.java
public Connection getConnection() throws SQLException {
    // 返回 Connection 的代理对象
    return popConnection(dataSource.getUsername(), dataSource.getPassword()).getProxyConnection();
}

先获取连接

// org/apache/ibatis/datasource/pooled/PooledDataSource.java
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) {
            // 检测空闲连接集合(idleConnections)是否为空
            if (!state.idleConnections.isEmpty()) {
                // idleConnections 不为空,表示有空闲连接可以使用
                conn = state.idleConnections.remove(0);
                if (log.isDebugEnabled()) {
                    log.debug("Checked out connection " + conn.getRealHashCode() + " from pool.");
                }
            } else {
                /*
                 * 暂无空闲连接可用,但如果活跃连接数还未超出限制
                 *(poolMaximumActiveConnections),则可创建新的连接
                 */
                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) {
                        // Can claim overdue connection
                        // 累加超时相关的统计字段
                        state.claimedOverdueConnectionCount++;
                        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");
                            }
                        }

                        /*
                         * 创建一个新的 PooledConnection,注意,
                         * 此处复用 oldestActiveConnection 的 realConnection 变量
                         */
                        conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);
                        /*
                         * 复用 oldestActiveConnection 的一些信息,注意 PooledConnection 中的
                         * createdTimestamp 用于记录 Connection 的创建时间,而非 PooledConnection
                         * 的创建时间。所以这里要复用原连接的时间信息。
                         */
                        conn.setCreatedTimestamp(oldestActiveConnection.getCreatedTimestamp());
                        conn.setLastUsedTimestamp(oldestActiveConnection.getLastUsedTimestamp());

                        // 设置旧的连接为无效状态
                        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) {
                // ping to server and check the connection is valid or not
                /*
                 * 检测连接是否有效,isValid 方法除了会检测 valid 是否为 true,
                 * 还会通过 PooledConnection 的 pingConnection 方法执行 SQL 语句,
                 * 检测连接是否可用。pingConnection 方法的逻辑不复杂,大家可以自行分析。
                 * 另外,官方文档在介绍 POOLED 类型数据源时,也介绍了连接有效性检测方面的
                 * 属性,有三个:poolPingQuery,poolPingEnabled 和
                 * poolPingConnectionsNotUsedFor。关于这三个属性,大家可以查阅官方文档
                 */
                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 + 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;
}

以上逻辑的伪代码实现

if (连接池中有空闲连接) {
    1. 将连接从空闲连接集合中移除并返回
} else {
    if (活跃连接数未超出限制) {
        1. 创建新连接
    } else {
        1. 从活跃连接集合中取出第一个元素
        2. 获取连接运行时长

        if (连接超时) {
            1. 将连接从活跃集合中移除
            2. 复用原连接的成员变量,并创建新的 PooledConnection 对象
        } else {
            1. 线程进入等待状态
            2. 线程被唤醒后,重新执行以上逻辑
        }
    }
}

popConnection 实现流程图

创建一个新的 PooledConnection

// org/apache/ibatis/datasource/pooled/PooledConnection.java
public PooledConnection(Connection connection, PooledDataSource dataSource) {
    this.hashCode = connection.hashCode();
    this.realConnection = connection;
    this.dataSource = dataSource;
    this.createdTimestamp = System.currentTimeMillis();
    this.lastUsedTimestamp = System.currentTimeMillis();
    this.valid = true;
    // 创建 Connection 的代理类对象
    this.proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), IFACES, this);
}

检测连接是否有效

// org/apache/ibatis/datasource/pooled/PooledConnection.java
public boolean isValid() {
    return valid && realConnection != null && dataSource.pingConnection(this);
}

通过PING检测连接是否有效

// org/apache/ibatis/datasource/pooled/PooledDataSource.java
protected boolean pingConnection(PooledConnection conn) {
    boolean result = true;

    try {
        result = !conn.getRealConnection().isClosed();
    } catch (SQLException e) {
        if (log.isDebugEnabled()) {
            log.debug("Connection " + conn.getRealHashCode() + " is BAD: " + e.getMessage());
        }
        result = false;
    }

    if (result) {
        if (poolPingEnabled) {
            // ping检测时间间隔 >= 0 && 最后一次使用时间 > ping检测时间间隔
            if (poolPingConnectionsNotUsedFor >= 0 && conn.getTimeElapsedSinceLastUse() > poolPingConnectionsNotUsedFor) {
                try {
                    if (log.isDebugEnabled()) {
                        log.debug("Testing connection " + conn.getRealHashCode() + " ...");
                    }
                    Connection realConn = conn.getRealConnection();
                    Statement statement = realConn.createStatement();
                    // 检查连接正确的语句,默认为"NO PING QUERY SET",即没有,使用会导致抛异常
                    ResultSet rs = statement.executeQuery(poolPingQuery);
                    rs.close();
                    statement.close();
                    if (!realConn.getAutoCommit()) {
                        realConn.rollback();
                    }
                    result = true;
                    if (log.isDebugEnabled()) {
                        log.debug("Connection " + conn.getRealHashCode() + " is GOOD!");
                    }
                } catch (Exception e) {
                    log.warn("Execution of ping query '" + poolPingQuery + "' failed: " + e.getMessage());
                    try {
                        conn.getRealConnection().close();
                    } catch (Exception e2) {
                        //ignore
                    }
                    result = false;
                    if (log.isDebugEnabled()) {
                        log.debug("Connection " + conn.getRealHashCode() + " is BAD: " + e.getMessage());
                    }
                }
            }
        }
    }
    return result;
}

回收连接,获取连接时,会通过JDK的动态代理创建一个连接的代理对象,当调用连接关闭时会触发回调invoke方法

// org/apache/ibatis/datasource/pooled/PooledConnection.java
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    String methodName = method.getName();
    // 检测 close 方法是否被调用,若被调用则拦截之
    if (CLOSE.hashCode() == methodName.hashCode() && CLOSE.equals(methodName)) {
        // 将回收连接中,而不是直接将连接关闭
        dataSource.pushConnection(this);
        return null;
    } else {
        try {
            if (!Object.class.equals(method.getDeclaringClass())) {
                // issue #579 toString() should never fail
                // throw an SQLException instead of a Runtime
                checkConnection();
            }
            // 调用真实连接的目标方法
            return method.invoke(realConnection, args);
        } catch (Throwable t) {
            throw ExceptionUtil.unwrapThrowable(t);
        }
    }
}

回收连接

// org/apache/ibatis/datasource/pooled/PooledDataSource.java
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
                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++;
        }
    }
}

回收连接流程图