Mybatis-数据源源码解析

Mybatis3.5.1源码分析

  1. Mybatis-SqlSessionFactoryBuilder,XMLConfigBuilder,XPathParser源码解析
  2. Mybatis-Configuration源码解析
  3. Mybatis-事务对象源码解析
  4. Mybatis-数据源源码解析
  5. Mybatis缓存策略源码解析
  6. Mybatis-DatabaseIdProvider源码解析
  7. Mybatis-TypeHandler源码解析
  8. Mybatis-Reflector源码解析
  9. Mybatis-ObjectFactory,ObjectWrapperFactory源码分析
  10. Mybatis-Mapper各类标签封装类源码解析
  11. Mybatis-XMLMapperBuilder,XMLStatmentBuilder源码分析
  12. Mybatis-MapperAnnotationBuilder源码分析
  13. [Mybatis-MetaObject,MetaClass源码解析]//www.greatytc.com/p/f51fa552f30a)
  14. Mybatis-LanguageDriver源码解析
  15. Mybatis-SqlSource源码解析
  16. Mybatis-SqlNode源码解析
  17. Mybatis-KeyGenerator源码解析
  18. Mybatis-Executor源码解析
  19. Mybatis-ParameterHandler源码解析
  20. Mybatis-StatementHandler源码解析
  21. Mybatis-DefaultResultSetHandler(一)源码解析
  22. Mybatis-DefaultResultSetHandler(二)源码解析
  23. Mybatis-ResultHandler,Cursor,RowBounds 源码分析
  24. Mybatis-MapperProxy源码解析
  25. Mybatis-SqlSession源码解析
  26. Mybatis-Interceptor源码解析

DataSourceFactory

/**
 *    Copyright 2009-2015 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.datasource;

import java.util.Properties;
import javax.sql.DataSource;

/**
 * 数据源工厂
 * @author Clinton Begin
 */
public interface DataSourceFactory {

  /**
   * 设置属性
   * @param props 属性集
   */
  void setProperties(Properties props);

  /**
   * 获取数据源
   * @return
   */
  DataSource getDataSource();

}

JndiDataSourceFactory

/**
 *    Copyright 2009-2016 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.datasource.jndi;

import java.util.Map.Entry;
import java.util.Properties;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

import org.apache.ibatis.datasource.DataSourceException;
import org.apache.ibatis.datasource.DataSourceFactory;

/**
 * jndi的数据源
 * https://blog.csdn.net/reliveit/article/details/45136297
 *
 *
 * @author Clinton Begin
 */
public class JndiDataSourceFactory implements DataSourceFactory {

  /**
   * 服务器的JNDI目录,不同的服务器该值不同,因此需要在mybatis-config的配置文件中传入该值
   */
  public static final String INITIAL_CONTEXT = "initial_context";
  /**
   * 对应着META-INF/context.xml中注册的服务名称(name属性值),即键值对中的键值
   */
  public static final String DATA_SOURCE = "data_source";
  public static final String ENV_PREFIX = "env.";

  private DataSource dataSource;

  /**
   * 该方法在初始化Mybatis的时候被调用,会将mybatis-config.xml中配置的属性注入进来
   * 这里除了获取属性以外,还根据属性,获取dataSource属性
   * @param properties
   */
  @Override
  public void setProperties(Properties properties) {
    try {
      //声明JAVA命名和目录接口的上下文类
      InitialContext initCtx;
      // properties在SqlSessionFactoryBuilder创建SqlSessionFactory的过程中收集<dataSource>标签下属性建立
      // env不为null的条件是在mybatis-config.xml中配置的JNDI属性以"env."开头
      // 其实不必要以"env."开头,在getEnvProperties方法中最终也会去掉"env."
      Properties env = getEnvProperties(properties);
      if (env == null) {
        // 进入到这个流程,默认使用SqlSessionFactoryBuilder流程中的properties
        initCtx = new InitialContext();
      } else {
        // 如果配置文件中配置的JNDI属性以"env."开头,则进入这个步骤
        initCtx = new InitialContext(env);
      }
      /**
       * mybatis-config.xml中有两种方式可以进行JNDI数据源的配置
       * 1. 第一种方式需要配置initial_context和data_source的值,本例中
       *      initial_context="java:/comp/env"
       *      data_source="jdbc/mybatis-jndi"
       * 2. 第二种方式只需要配置data_source的值
       *      data_source="java:/comp/env/jdbc/mybatis-jndi"
       *
       * 结论:其实是一样的,请结合context.xml配置文件内容查看此处
       */
      if (properties.containsKey(INITIAL_CONTEXT)
          && properties.containsKey(DATA_SOURCE)) {
        Context ctx = (Context) initCtx.lookup(properties.getProperty(INITIAL_CONTEXT));
        dataSource = (DataSource) ctx.lookup(properties.getProperty(DATA_SOURCE));
      } else if (properties.containsKey(DATA_SOURCE)) {
        dataSource = (DataSource) initCtx.lookup(properties.getProperty(DATA_SOURCE));
      }

    } catch (NamingException e) {
      throw new DataSourceException("There was an error configuring JndiDataSourceTransactionPool. Cause: " + e, e);
    }
  }

  /**
   * 直接返回数据源
   *      因为数据源交由服务器托管,因此mybatis不需要再像pooled类型那样自己实现连接池并通过动态代理增强java.sql.Connection
   */
  @Override
  public DataSource getDataSource() {
    return dataSource;
  }

  /**
   * 获取env.属性,可以以通过添加前缀"env."直接把属性传递给初始上下文,如env.encoding=UTF8
   * 这就会在初始上下文(InitialContext)实例化时往它的构造方法传递值为 UTF8 的 encoding 属性。
   *
   * // 如果配置文件中配置的JNDI属性以"env."开头,那么就新建一个properties
   *     // 最后再把"evn."去掉放入properties中
   *     // 现在两个properties一模一样了,只是构造InitalContext的时候不同
   *     // 这个感觉是比较鸡肋的方法,也许是javax.naming中有优化吧
   */
  private static Properties getEnvProperties(Properties allProps) {
    final String PREFIX = ENV_PREFIX;
    Properties contextProperties = null;
    for (Entry<Object, Object> entry : allProps.entrySet()) {
      String key = (String) entry.getKey();
      String value = (String) entry.getValue();
      if (key.startsWith(PREFIX)) {
        if (contextProperties == null) {
          contextProperties = new Properties();
        }
        contextProperties.put(key.substring(PREFIX.length()), value);
      }
    }
    return contextProperties;
  }

}

UnpooledDataSourceFactory

/**
 *    Copyright 2009-2015 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.datasource.unpooled;

import java.util.Properties;

import javax.sql.DataSource;

import org.apache.ibatis.datasource.DataSourceException;
import org.apache.ibatis.datasource.DataSourceFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;

/**
 * 构建{@link UnpooledDataSource} 工厂类
 * @author Clinton Begin
 */
public class UnpooledDataSourceFactory implements DataSourceFactory {

  /**
   * 数据库驱动前缀
   */
  private static final String DRIVER_PROPERTY_PREFIX = "driver.";
  /**
   * 数据库驱动前缀长度
   */
  private static final int DRIVER_PROPERTY_PREFIX_LENGTH = DRIVER_PROPERTY_PREFIX.length();

  protected DataSource dataSource;

  public UnpooledDataSourceFactory() {
    this.dataSource = new UnpooledDataSource();
  }

  /**
   * 将mybatis-config设置的相关属性设置到(成员变量dataSource)中
   * @param properties
   */
  @Override
  public void setProperties(Properties properties) {
    Properties driverProperties = new Properties();
    MetaObject metaDataSource = SystemMetaObject.forObject(dataSource);
    for (Object key : properties.keySet()) {
      String propertyName = (String) key;
      // 以 "driver." 开头的配置项是对 DataSource 的配置,记录到 driverProperties  中
      if (propertyName.startsWith(DRIVER_PROPERTY_PREFIX)) {
        String value = properties.getProperty(propertyName);
        driverProperties.setProperty(propertyName.substring(DRIVER_PROPERTY_PREFIX_LENGTH), value);
      } else if (metaDataSource.hasSetter(propertyName)) {//判断dataSource中又可以属性的set方法
        String value = (String) properties.get(propertyName);
        Object convertedValue = convertValue(metaDataSource, propertyName, value);
        metaDataSource.setValue(propertyName, convertedValue);
      } else {
        throw new DataSourceException("Unknown DataSource property: " + propertyName);
      }
    }
    if (driverProperties.size() > 0) {
      metaDataSource.setValue("driverProperties", driverProperties);
    }
  }

  @Override
  public DataSource getDataSource() {
    return dataSource;
  }

  /**
   * 转换(参数value)的类型,以防止反射时候的类型不匹配问题
   * <p>
   *     将(参数propertyName)传入(参数metaDataSoure)的{@link MetaObject#getSetterType(String)}方法,得到
   *     对应(参数propertyName)的类属性的setter方法的参数类型并赋值(变量targetType),然后判断(变量targertType)
   *     属于哪个类型,就转换将(参数value)封装成哪个类型的对象,目前只做了三个基本类型的转换:
   *     <ul>
   *         <li>Integer.class || int.class => Integer.valueOf(value)</li>
   *         <li>Long.class || long.class => Long.valueOf(value)</li>
   *         <li>Boolean.class || boolean.class => Boolean.valueOf(value)</li>
   *         <li>default => value </li>
   *     </ul>
   * </p>
   * @param metaDataSource
   * @param propertyName
   * @param value
   * @return
   */
  private Object convertValue(MetaObject metaDataSource, String propertyName, String value) {
    Object convertedValue = value;
    Class<?> targetType = metaDataSource.getSetterType(propertyName);
    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;
  }

}

PooledDataSourceFactory

/**
 *    Copyright 2009-2015 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.datasource.pooled;

import org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory;

/**
 * 构建{@link PooledDataSource} 工厂类
 * <p>
 *     该工厂类继承 {@link UnpooledDataSourceFactory} ,只是重写了构造方法,将 {@link PooledDataSource}
 *     赋值给(成员变量dataSource)。也是说{@link UnpooledDataSourceFactory} 的功能该类也有。
 *
 * </p>
 * @author Clinton Begin
 */
public class PooledDataSourceFactory extends UnpooledDataSourceFactory {

  public PooledDataSourceFactory() {
    this.dataSource = new PooledDataSource();
  }

}

DataSource

Mybatis继承javax.sql.DataSourc接口进行实现

PooledDataSource

/**
 *    Copyright 2009-2019 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.datasource.pooled;

import java.io.PrintWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import java.util.logging.Logger;

import javax.sql.DataSource;

import org.apache.ibatis.datasource.unpooled.UnpooledDataSource;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;

/**
 * This is a simple, synchronous, thread-safe database connection pool.
 * 具有一个简单,同步,线程安全数据库连接池的数据源
 * @author Clinton Begin
 */
public class PooledDataSource implements DataSource {

  /**
   * 默认配置Log,可从mybatis-config上配置
   */
  private static final Log log = LogFactory.getLog(PooledDataSource.class);

  /**
   * 当前连接池的状态信息
   */
  private final PoolState state = new PoolState(this);

  /**
   * 无连接池数据源
   */
  private final UnpooledDataSource dataSource;

  // OPTIONAL CONFIGURATION FIELDS
  /**
   * 最大活跃连接数
   */
  protected int poolMaximumActiveConnections = 10;
  /**
   * 最大空闲连接数
   */
  protected int poolMaximumIdleConnections = 5;

  /**
   * 最大可回收时间,即当达到最大活动链接数时,此时如果有程序获取连接,则检查最先使用的连接,看其是否超出了该时间,如果超出了该时间,则可以回收该连接。(默认20s)
   */
  protected int poolMaximumCheckoutTime = 20000;

  /**
   *  在无法获取连接时,线程需要等待的时间
   */
  protected int poolTimeToWait = 20000;

  /**
   * 这是一个关于坏连接容忍度的底层设置, 作用于每一个尝试从缓存池获取连接的线程。
   *  如果这个线程获取到的是一个坏的连接,那么这个数据源允许这个线程尝试重新获取一个新的连接但是这个重新尝试的次数不应该超过
   *  (最大空闲连接数poolMaximumIdleConnections)与poolMaximumLocalBadConnectionTolerance之和。
   */
  protected int poolMaximumLocalBadConnectionTolerance = 3;

  /**
   * 检查连接正确的语句,默认为"NO PING QUERY SET",即没有,使用会导致抛异常
   */
  protected String poolPingQuery = "NO PING QUERY SET";

  /**
   * 是否开启ping检测,(默认:false)
   */
  protected boolean poolPingEnabled;

  /**
   * 设置ping检测时间间隔,通常用于检测超时连接(默认为0,即当开启检测后每次从连接词中获取连接以及放回连接池都需要检测)
   */
  protected int poolPingConnectionsNotUsedFor;

  /**
   * 预期的连接类型代码
   * <p>
   *     就是url+username+password组成的字符串所得到hashCode,
   *     详情请看 {@link PooledDataSource#assembleConnectionTypeCode(String, String, String)}
   * </p>
   */
  private int expectedConnectionTypeCode;

  public PooledDataSource() {
    dataSource = new UnpooledDataSource();
  }

  public PooledDataSource(UnpooledDataSource dataSource) {
    this.dataSource = dataSource;
  }

  public PooledDataSource(String driver, String url, String username, String password) {
    dataSource = new UnpooledDataSource(driver, url, username, password);
    expectedConnectionTypeCode = assembleConnectionTypeCode(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
  }

  public PooledDataSource(String driver, String url, Properties driverProperties) {
    dataSource = new UnpooledDataSource(driver, url, driverProperties);
    expectedConnectionTypeCode = assembleConnectionTypeCode(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
  }

  public PooledDataSource(ClassLoader driverClassLoader, String driver, String url, String username, String password) {
    dataSource = new UnpooledDataSource(driverClassLoader, driver, url, username, password);
    expectedConnectionTypeCode = assembleConnectionTypeCode(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
  }

  public PooledDataSource(ClassLoader driverClassLoader, String driver, String url, Properties driverProperties) {
    dataSource = new UnpooledDataSource(driverClassLoader, driver, url, driverProperties);
    expectedConnectionTypeCode = assembleConnectionTypeCode(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
  }

  /**
   * 获取数据库连接,返回的是 {@link PooledConnection#getProxyConnection()}
   * <p>
   *     取 {@link PooledDataSource#dataSource} 的 {@link UnpooledDataSource#getUsername()} 和 {@link UnpooledDataSource#getUsername()}
   *     传入 {@link PooledDataSource#popConnection(String, String)} 得到 {@link PooledConnection} ,再调用
   *      {@link PooledConnection#getProxyConnection()} 返回出去
   * </p>
   */
  @Override
  public Connection getConnection() throws SQLException {
    return popConnection(dataSource.getUsername(), dataSource.getPassword()).getProxyConnection();
  }

  /**
   * 获取数据库连接,返回的是 {@link PooledConnection#getProxyConnection()}
   * <p>
   *     取 (参数username) 和 (参数password) 传入 {@link PooledDataSource#popConnection(String, String)} 得到
   *      {@link PooledConnection} ,再调用 {@link PooledConnection#getProxyConnection()} 返回出去
   * </p>
   */
  @Override
  public Connection getConnection(String username, String password) throws SQLException {
    return popConnection(username, password).getProxyConnection();
  }

  /**
   * 设置登录超时时长,直接调用 {@link DriverManager#setLoginTimeout(int)}
   */
  @Override
  public void setLoginTimeout(int loginTimeout) {
    DriverManager.setLoginTimeout(loginTimeout);
  }

  /**
   * 获取登录超时时长,直接调用 {@link DriverManager#getLoginTimeout()}
   * @return
   */
  @Override
  public int getLoginTimeout() {
    return DriverManager.getLoginTimeout();
  }

  /**
   * 设置Log输出流
   *  <p>直接调用{@link DriverManager#setLogWriter(PrintWriter)}</p>
   */
  @Override
  public void setLogWriter(PrintWriter logWriter) {
    DriverManager.setLogWriter(logWriter);
  }

  /**
   * 获取Log输出流
   *  <p>直接调用{@link DriverManager#getLogWriter()}</p>
   */
  @Override
  public PrintWriter getLogWriter() {
    return DriverManager.getLogWriter();
  }

  /**
   * 设置驱动
   * <p>
   *     直接调用 {@link UnpooledDataSource#setDriver(String)} ,并且调用 {@link PooledDataSource#forceCloseAll()}
   * </p>
   */
  public void setDriver(String driver) {
    dataSource.setDriver(driver);
    forceCloseAll();
  }

  /**
   * 设置数据库地址
   * <p>
   *     直接调用 {@link UnpooledDataSource#setUrl(String)} ,并且调用 {@link PooledDataSource#forceCloseAll()}
   * </p>
   */
  public void setUrl(String url) {
    dataSource.setUrl(url);
    forceCloseAll();
  }

  /**
   * 设置数据库用户名
   * <p>
   *     直接调用 {@link UnpooledDataSource#setUsername(String)} ,并且调用 {@link PooledDataSource#forceCloseAll()}
   * </p>
   */
  public void setUsername(String username) {
    dataSource.setUsername(username);
    forceCloseAll();
  }

  /**
   * 设置数据库密码
   * <p>
   *     直接调用 {@link UnpooledDataSource#setPassword(String)} ,并且调用 {@link PooledDataSource#forceCloseAll()}
   * </p>
   */
  public void setPassword(String password) {
    dataSource.setPassword(password);
    forceCloseAll();
  }

  /**
   * 设置自动提交
   * <p>
   *     直接调用 {@link UnpooledDataSource#setAutoCommit(Boolean)} ,并且调用 {@link PooledDataSource#forceCloseAll()},
   *     虽然我认为关闭连接池中的所有连接也行,因为可以调用所有连接的{@link UnpooledDataSource#setAutoCommit(Boolean)}
   *     但是既然写了,肯定有作者的用意吧,毕竟对应更改配置的事情,还是关闭重新来过好一点
   * </p>
   */
  public void setDefaultAutoCommit(boolean defaultAutoCommit) {
    dataSource.setAutoCommit(defaultAutoCommit);
    forceCloseAll();
  }

  /**
   * 设置事务隔离级别
   * <p>
   *     直接调用 {@link UnpooledDataSource#setDefaultTransactionIsolationLevel(Integer)} ,
   *     并且调用 {@link PooledDataSource#forceCloseAll()},
   * </p>
   */
  public void setDefaultTransactionIsolationLevel(Integer defaultTransactionIsolationLevel) {
    dataSource.setDefaultTransactionIsolationLevel(defaultTransactionIsolationLevel);
    forceCloseAll();
  }

  /**
   * 设置驱动属性
   * <p>
   *     直接调用 {@link UnpooledDataSource#setDriverProperties(Properties)} ,
   *     并且调用 {@link PooledDataSource#forceCloseAll()},
   * </p>
   */
  public void setDriverProperties(Properties driverProps) {
    dataSource.setDriverProperties(driverProps);
    forceCloseAll();
  }

  /**
   * Sets the default network timeout value to wait for the database operation to complete. See {@link Connection#setNetworkTimeout(java.util.concurrent.Executor, int)}
   * 设置网络连接超时时长
   * <p>
   *     直接调用 {@link UnpooledDataSource#setDefaultNetworkTimeout(Integer)} ,
   *      并且调用 {@link PooledDataSource#forceCloseAll()},
   * </p>
   * @param milliseconds
   *          The time in milliseconds to wait for the database operation to complete.
   * @since 3.5.2
   */
  public void setDefaultNetworkTimeout(Integer milliseconds) {
    dataSource.setDefaultNetworkTimeout(milliseconds);
    forceCloseAll();
  }

  /**
   * The maximum number of active connections.
   * 设置池的最大活动连接数
   * <p>
   *     直接对 (最大活动连接数poolMaximumActiveConnections)附上 (参数poolMaximumActiveConnections),
   *     并且调用 {@link PooledDataSource#forceCloseAll()},
   *     我认为,不关闭连接池中的所有连接也行,因为下次触发获取连接需要创建连接时,
   *     根据poolMaximumActiveConnections来就创建可以,又或者根据poolMaximumActiveConnections的值去关闭多余的活跃连接,
   *     没必要说所有关闭连接,毕竟创建连接比较耗资源和耗时。
   * </p>
   * @param poolMaximumActiveConnections The maximum number of active connections
   */
  public void setPoolMaximumActiveConnections(int poolMaximumActiveConnections) {
    this.poolMaximumActiveConnections = poolMaximumActiveConnections;
    forceCloseAll();
  }

  /**
   * The maximum number of idle connections.
   * 设置池的最大空闲连接数
   * <p>
   *      直接对 (最大空闲连接数poolMaximumIdleConnections)附上 (参数poolMaximumIdleConnections),
   *      并且调用 {@link PooledDataSource#forceCloseAll()}, 我认为,不关闭连接池中的所有连接也行,
   *      因为下次触发连接需要创建连接时,根据poolMaximumIdleConnections来就创建可以,
   *      又或者根据poolMaximumIdleConnections的值去关闭多余的空闲连接,没必要说关闭所有连接,毕竟创建连接比较耗资源和耗时。
   * </p>
   * @param poolMaximumIdleConnections The maximum number of idle connections
   */
  public void setPoolMaximumIdleConnections(int poolMaximumIdleConnections) {
    this.poolMaximumIdleConnections = poolMaximumIdleConnections;
    forceCloseAll();
  }

  /**
   * The maximum number of tolerance for bad connection happens in one thread
   * which are applying for new {@link PooledConnection}.
   * 设置 坏连接容忍度poolMaximumLocalBadConnectionTolerance
   * @param poolMaximumLocalBadConnectionTolerance
   * max tolerance for bad connection happens in one thread
   *
   * @since 3.4.5
   */
  public void setPoolMaximumLocalBadConnectionTolerance(
      int poolMaximumLocalBadConnectionTolerance) {
    this.poolMaximumLocalBadConnectionTolerance = poolMaximumLocalBadConnectionTolerance;
  }

  /**
   * The maximum time a connection can be used before it *may* be
   * given away again.
   * 设置最大可回收时间
   * <p>
   *     直接对 (最大可回收时间poolMaximumCheckoutTime)附上 (参数poolMaximumCheckoutTime),
   *     并且调用 {@link PooledDataSource#forceCloseAll()}, 我认为,不关闭连接池中的所有连接也行,
   *     直接对现有连接进行检查,将不符合poolMaximumCheckoutTime的连接关闭掉就可以了。没必要说关闭所有连接,
   *     毕竟创建连接比较耗资源和耗时。
   * </p>
   */
  public void setPoolMaximumCheckoutTime(int poolMaximumCheckoutTime) {
    this.poolMaximumCheckoutTime = poolMaximumCheckoutTime;
    forceCloseAll();
  }

  /**
   * The time to wait before retrying to get a connection.
   * 设置 在无法获取连接时,线程需要等待的时间
   * <p>
   *      直接对 (在无法获取连接时,线程需要等待的时间poolTimeToWait)附上 (参数poolTimeToWait),
   *      并且调用 {@link PooledDataSource#forceCloseAll()}, 我认为,不关闭连接池中的所有连接也行,
   *      因为这个(在无法获取连接时,线程需要等待的时间poolTimeToWait)是会作用于创建连接,下次根据
   *      (在无法获取连接时,线程需要等待的时间poolTimeToWait)来创建连接即可。没必要说关闭所有连接,
   *       毕竟创建连接比较耗资源和耗时。
   * </p>
   * @param poolTimeToWait The time to wait
   */
  public void setPoolTimeToWait(int poolTimeToWait) {
    this.poolTimeToWait = poolTimeToWait;
    forceCloseAll();
  }

  /**
   * The query to be used to check a connection.
   * 设置 (心跳执行的SQL语句poolPingQuery)
   * <p>
   *      直接对 (心跳执行的SQL语句poolPingQuery)附上 (参数poolPingQuery),
   *      并且调用 {@link PooledDataSource#forceCloseAll()}, 我认为,不关闭连接池中的所有连接也行,
   *      因为心跳时,就是引用(心跳执行的SQL语句poolPingQuery)的SQL的,(心跳执行的SQL语句poolPingQuery)
   *      的更改,不管是否新连接还是旧连接都引用到,没必要说关闭所有连接,毕竟创建连接比较耗资源和耗时。
   * </p>
   * @param poolPingQuery The query
   */
  public void setPoolPingQuery(String poolPingQuery) {
    this.poolPingQuery = poolPingQuery;
    forceCloseAll();
  }

  /**
   * Determines if the ping query should be used.
   * 设置 (心跳是否启用poolPingEnabled)
   * <p>
   *      直接对 (心跳是否启用poolPingEnabled)附上 (心跳是否启用poolPingEnabled),
   *      并且调用 {@link PooledDataSource#forceCloseAll()}, 我认为,不关闭连接池中的所有连接也行,
   *      因为心跳时,就是引用(心跳是否启用poolPingEnabled),(心跳是否启用poolPingEnabled)
   *      的更改,不管是否新连接还是旧连接都引用到,没必要说关闭所有连接,毕竟创建连接比较耗资源和耗时。
   * </p>
   * @param poolPingEnabled True if we need to check a connection before using it
   */
  public void setPoolPingEnabled(boolean poolPingEnabled) {
    this.poolPingEnabled = poolPingEnabled;
    forceCloseAll();
  }

  /**
   * If a connection has not been used in this many milliseconds, ping the
   * database to make sure the connection is still good.
   * 设置 (心跳的时间间隔poolPingConnectionsNotUsedFor)
   * <p>
   *      直接对 (心跳的时间间隔poolPingConnectionsNotUsedFor)附上 (心跳的时间间隔poolPingConnectionsNotUsedFor),
   *      并且调用 {@link PooledDataSource#forceCloseAll()}, 我认为,不关闭连接池中的所有连接也行,
   *      因为心跳时,就是引用(心跳的时间间隔poolPingConnectionsNotUsedFor),(心跳的时间间隔poolPingConnectionsNotUsedFor)
   *      的更改,不管是否新连接还是旧连接都引用到,没必要说关闭所有连接,毕竟创建连接比较耗资源和耗时。
   * </p>
   * @param milliseconds the number of milliseconds of inactivity that will trigger a ping
   */
  public void setPoolPingConnectionsNotUsedFor(int milliseconds) {
    this.poolPingConnectionsNotUsedFor = milliseconds;
    forceCloseAll();
  }

  /**
   * 获取驱动,直接调用 {@link UnpooledDataSource#getDriver()}
   */
  public String getDriver() {
    return dataSource.getDriver();
  }

  /**
   * 获取数据库连接地址,直接调用 {@link UnpooledDataSource#getUrl()}
   */
  public String getUrl() {
    return dataSource.getUrl();
  }

  /**
   * 获取数据库用户,直接调用 {@link UnpooledDataSource#getUsername()}
   */
  public String getUsername() {
    return dataSource.getUsername();
  }

  /**
   * 获取数据库密码,直接调用 {@link UnpooledDataSource#getPassword()}
   */
  public String getPassword() {
    return dataSource.getPassword();
  }

  /**
   * 是否自动提交事务,直接调用 {@link UnpooledDataSource#isAutoCommit()}
   */
  public boolean isAutoCommit() {
    return dataSource.isAutoCommit();
  }

  /**
   * 获取数据库事务隔离级别,直接调用 {@link UnpooledDataSource#getDefaultTransactionIsolationLevel()}
   */
  public Integer getDefaultTransactionIsolationLevel() {
    return dataSource.getDefaultTransactionIsolationLevel();
  }

  /**
   * 获取数据库驱动属性,直接调用 {@link UnpooledDataSource#getDriverProperties()}
   */
  public Properties getDriverProperties() {
    return dataSource.getDriverProperties();
  }

  /**
   * 获取数据库网络超时时长,直接调用 {@link UnpooledDataSource#getDefaultNetworkTimeout()}
   * @since 3.5.2
   */
  public Integer getDefaultNetworkTimeout() {
    return dataSource.getDefaultNetworkTimeout();
  }

  /**
   * 获取 最大获得连接数
   */
  public int getPoolMaximumActiveConnections() {
    return poolMaximumActiveConnections;
  }

  /**
   * 获取 最大空闲连接数
   */
  public int getPoolMaximumIdleConnections() {
    return poolMaximumIdleConnections;
  }

  /**
   * 获取 坏连接的容忍度
   */
  public int getPoolMaximumLocalBadConnectionTolerance() {
    return poolMaximumLocalBadConnectionTolerance;
  }

  /**
   * 获取 最大可回收时间
   */
  public int getPoolMaximumCheckoutTime() {
    return poolMaximumCheckoutTime;
  }

  /**
   * 获取 在无法获取连接,线程需要等待的时长
   */
  public int getPoolTimeToWait() {
    return poolTimeToWait;
  }

  /**
   * 获取心跳的执行SQL
   */
  public String getPoolPingQuery() {
    return poolPingQuery;
  }

  /**
   * 是否启用心跳
   */
  public boolean isPoolPingEnabled() {
    return poolPingEnabled;
  }

  /**
   * 获取心跳的时间间隔
   */
  public int getPoolPingConnectionsNotUsedFor() {
    return poolPingConnectionsNotUsedFor;
  }

  /**
   * Closes all active and idle connections in the pool.
   * 关闭连接池中所有的活动连接和空闲连接
   */
  public void forceCloseAll() {
    synchronized (state) {
      expectedConnectionTypeCode = assembleConnectionTypeCode(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
      //对活动的连接逐一关闭
      //这里的循环很巧妙,非常值得我去学习。
      for (int i = state.activeConnections.size(); i > 0; i--) {
        try {
          PooledConnection conn = state.activeConnections.remove(i - 1);
          conn.invalidate();//设置无效
          // 如果不是自动提交事务,则将其回滚,因为可能存在一些操作
          Connection realConn = conn.getRealConnection();
          if (!realConn.getAutoCommit()) {
            realConn.rollback();
          }
          realConn.close();//这里是关闭真实连接,并没有不是代理连接。
        } catch (Exception e) {
          // ignore
        }
      }
      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.");
    }
  }

  /**
   * 获取连接池状态
   */
  public PoolState getPoolState() {
    return state;
  }

  /**
   * 构造 (预期的连接类型代码assembleConnectionTypeCode)
   * <p>
   *     拼装(参数url)+(参数username)+(参数password)得到一个字符串,并调用该字符串的{@link String#hashCode()}取到
   *     其hashCode返回出去。
   * </p>
   */
  private int assembleConnectionTypeCode(String url, String username, String password) {
    return ("" + url + username + password).hashCode();
  }

  /**
   * 根据当前状态来决定放入空闲链表中还是释放掉。
   */
  protected void pushConnection(PooledConnection conn) throws SQLException {

    synchronized (state) {
      //从活动连接中移除该连接
      state.activeConnections.remove(conn);
      if (conn.isValid()) {
        //如果当前线程的空间连接列表 < 最大空闲连接数 && 当前的连接是否变更过(即用户名,密码,Url等变更)
        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++;
      }
    }
  }

  /**
   * 创建连接以及分配连接,当有空闲连接时则直接使用,否则再根据是否达到了设置的峰值,来决定是否需要创建新的连接等。
   */
  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
            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;
              // 将连接从活动队列中移除
              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.
                     只是调试日志消息然后继续执行下面的语句像什么也没发生一样。
                     包装一个坏连接的连接和一个新的池连接,这将有助于不中断当前执行中线程和给当前线程一个
                     加入争端另一个有效或者好的数据库连接。在这个循环的结束,坏的连接会被设置为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 {
              // 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()) {
            // 连接为非自动提交事务,则将其回滚,可能存在一些未提交操作,并且防止影响下一次使用
            if (!conn.getRealConnection().getAutoCommit()) {
              conn.getRealConnection().rollback();
            }
            // 根据URL,用户名以及密码计算出一个Hash,用于标识此次连接
            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++;
            //置空连接,因为已经连接无效了,所以再调用关闭连接方法也不会有反应,所以只需要交给GC回收
            conn = null;
            // 如果这个线程获取到的是一个坏的连接,那么这个数据源允许这个线程尝试重新获取一个新的连接但是这个重新尝试的次数不应该超过
            // (最大空闲连接数poolMaximumIdleConnections)与(坏连接容忍度poolMaximumLocalBadConnectionTolerance)之和。
            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.");
            }
          }
        }
      }

    }

    // 从上面的循环退出,如果为null,则一定出现异常情况了
    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;
  }

  /**
   * Method to check to see if a connection is still usable
   * 检出连接是否仍然可用
   * <p>
   *     先检查真实连接是否已将关闭并将结果赋值给(变量result),关闭就直接返回(变量result);否则判断是否启用心跳,没有启用也
   *     直接返回(变量result);否则根据(ping的检查时间间隔poolPingConnectionsNotUsedFor)判断是否可触发检查心跳的逻辑。
   *     心跳逻辑主要通过(参数conn)执行(成员变量poolPingQuery)的SQL脚本,如果执行期间没有发生异常,就认为该连接是可以用的。
   * </p>
   * @param conn - the connection to check
   * @return True if the connection is still usable
   */
  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();
            //将poolPingQuery的SQL脚本通过该连接进行发送,poolPingQuery默认的SQL并不是一个可执行SQL所以调用是会报错的,
            // 所以要对poolPingQuery进行修改,由于poolPingQuery的SQL脚本只是用于心跳,所以用对数据库最不耗时,资源最小
            //的SQL脚本。
            try (Statement statement = realConn.createStatement()) {
              statement.executeQuery(poolPingQuery).close();
            }
            //如果在连接没有明确自动提交的情况下,默认还是回退的,因为这样能保证数据库不会因为心跳的脚本到时影响到业务逻辑。
            //正式因为默认回退,所以poolPingQuery如果是insert,delete,update也不会真正的修改到数据库数据
            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 {
              //在出现异常的情况下,会抛出关闭真实连接。哪怕poolPingQuery执行抛出的异常,也会关闭连接,
              //所以poolPingQuery必须保证是正确的,否则真实连接关闭。
              conn.getRealConnection().close();
            } catch (Exception e2) {
              //ignore
            }
            result = false;
            if (log.isDebugEnabled()) {
              log.debug("Connection " + conn.getRealHashCode() + " is BAD: " + e.getMessage());
            }
          }
        }
      }
    }
    return result;
  }

  /**
   * Unwraps a pooled connection to get to the 'real' connection
   * 判断 {@link @conn} 是否是代理连接 ,如果是就或者{@link @conn}的真实连接,否则直接返回 {@link @conn}
   * <p>
   *
   * </p>
   * @param conn - the pooled connection to unwrap
   * @return The 'real' connection
   */
  public static Connection unwrapConnection(Connection conn) {
    if (Proxy.isProxyClass(conn.getClass())) {
      InvocationHandler handler = Proxy.getInvocationHandler(conn);
      if (handler instanceof PooledConnection) {
        return ((PooledConnection) handler).getRealConnection();
      }
    }
    return conn;
  }

  /**
   * GC回收时会调用该方法,这时先调用 {@link PooledDataSource#forceCloseAll()} 关闭掉所有连接后,再调用该类的{@link Object#finalize()}
   * @throws Throwable
   */
  @Override
  protected void finalize() throws Throwable {
    forceCloseAll();
    super.finalize();
  }

  /**
   * {@link PooledDataSource} 不是一个包装类,不提供解开方法,调用该方法会抛出 {@link SQLException}
   */
  @Override
  public <T> T unwrap(Class<T> iface) throws SQLException {
    throw new SQLException(getClass().getName() + " is not a wrapper.");
  }

  /**
   * {@link PooledDataSource} 不是一个包装类,不会为任何类进行包装,这里写死返回false
   */
  @Override
  public boolean isWrapperFor(Class<?> iface) {
    return false;
  }

  /**
   * 获取父级Logger
   */
  @Override
  public Logger getParentLogger() {
    return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
  }

}

UnpooledDataSource

/**
 *    Copyright 2009-2019 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.datasource.unpooled;

import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.DriverPropertyInfo;
import java.sql.SQLException;
import java.util.Enumeration;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.logging.Logger;

import javax.sql.DataSource;

import org.apache.ibatis.io.Resources;

/**
 * 无连接池数据源
 * <p>每次获取请求都简单的打开和关闭连接</p>
 * @author Clinton Begin
 * @author Eduardo Macarron
 */
public class UnpooledDataSource implements DataSource {

  private ClassLoader driverClassLoader;
  /**
   * 驱动属性,有mybatis-config.xml传递进来
   */
  private Properties driverProperties;
  /**
   *
   */
  private static Map<String, Driver> registeredDrivers = new ConcurrentHashMap<>();

  /**
   * 当前驱动
   */
  private String driver;
  private String url;
  private String username;
  private String password;

  private Boolean autoCommit;
  /**
   * 默认事务等级
   */
  private Integer defaultTransactionIsolationLevel;
  /**
   * 默认超时时长
   */
  private Integer defaultNetworkTimeout;

  /**
   * 当类加载的时候,就从DriverManager中获取所有的驱动信息,放到当前维护的Map中,
   * 拿到的是项目内的所有数据库驱动,
   * //www.greatytc.com/p/f5f677826715
   */
  static {
    Enumeration<Driver> drivers = DriverManager.getDrivers();
    while (drivers.hasMoreElements()) {
      Driver driver = drivers.nextElement();
      registeredDrivers.put(driver.getClass().getName(), driver);
    }
  }

  public UnpooledDataSource() {
  }

  public UnpooledDataSource(String driver, String url, String username, String password) {
    this.driver = driver;
    this.url = url;
    this.username = username;
    this.password = password;
  }

  public UnpooledDataSource(String driver, String url, Properties driverProperties) {
    this.driver = driver;
    this.url = url;
    this.driverProperties = driverProperties;
  }

  public UnpooledDataSource(ClassLoader driverClassLoader, String driver, String url, String username, String password) {
    this.driverClassLoader = driverClassLoader;
    this.driver = driver;
    this.url = url;
    this.username = username;
    this.password = password;
  }

  public UnpooledDataSource(ClassLoader driverClassLoader, String driver, String url, Properties driverProperties) {
    this.driverClassLoader = driverClassLoader;
    this.driver = driver;
    this.url = url;
    this.driverProperties = driverProperties;
  }

  /**
   * 获取连接
   * <p>
   *     直接将(成员变量username)和(成员变量password)传入{@link UnpooledDataSource#doGetConnection(String, String)}
   * </p>
   */
  @Override
  public Connection getConnection() throws SQLException {
    return doGetConnection(username, password);
  }

  /**
   * 获取连接
   * <p>
   *     直接将(参数username)和(参数password)传入{@link UnpooledDataSource#doGetConnection(String, String)}
   * </p>
   */
  @Override
  public Connection getConnection(String username, String password) throws SQLException {
    return doGetConnection(username, password);
  }

  /**
   * 设置登录超时时长
   * <p>直接调用{@link DriverManager#setLoginTimeout(int)}</p>
   */
  @Override
  public void setLoginTimeout(int loginTimeout) {
    DriverManager.setLoginTimeout(loginTimeout);
  }

  /**
   * 获取登录超时时长
   * <p>直接调用{@link DriverManager#getLoginTimeout()}</p>
   */
  @Override
  public int getLoginTimeout() {
    return DriverManager.getLoginTimeout();
  }

  /**
   * 设置Log输出流
   *  <p>直接调用{@link DriverManager#setLogWriter(PrintWriter)}</p>
   */
  @Override
  public void setLogWriter(PrintWriter logWriter) {
    DriverManager.setLogWriter(logWriter);
  }

  /**
   * 获取Log输出流
   *  <p>直接调用{@link DriverManager#getLogWriter()}</p>
   */
  @Override
  public PrintWriter getLogWriter() {
    return DriverManager.getLogWriter();
  }

  /**
   * 获取驱动类加载器
   * @return
   */
  public ClassLoader getDriverClassLoader() {
    return driverClassLoader;
  }

  /**
   * 设置驱动类加载器
   */
  public void setDriverClassLoader(ClassLoader driverClassLoader) {
    this.driverClassLoader = driverClassLoader;
  }

  /**
   * 获取驱动属性
   */
  public Properties getDriverProperties() {
    return driverProperties;
  }

  /**
   * 设置驱动属性
   */
  public void setDriverProperties(Properties driverProperties) {
    this.driverProperties = driverProperties;
  }

  /**
   * 获取驱动类(包名+类名)
   */
  public String getDriver() {
    return driver;
  }

  /**
   * 设置驱动类(包名+类名),这里加上synchronized关键字
   */
  public synchronized void setDriver(String driver) {
    this.driver = driver;
  }

  /**
   * 获取数据库地址
   */
  public String getUrl() {
    return url;
  }

  /**
   * 设置数据库地址
   * @param url
   */
  public void setUrl(String url) {
    this.url = url;
  }

  public String getUsername() {
    return username;
  }

  public void setUsername(String username) {
    this.username = username;
  }

  public String getPassword() {
    return password;
  }

  public void setPassword(String password) {
    this.password = password;
  }

  public Boolean isAutoCommit() {
    return autoCommit;
  }

  public void setAutoCommit(Boolean autoCommit) {
    this.autoCommit = autoCommit;
  }

  public Integer getDefaultTransactionIsolationLevel() {
    return defaultTransactionIsolationLevel;
  }

  public void setDefaultTransactionIsolationLevel(Integer defaultTransactionIsolationLevel) {
    this.defaultTransactionIsolationLevel = defaultTransactionIsolationLevel;
  }

  /**
   * @since 3.5.2
   */
  public Integer getDefaultNetworkTimeout() {
    return defaultNetworkTimeout;
  }

  /**
   * Sets the default network timeout value to wait for the database operation to complete. See {@link Connection#setNetworkTimeout(java.util.concurrent.Executor, int)}
   *
   * @param milliseconds
   *          The time in milliseconds to wait for the database operation to complete.
   * @since 3.5.2
   */
  public void setDefaultNetworkTimeout(Integer defaultNetworkTimeout) {
    this.defaultNetworkTimeout = defaultNetworkTimeout;
  }

  /**
   * 获取连接,最终调用{@link UnpooledDataSource#doGetConnection(Properties)}
   * <p>
   *    新建一个{@link Properties}对象赋值给(变量props),将(参数username),(参数password),
   *    和(成员变量driverProperties)加入到(变量props),最后将(变量prop)传入
   *    {@link UnpooledDataSource#doGetConnection(Properties)}。(ps:(参数username)的key
   *    名为'user',(参数password)的key名为'password')
   * </p>
   *
   */
  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);
  }

  /**
   * 获取连接
   * <p>
   *     先调用{@link UnpooledDataSource#initializeDriver()}初始化驱动,再将(参数properties)和(成员变量url)
   *     传入{@link DriverManager#getConnection(String, Properties)}得到{@link Connection}并赋值给(变量
   *     connection),再将{@link UnpooledDataSource#configureConnection(Connection)}配置(变量connection)
   *     最后返回(变量connection)出去
   * </p>
   * @param properties
   * @return
   * @throws SQLException
   */
  private Connection doGetConnection(Properties properties) throws SQLException {
    initializeDriver();
    Connection connection = DriverManager.getConnection(url, properties);
    configureConnection(connection);
    return connection;
  }

  /**
   * 初始化驱动信息,只是对driver进行了是否注册到DriverManager和registerDrivers的判断,没有就是尝试注册,注册不成功就抛出异常。
   */
  private synchronized void initializeDriver() throws SQLException {
    // 如果没有包含在已注册Map中,则需要将该驱动加载进来
    if (!registeredDrivers.containsKey(driver)) {
      Class<?> driverType;
      try {
        // 加载数据库连接驱动
        if (driverClassLoader != null) {
          driverType = Class.forName(driver, true, driverClassLoader);
        } else {
          // Resources为MyBatis内置的资源工具类,该方法依次尝试从多个ClassLoader中获取Class类,
          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中,用于创建数据库连接
        DriverManager.registerDriver(new DriverProxy(driverInstance));
        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()) {//如果autoCommit与conn.getAutoCommit不一致的情况下设置
      conn.setAutoCommit(autoCommit);
    }
    if (defaultTransactionIsolationLevel != null) {
      conn.setTransactionIsolation(defaultTransactionIsolationLevel);
    }
  }

  /**
   * 驱动代理类,只是重写getParentLogger方法设置Logger
   */
  private static class DriverProxy implements Driver {
    private Driver driver;

    DriverProxy(Driver d) {
      this.driver = d;
    }

    @Override
    public boolean acceptsURL(String u) throws SQLException {
      return this.driver.acceptsURL(u);
    }

    @Override
    public Connection connect(String u, Properties p) throws SQLException {
      return this.driver.connect(u, p);
    }

    @Override
    public int getMajorVersion() {
      return this.driver.getMajorVersion();
    }

    @Override
    public int getMinorVersion() {
      return this.driver.getMinorVersion();
    }

    @Override
    public DriverPropertyInfo[] getPropertyInfo(String u, Properties p) throws SQLException {
      return this.driver.getPropertyInfo(u, p);
    }

    @Override
    public boolean jdbcCompliant() {
      return this.driver.jdbcCompliant();
    }

    /**
     * 设置java.util.Logger
     * @return
     */
    @Override
    public Logger getParentLogger() {
      return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
    }
  }

  @Override
  public <T> T unwrap(Class<T> iface) throws SQLException {
    throw new SQLException(getClass().getName() + " is not a wrapper.");
  }

  @Override
  public boolean isWrapperFor(Class<?> iface) throws SQLException {
    return false;
  }

  @Override
  public Logger getParentLogger() {
    // requires JDK version 1.6
    return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
  }

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容