原创

MyBatis源码解析 - 数据源模块

前言

在数据持久层中,数据源是一一个非常重要的组件,其性能直接关系到整个数据持久层的性能。在实践中比较常见的第三方数据源组件有Apache Common DBCPC3P0Proxool等, MyBatis不仅可以集成第三方数据源组件,还提供了自己的数据源实现。
常见的数据源组件都实现了javax.sql.DataSource接口,MyBatis自身实现的数据源实现也不例外。MyBatis提供了两个javax.sql.DataSource接口实现,分别是PooledDataSourceUnpooledDataSourceMybatis使用不同的DataSourceFactory接口实现创建不同类型的DataSource,如图所示,这是工厂方法模式的一个典型应用。

工厂方法模式

在工厂方法模式中,定义了一个用于创建对象的工厂接口,并根据工厂接口的具体实现类决定具体实例化哪一个具体产品类。首先来看工厂方法模式的UML图,从整体上了解该模式的结构,如图所示。

工厂方法模式有四个角色构成。

  • 工厂接口(Factory):工厂接口是工厂方法模式的核心接口,调用者会直接与工厂接口交互用于获取具体的产品实现类。
  • 具体工厂类(ConcreteFactory):具体工厂类是工厂接口的实现类,用于实例化产品对象,不同的具体工厂类会根据需求实例化不同的产品实现类。
  • 产品接口(Product):产品接口用于定义产品类的功能,具体工厂类产生的所有产品对象都必须实现该接口。调用者一般会面向产品接口进行编程,所以产品接口会与调用者直接交互,也是调用者最为关心的接口。
  • 具体产品类(ConcreteProduct):实现产品接口的实现类,具体产品类中定义了具体的业务逻辑。

如果需要产生新的产品,例如对于MyBatis的数据源模块来说,就是添加新的第三方数据源组件,只需要添加对应的工厂实现类,新数据源就可以被MyBatis使用,而不必修改已有的代码。显然,工厂方法模式符合“开放-封闭”原则。除此之外,工厂方法会向调用者隐藏具体产品类的实例化细节,调用者只需要了解工厂接口和产品接口,面向这两个接口编程即可。工厂方法模式也是存在缺点的。在增加新产品实现类时,还要提供一个与之对应的工厂实现类,所以实际新增的类是成对出现的,这增加了系统的复杂度。另外,工厂方法模式引入了工厂接口和产品接口这一层抽象,调用者面向该抽象层编程,增加了程序的抽象性和理解难度。

DataSourceFactory

在数据源模块中, DataSourceFactory接口扮演工厂接口的角色。UnpooledDataSourceFactoryPooledDataSourceFactory则扮演着具体工厂类的角色。我们从DataSourceFactory接口开始分析,其定义如下:

public interface DataSourceFactory {

  //设置DataSource的相关属性,一般紧跟在初始化完成之后
  void setProperties(Properties props);
  //获取DataSource对象
  DataSource getDataSource();

}

UnpooledDataSourceFactory的构造函数中会直接创建UnpooledDataSource对象,并初始化UnpooledDataSourceFactory.dataSource字段。UnpooledDataSourceFactory.setProperties()方法完成对UnpooledDataSource对象的配置,代码如下:

public void setProperties(Properties properties) {
  Properties driverProperties = new Properties();
  //创建dataSource对应的MetaObject
  MetaObject metaDataSource = SystemMetaObject.forObject(dataSource);
  //遍历properties集合,该集合配置了数据源相关的一些属性
  for (Object key : properties.keySet()) {
    String propertyName = (String) key;
    if (propertyName.startsWith(DRIVER_PROPERTY_PREFIX)) {
      String value = properties.getProperty(propertyName);
      // 以"driver. "开头的配置项是对DataSource 的配置,记录到driverProperties 中保存
      driverProperties.setProperty(propertyName.substring(DRIVER_PROPERTY_PREFIX_LENGTH), value);
    } else if (metaDataSource.hasSetter(propertyName)) {
      String value = (String) properties.get(propertyName);
      //根据属性类型进行类型转换,主要是Integer、 Long、 Boolean三种类型的转换
      Object convertedValue = convertValue(metaDataSource, propertyName, value);
      //设置DataSource的相关属性值
      metaDataSource.setValue(propertyName, convertedValue);
    } else {
      throw new DataSourceException("Unknown DataSource property: " + propertyName);
    }
  }
  if (driverProperties.size() > 0) {
    //设置DataSource . driverProperties属性值
    metaDataSource.setValue("driverProperties", driverProperties);
  }
}

UnpooledDataSourceFactory.getDataSource()方法实现比较简单,它直接返回dataSource字段记录的UnpooledDataSource对象。
PooledDataSourceFactory继承了UnpooledDataSourceFactory, 但并没有覆盖setProperties()方法和getDataSource()方法。两者唯一的区别是PooledDataSourceFactory 的构造函数会将其dataSource字段初始化为PooledDataSource对象。
JndiDataSourceFactory是依赖JNDI服务从容器中获取用户配置的DataSource,其逻辑并不复杂,这里就不再赘述了。

UnpooledDataSource

javax.sql.DataSource接口在数据源模块中扮演了产品接口的角色,MyBatis提供了两个DataSource接口的实现类,分别是UnpooledDataSourcePooledDataSource,它们扮演着具体产品类的角色。
UnpooledDataSource实现了javax.sql.DataSource接口中定义的getConnection()方法及其重载方法,用于获取数据库连接。每次通过UnpooledDataSource. getConnection()方法获取数据库连接时都会创建一个 新连接。UnpooledDataSource 中的字段如下,每个字段都有对应的gtter/setter
方法:

//加载driver类的类加载亲
private ClassLoader driverClassLoader;
//数据库连接驱动相关的配置
private Properties driverProperties;
//缓存所有已注册的数据库连接驱动
private static Map<String, Driver> registeredDrivers = new ConcurrentHashMap<>();

//数据库连接驱动名称
private String driver;
//数据库URL
private String url;
//数据库用户名
private String username;
//数据库密码
private String password;

//是否自动提交
private Boolean autoCommit;
//事务隔离级别
private Integer defaultTransactionIsolationLevel;
//默认网络超时时间
private Integer defaultNetworkTimeout;

熟悉JDBC的小伙伴都知道,据库连接之前,需要先向DriverManager注册JDBC驱动类。我们以MySQL提供的JDBC驱动为例进行简单分析,com.mysql.jdbc.Driver 中有如下静态代码块:

static {
    try {
            //向DriverManager注册JDBC驱动
        java.sql.DriverManager.registerDriver(new Driver());
    } catch (SQLException E) {
        throw new RuntimeException("Can't register driver!");
    }
}

DriverManager中定义了registeredDrivers字段用于记录注册的JDBC驱动,定义如下:

private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();
public static synchronized void registerDriver(java.sql.Driver driver,
            DriverAction da)
        throws SQLException {

        /* Register the driver if it has not already been added to our list */
        if(driver != null) {
                //添加JDBC驱动
            registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
        } else {
                //抛出异常
            // This is for compatibility with the original DriverManager
            throw new NullPointerException();
        }

        println("registerDriver: " + driver);

}

下面回到MyBatisUnpooledDataSource的分析,UnpooledDataSource中定义了如下静态代码块,在UnpooledDataSource 加载时会通过该静态代码块将已在DriverManager 中注册的JDBCDriver复制一份到UnpooledDataSource.registeredDrivers集合中。

static {
  Enumeration<Driver> drivers = DriverManager.getDrivers();
  while (drivers.hasMoreElements()) {
    Driver driver = drivers.nextElement();
    registeredDrivers.put(driver.getClass().getName(), driver);
  }
}

UnpooledDataSource.getConection()方法的所有重载最终会调用UnpooledDataSource.doGetConnection()方法获取数据库连接,具体实现如下:

private Connection doGetConnection(Properties properties) throws SQLException {
  //初始化数据库驱动
  initializeDriver();
  //创建数据库连接
  Connection connection = DriverManager.getConnection(url, properties);
  //配置数据库隔离级别和自动提交
  configureConnection(connection);
  return connection;
}

UnpooledDataSource.initializeDriver()方法主要负责数据库驱动的初始化,该方法会创建配置中指定的Driver对象,并将其注册到DriverManager 以及上面介绍的UnpooledDataSource. registeredDrivers集合中保存。

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.getDeclaredConstructor().newInstance();  //创建Driver对象
      //注册驱动,DriverProxy是定义在UnpooledDataSource的内部类,是Driver的一个静态内部类
      DriverManager.registerDriver(new DriverProxy(driverInstance));
      //将驱动添加到registeredDrivers集合中
      registeredDrivers.put(driver, driverInstance);
    } catch (Exception e) {
      throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e);
    }
  }
}

private void configureConnection(Connection conn) throws SQLException {
  //设置超时
  if (defaultNetworkTimeout != null) {
    conn.setNetworkTimeout(Executors.newSingleThreadExecutor(), defaultNetworkTimeout);
  }
  if (autoCommit != null && autoCommit != conn.getAutoCommit()) {
    //设置自动提交
    conn.setAutoCommit(autoCommit);
  }
  if (defaultTransactionIsolationLevel != null) {
    //设置事务隔离级别
    conn.setTransactionIsolation(defaultTransactionIsolationLevel);
  }
}

PooledDataSource

数据库连接是非常珍贵的资源,使用数据库连接池就很重要了,使用数据库连接池有很多好处。例如:可以实现数据库连接的重用、提高响应速度、防止数据库连接过多造成数据库假死、避免数据库连接泄露等。

PooledDataSource实现了简易数据库连接池的功能,它依赖的组件如图所示,其中需要注意的是,PooledDataSource创建新数据库连接的功能是依赖其中封装的UnpooledDataSource对象实现的。

PooledConnection的核心字段如下:

//记录当前PooledConnection对象所在的PooledDataSource对象。该PooledConnection是从
//该PooledDataSource 中获取的;当调用close()方法时会将PooledConnection放回该
private final PooledDataSource dataSource;
//真正的数据库连接
private final Connection realConnection;
//数据库连接的代理对象
private final Connection proxyConnection;
//从连接池中取出该连接的时间戳
private long checkoutTimestamp;
//该连接创建的时间戳
private long createdTimestamp;
//最后一次被使用的时间戳
private long lastUsedTimestamp;
//由数据库URL、用户名和密码计算出来的hash值,可用于标识该连接所在的连接池
private int connectionTypeCode;
//检测当前PooledConnection是否有效,主要是为了防止程序通过close()方法将连接归还给连接池之后,依
//然通过该连接操作数据库
private boolean valid;

PooledConnection中提供了上述字段的getter/setter方法,代码比较简单。这里重点关注PooledConnection.invoke()方法的实现,该方法是proxyConnection这个连接代理对象的真正代理逻辑,它会对close()方法的调用进行代理,并且在调用真正数据库连接的方法之前进行检测,代码如下:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  String methodName = method.getName();
  //如果调用close方法,则将其重新放回连接池,而不是真正关闭数据库连接
  if (CLOSE.equals(methodName)) {
    dataSource.pushConnection(this);
    return null;
  }
  try {
    if (!Object.class.equals(method.getDeclaringClass())) {
      // issue #579 toString() should never fail
      // throw an SQLException instead of a Runtime
      checkConnection();  //通过valid字段判定连接是否有效
    }
    //调用真正的数据库连接对象的对应方法
    return method.invoke(realConnection, args);
  } catch (Throwable t) {
    throw ExceptionUtil.unwrapThrowable(t);
  }
}

PoolState是用于管理PooledConnection 对象状态的组件,它通过两个ArrayList<PooledConnection>集合分别管理空闲状态的连接和活跃状态的连接,定义如下:

//空闲的PooledConnection集合
protected final List<PooledConnection> idleConnections = new ArrayList<>();
//活跃的PooledConnection集合
protected final List<PooledConnection> activeConnections = new ArrayList<>();

PoolState还定义了一系列用于统计的字段,定义如下:

//请求数据库连接的次数
protected long requestCount = 0;
//获取连接的累计时间
protected long accumulatedRequestTime = 0;
// checkoutTime 表示应用从连接池中取出连接,到归还连接这段时长,
// accumulatedCheckoutTime记录了所有连接累积的checkoutTime 时长
protected long accumulatedCheckoutTime = 0;
//当连接长时间未归还给连接池时,会被认为该连接超时,
// claimedOverdueConnectionCount记录了超时的连接个数
protected long claimedOverdueConnectionCount = 0;
//累计超时时间
protected long accumulatedCheckoutTimeOfOverdueConnections = 0;
//累计等待时间
protected long accumulatedWaitTime = 0;
//等待次数
protected long hadToWaitCount = 0;
//无效的连接数
protected long badConnectionCount = 0;

PooledDataSource中管理的真正的数据库连接对象是由PooledDataSource中封装的UnpooledDataSource对象创建的,并由PoolState 管理所有连接的状态。PooledDataSource中核心字段的含义和功能如下:

//通过Poolstate管理连接池的状态并记录统计信息
private final PoolState state = new PoolState(this);
//记录UnpooledDataSource对象,用于生成真实的数据库连接对象,构造函数中会初始化该字段
private final UnpooledDataSource dataSource;

// OPTIONAL CONFIGURATION FIELDS
// 最大活跃连接数
protected int poolMaximumActiveConnections = 10;
//最大空闲连接数
protected int poolMaximumIdleConnections = 5;
//最大checkOut时长
protected int poolMaximumCheckoutTime = 20000;
//在无法获取连接时 线程等待时长
protected int poolTimeToWait = 20000;
protected int poolMaximumLocalBadConnectionTolerance = 3;
//在检测一个数据库连接是否可用时,会给数据库发送一个测试SQL语句
protected String poolPingQuery = "NO PING QUERY SET";
//是否允许发送测试SQL语句
protected boolean poolPingEnabled;
//当连接超过poolPingConnect ionsNotUsedFor毫秒未使用时,会发送一次测试SQL语句,检测连接是否正常
protected int poolPingConnectionsNotUsedFor;
//根据数据库的URL、 用户名和密码生成的一个hash值,该哈希值用于标志着当前的连接池,在构造函数中初始化
private int expectedConnectionTypeCode;

PooledDataSource.getConnection()方法首先会调用PooledDataSource.popConnection()方法获取PooledConnection对象,然后通过PooledConnection.getProxyConnection()方法获取数据库连接的代理对象。popConnection()方法是PooledDataSource的核心逻辑之一,其具体逻辑如图所示。

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 {
        //当前线程池没有空闲连接
        // Pool does not have available connection
        if (state.activeConnections.size() < poolMaximumActiveConnections) {
          // Can create new connection
          // 创建新数据库连接,并封装成PooledConnection对象
          conn = new PooledConnection(dataSource.getConnection(), this);
          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;
            //将超时连接移出oldestActiveConnection集合
            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 happened.
                   Wrap the bad connection with a new PooledConnection, this will help
                   to not interrupt current executing thread and give current thread a
                   chance to join the next competition 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对象 但是真正的数据库连接并未创建新的
            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 {
            //没有空闲 无法创建新连接 无超时连接 就等待
            // 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
        if (conn.isValid()) { //检测PooledConnection是否有效
          if (!conn.getRealConnection().getAutoCommit()) {
            conn.getRealConnection().rollback();
          }
          //配置PooledConnection相关属性 ConnectionTypeCode CheckoutTimestamp LastUsedTimestamp 等
          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;
}

通过前面对PooledConnection.invoke()方法的分析我们知道,当调用连接的代理对象的close()方法时,并未关闭真正的数据连接,而是调用PooledDataSource. pushConnection()方法将PooledConnection对象归还给连接池,供之后重用。PooledDataSource.pushConnection()方法也是PooledDataSource的核心逻辑之一,其逻辑如图所示。

PooledConnection.pushConnection()方法的具体实现如下:

protected void pushConnection(PooledConnection conn) throws SQLException {
  synchronized (state) {  //同步
    //从activeConnections集合移除该PooledConnection对象
    state.activeConnections.remove(conn);
    if (conn.isValid()) { //检测PooledConnection是否有效
      //检测空闲连接数量是否已到上限 以及PooledConnection是否是连接池的连接
      if (state.idleConnections.size() < poolMaximumIdleConnections && conn.getConnectionTypeCode() == expectedConnectionTypeCode) {
        //累积checkout的时长
        state.accumulatedCheckoutTime += conn.getCheckoutTime();
        if (!conn.getRealConnection().getAutoCommit()) {
          //回滚未提交的事务
          conn.getRealConnection().rollback();
        }
        //未返还连接创建新的PooledConnection对象
        PooledConnection newConn = new PooledConnection(conn.getRealConnection(), this);
        state.idleConnections.add(newConn); //添加到空闲连接idleConnections集合
        newConn.setCreatedTimestamp(conn.getCreatedTimestamp());
        newConn.setLastUsedTimestamp(conn.getLastUsedTimestamp());
        conn.invalidate();  //将原来的连接设置为无效
        if (log.isDebugEnabled()) {
          log.debug("Returned connection " + newConn.getRealHashCode() + " to pool.");
        }
        //唤醒其他阻塞的线程
        state.notifyAll();
      } else {
        //空闲连接数已达到上限或PooledConnection对象并不属于该连接池
        state.accumulatedCheckoutTime += conn.getCheckoutTime();  //累积checkout时长
        if (!conn.getRealConnection().getAutoCommit()) {
          //未自动提交的 回滚
          conn.getRealConnection().rollback();
        }
        //关闭真正的数据库连接
        conn.getRealConnection().close();
        if (log.isDebugEnabled()) {
          log.debug("Closed connection " + conn.getRealHashCode() + ".");
        }
        //将PooledConnection对象设置为无效
        conn.invalidate();
      }
    } else {
      if (log.isDebugEnabled()) {
        log.debug("A bad connection (" + conn.getRealHashCode() + ") attempted to return to the pool, discarding connection.");
      }
      //统计无效PooledConnection对象的个数
      state.badConnectionCount++;
    }
  }
}

这里需要注意的是,PooledDataSource.pushConnection()方法和popConnetion()方法中都调用了PooledConnection.isValid()方法来检测PooledConnection的有效性,该方法除了检测
PooledConnection.valid字段的值,还会调用PooledDataSource.pingConnection()方法尝 试让数据库执行poolPingQuery 字段中记录的测试SQL语句,从而检测真正的数据库连接对象是否依然
可以正常使用。isValid()方法以及pingConnection()方法的代码如下:

public boolean isValid() {
  //检测真正的数据库连接是否关闭
  return valid && realConnection != null && dataSource.pingConnection(this);
}
protected boolean pingConnection(PooledConnection conn) {
    boolean result = true;  //记录ping操作是否成功
    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) {  //检测poolPingEnabled设置,是否执行测试SQL语句。
        //长时间(超过poolPingConnect ionsNotUsedFor指定的时长)未使用的连接,才需要ping
        // 操作来检测数据库连接是 否正常
        if (poolPingConnectionsNotUsedFor >= 0 && conn.getTimeElapsedSinceLastUse() > poolPingConnectionsNotUsedFor) {
          try {
            if (log.isDebugEnabled()) {
              log.debug("Testing connection " + conn.getRealHashCode() + " ...");
            }
            //下面是执行测试SQL语句的JDBC操作,不多做解释
            Connection realConn = conn.getRealConnection();
            try (Statement statement = realConn.createStatement()) {
              statement.executeQuery(poolPingQuery).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;
}

最后需要注意的是PooledDataSource.forceCloseAll()方法,当修改PooledDataSource 的字段时,例如数据库URL、用户名、密码、autoCommit配置等,都会调用forceCloseAll()方法将所有数据库连接关闭,同时也会将所有相应的PooledConnection对象都设置为无效,清空activeConnections集合和idleConnections集合。应用系统之后通过PooledDataSource.getConnection()获取连接时,会按照新的配置重新创建新的数据库连接以及相应的PooledConnection对象。forceCloseAll()方法的具体实现如下:

public void forceCloseAll() {
  synchronized (state) {
    //更新当前连接池的标识
    expectedConnectionTypeCode = assembleConnectionTypeCode(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
    for (int i = state.activeConnections.size(); i > 0; i--) {  //处理全部的活跃连接
      try {
        //从PooledState.activeConnections集合中获取PooledConnection对象
        PooledConnection conn = state.activeConnections.remove(i - 1);
        //将PooledConnection设为无效
        conn.invalidate();

        Connection realConn = conn.getRealConnection(); //获取真正的数据库连接
        if (!realConn.getAutoCommit()) {
          //回滚未提交的事务
          realConn.rollback();
        }
        //关闭真正的数据库连接
        realConn.close();
      } catch (Exception e) {
        // ignore
      }
    }
    // 上面同样的逻辑处理从PooledState.idleConnections集合  空闲连接
    for (int i = state.idleConnections.size(); i > 0; i--) {
      try {
        PooledConnection conn = state.idleConnections.remove(i - 1);
        conn.invalidate();

        Connection realConn = conn.getRealConnection();
        if (!realConn.getAutoCommit()) {
          realConn.rollback();
        }
        realConn.close();
      } catch (Exception e) {
        // ignore
      }
    }
  }
  if (log.isDebugEnabled()) {
    log.debug("PooledDataSource forcefully closed/removed all connections.");
  }
}
正文到此结束
该篇文章的评论功能已被站长关闭