MyBatis--连接池模块

常见的数据源组件都实现了java.sql.DataSource接口。MyBatis也是这个套路。

从上图可以看出:MyBatis使用不同的DataSourceFactory接口实现创建不同类型的DataSource。工厂模式

DataSourceFactory

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

}
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;
//构函初始化dataSource
  public UnpooledDataSourceFactory() {
    this.dataSource = new UnpooledDataSource();
  }

  @Override
  public void setProperties(Properties properties) {
    Properties driverProperties = new Properties();
    //创建DataSource相应的MetaObject
    MetaObject metaDataSource = SystemMetaObject.forObject(dataSource);
    //遍历
    for (Object key : properties.keySet()) {
      String propertyName = (String) key;
      if (propertyName.startsWith("driver.")) {
        //以"driver."开始的配置是对DataSource的配置保存到driverProperties
        String value = properties.getProperty(propertyName);
        driverProperties.setProperty(propertyName.substring(DRIVER_PROPERTY_PREFIX_LENGTH), value);
      } else if (metaDataSource.hasSetter(propertyName)) {//是否有该属性的setter方法
        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) {
      //设置datasource.driverProperties相关属性值
      metaDataSource.setValue("driverProperties", driverProperties);
    }
  }

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

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

}
复制代码

UnpooledDataSource

public class UnpooledDataSource implements DataSource {
  
  private ClassLoader driverClassLoader;//加载Driver类的加载器
  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;//事务隔离级别

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

  @Override
  public Connection getConnection() throws SQLException {
    return doGetConnection(username, password);
  }

  @Override
  public Connection getConnection(String username, String password) throws SQLException {
    return doGetConnection(username, password);
  }

  @Override
  public void setLoginTimeout(int loginTimeout) throws SQLException {
    DriverManager.setLoginTimeout(loginTimeout);
  }

  @Override
  public int getLoginTimeout() throws SQLException {
    return DriverManager.getLoginTimeout();
  }

  @Override
  public void setLogWriter(PrintWriter logWriter) throws SQLException {
    DriverManager.setLogWriter(logWriter);
  }

  @Override
  public PrintWriter getLogWriter() throws SQLException {
    return DriverManager.getLogWriter();
  }

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


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

  private Connection doGetConnection(Properties properties) throws SQLException {
    initializeDriver(); //1.初始化驱动
    //2.从DriverManager中获取连接,获取新的Connection对象
    Connection connection = DriverManager.getConnection(url, properties);
    configureConnection(connection);//3.配置autoCommit和隔离级别
    return connection;
  }

  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);
        }
   //创建Driver对象
        Driver driverInstance = (Driver)driverType.newInstance();
        //注册驱动
        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 (autoCommit != null && autoCommit != conn.getAutoCommit()) {
      conn.setAutoCommit(autoCommit);//设置事务
    }
    if (defaultTransactionIsolationLevel != null) {//设置隔离级别
      conn.setTransactionIsolation(defaultTransactionIsolationLevel);
    }
  }

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

    // @Override only valid jdk7+
    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 only valid jdk7+
  public Logger getParentLogger() {
    // requires JDK version 1.6
    return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
  }

}
复制代码

PooledDataSource

依赖的组件

PooledDataSource并不会直接管理connection对象,而是管理PooledConnection对象。PooledConnection中封装真正的连接对象以及其代理对象(通过jdk代理)

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

  protected int poolMaximumActiveConnections = 10;//最大活跃连接
  protected int poolMaximumIdleConnections = 5;//最大空闲连接
  protected int poolMaximumCheckoutTime = 20000;//最大checkout时长
  protected int poolTimeToWait = 20000;//无法获取链接时,线程要等待的时间
  protected int poolMaximumLocalBadConnectionTolerance = 3;
//检测连接是否可用,会发送一个测试SQL语句
  protected String poolPingQuery = "NO PING QUERY SET";
  protected boolean poolPingEnabled;//是否允许发送测试SQL语句
//当连接超过poolPingConnectionsNotUsedFor毫秒未用时,发送一次测试SQL,检测连接是否正常
  protected int poolPingConnectionsNotUsedFor;

  private int expectedConnectionTypeCode;
//获取链接过程
 public Connection getConnection() throws SQLException {
    return popConnection(dataSource.getUsername(), dataSource.getPassword())
      .getProxyConnection();//得到连接后,返回代理。(这个是在初始化PooledConnection时,生成的代理)
  }

// 返回可用的PooledConnection
  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()) {
          // 连接池中有空闲连接,取出第一个
          conn = state.idleConnections.remove(0);
        } else {
          //  // 连接池中没有空闲连接,则取当前正在使用的连接数小于最大限定值
          if (state.activeConnections.size() < poolMaximumActiveConnections) {
            //  // 创建一个新的connection对象
            conn = new PooledConnection(dataSource.getConnection(), this);
            if (log.isDebugEnabled()) {
              log.debug("Created connection " + conn.getRealHashCode() + ".");
            }
          } else {

            //  当活动连接池已满,不能创建时,取出活动连接池的第一个,即最先进入连接池的PooledConnection对象
            // 计算它的校验时间,如果校验时间大于连接池规定的最大校验时间,则认为它已经过期了,
            // 利用这个PoolConnection内部的realConnection重新生成一个PooledConnection
            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) {
                  log.debug("Bad connection. Could not roll back");
                }  
              }//如果过期,创建新的连接放入活跃池。//这里创建代理
              conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);
              conn.setCreatedTimestamp(oldestActiveConnection.getCreatedTimestamp());
              conn.setLastUsedTimestamp(oldestActiveConnection.getLastUsedTimestamp());
              oldestActiveConnection.invalidate();
              if (log.isDebugEnabled()) {
                log.debug("Claimed overdue connection " + conn.getRealHashCode() + ".");
              }
            } else {
              //如果不能释放,则必须等待有
              try {
                if (!countedWait) {
                  state.hadToWaitCount++;
                  countedWait = true;
                }
                long wt = System.currentTimeMillis();
                state.wait(poolTimeToWait);
                state.accumulatedWaitTime += System.currentTimeMillis() - wt;
              } catch (InterruptedException e) {
                break;
              }
            }
          }
        }  //如果获取PooledConnection成功,则更新其信息
        if (conn != null) {
          // ping to server and check the connection is valid or not
          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 {
            state.badConnectionCount++;
            localBadConnectionCount++;
            conn = null;
            if (localBadConnectionCount > (poolMaximumIdleConnections + poolMaximumLocalBadConnectionTolerance)) {
              throw new SQLException("PooledDataSource: Could not get a good connection to the database.");
            }
          }
        }
      }

    }
####返回连接
  //通过PooledConnection.invoke()方法我们知道,调用连接的代理的close方法时,调用的是//PooledDataSource.pushConnectioin方法,将PooledConnction归还给连接池,供之后重用。
  
  protected void pushConnection(PooledConnection conn) throws SQLException {
    synchronized (state) {//同步
      state.activeConnections.remove(conn);//从活跃集合中移除该PooledConnection对象
      if (conn.isValid()) {//检测PooledConnection是否有效
        if (state.idleConnections.size() < poolMaximumIdleConnections && conn.getConnectionTypeCode() == expectedConnectionTypeCode) {//空闲连接是否达到上限,以及连接是否为该连接池连接
          state.accumulatedCheckoutTime += conn.getCheckoutTime();//累积checkout时长
          if (!conn.getRealConnection().getAutoCommit()) {//回滚未提交的事务
            conn.getRealConnection().rollback();
          }//为返还的连接创建新的PooledConnection对象
          PooledConnection newConn = new PooledConnection(conn.getRealConnection(), this);
          state.idleConnections.add(newConn);//添加到idle集合
          newConn.setCreatedTimestamp(conn.getCreatedTimestamp());
          newConn.setLastUsedTimestamp(conn.getLastUsedTimestamp());
          conn.invalidate();//将原pooledConnecion设为无效
          state.notifyAll();//唤醒阻塞等待的线程
        } else {//空闲连接到达上限或pooledConnection对象不属于该池
          state.accumulatedCheckoutTime += conn.getCheckoutTime();//累积checkout时长
          if (!conn.getRealConnection().getAutoCommit()) {
            conn.getRealConnection().rollback();
          }
          conn.getRealConnection().close();//关闭真正的连接
          conn.invalidate();//将PooledConncetion设为无效
        }
      } else {
        state.badConnectionCount++;//统计无效PooledConnection对象个数
      }
    }
  }
    
    //检测有效性的方法
     public boolean isValid() {
    return valid && realConnection != null
      && dataSource.pingConnection(this);//检测真正连接是否有用
  }
    //执行测试SQL
     protected boolean pingConnection(PooledConnection conn) {
    boolean result = true;//记录ping操作是否成功
    try {
      result = !conn.getRealConnection().isClosed();//检测真正的连接是否关闭
    } catch (SQLException e) {
      result = false;
    }

    if (result) {
      if (poolPingEnabled) {//是否执行SQL语句
        if (poolPingConnectionsNotUsedFor >= 0 && conn.getTimeElapsedSinceLastUse() > poolPingConnectionsNotUsedFor) {//长时间未使用的连接,才需要ping来检测是否正常
          try {//执行测试语句
            Connection realConn = conn.getRealConnection();
            try (Statement statement = realConn.createStatement()) {
              statement.executeQuery(poolPingQuery).close();
            }
            if (!realConn.getAutoCommit()) {
              realConn.rollback();
            }
            result = true;
          } catch (Exception e) {
            try {
              conn.getRealConnection().close();
            } catch (Exception e2) {
              //ignore
            }
            result = false;
          }
        }
      }
    }
    return result;
  }
复制代码

PooledConnection核心字段:

 //当前PooledConnection所属的PooledDataSource。
 private final PooledDataSource dataSource;
  private final Connection realConnection;//真正的数据连接
  private final Connection proxyConnection;//连接的代理
  private long checkoutTimestamp;//从池中取出该连接的时间
  private long createdTimestamp;//该连接的创建时间
  private long lastUsedTimestamp;//最后一次使用时间
  private int connectionTypeCode;//url 用户名密码得到的hash,标示连接所在的连接池
  private boolean valid;//检测当前连接是否有效。主要是防止程序通过close()方法将连接归还池后,依然通过该连接操作数据库
//当调用关闭的时候,回收此Connection到PooledDataSource中
  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())) {
          //通过valid字段检测连接是否有效
          checkConnection();
        }//调用真正数据库对象的对应方法
        return method.invoke(realConnection, args);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
  }

//池和连接的理解https://www.cnblogs.com/panxuejun/p/6403760.html
//http://shift-alt-ctrl.iteye.com/blog/1967020
//数据库连接池在初始化时,会创建一定数量的连接放在池中备用。
//当池中连接全部用完,请求就会进入阻塞队列等待。
复制代码

PoolState是用于管理PooledConnection对象状态的组件,通过两个ArrayList<>管理空闲和活跃两种状态。

protected final List<PooledConnection> idleConnections = new ArrayList<>();//空闲
  protected final List<PooledConnection> activeConnections = new ArrayList<>();//活跃
  //统计的字段
  protected long requestCount = 0;//请求连接次数
  protected long accumulatedRequestTime = 0;//获取连接的累积时间
  protected long accumulatedCheckoutTime = 0;//所有连接累积CheckoutTime时长
  protected long claimedOverdueConnectionCount = 0;//超时连接个数
  protected long accumulatedCheckoutTimeOfOverdueConnections = 0;//累积超时时间
  protected long accumulatedWaitTime = 0;//累积等待时间
  protected long hadToWaitCount = 0;//等待次数
  protected long badConnectionCount = 0;//无效的连接数
复制代码

转载于:https://juejin.im/post/5bdbf5c56fb9a049bc4c1626

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值