mybatis——datasource

本来准备看下DruidDatasource的,发现太复杂了,还是研究一下mybatis的datasource吧

mybatis的datasource实现:无连接池实现的UnpooledDataSource,连接池实现的PooledDataSource

 一、DataSource

DataSource是JDK中提供数据库库连接的顶级接口。DataSource类图

 主要实现:

getConnection():获取一个数据库连接。

getLogWriter():日志

setLoginTimeout:数据源在尝试连接数据库时的最大等待时间

wrapper:包装器,例如BeanWrapper包装Bean,允许实例包装datasource。

二、UnpooledDataSource

 UnpooledDataSource是一个无连接池实现的DataSource,JDBC的简单封装,开启一个连接,执行完关闭连接。

1、变量与构造方法

  private ClassLoader driverClassLoader;//Driver的类加载器
  private Properties driverProperties;//启动属性
  private static Map<String, Driver> registeredDrivers = new ConcurrentHashMap<String, Driver>();//驱动程序容器

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

  private Boolean autoCommit;//自动提交
  private Integer defaultTransactionIsolationLevel;//默认事务隔离级别

  static {
    //类加载时,扫描DriverMananger中所有的数据库连接驱动程序
    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;
  }

2、主要实现方法

getConnection():获取数据库连接,JDBC的封装使用。

/* org.apache.ibatis.datasource.unpooled.UnpooledDataSource*/
  public Connection getConnection() throws SQLException {
    return doGetConnection(username, password);
  }

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

  private Connection doGetConnection(String username, String password) throws SQLException {
    Properties props = new Properties();
    if (driverProperties != null) {
      //设置driverProperties文件到属性对象中
      props.putAll(driverProperties);
    }
    if (username != null) {
      //设置username到属性对象中,注意显式配置username会覆盖driverPropertie文件中的
      props.setProperty("user", username);
    }
    if (password != null) {
      //设置password到属性对象中中,注意显式配置password会覆盖driverPropertie文件中的
      props.setProperty("password", password);
    }
    return doGetConnection(props);
  }

  private Connection doGetConnection(Properties properties) throws SQLException {
    //启动驱动程序
    //若在registerDrivers中(已启动),直接返回
    //若不在registerDrivers中,使用driverClassLoader(不为空)加载driver类,启动驱动程序
   //若driverClassLoader为空,所有的类加载器,遍历加载一次,直到能加载为止
    initializeDriver();
    //JDBC获取连接的语句
    Connection connection = DriverManager.getConnection(url, properties);
    //设置连接属性
    //自动提交autocommit
    //默认事务隔离级别:defaultTransactionIsolationLevel
    configureConnection(connection);
    return connection;
  }

3、总结

UnpooledDataSource就是JDBC的简单封装,没有其他逻辑。

① 类似工厂方法提供一个连接方法,getConnection().

② 启动数据库连接驱动程序driver,类加载器不是我以为的线程上下文类加载器,而是遍历所有的类加载器,直到找到合适的类加载器加载成功(cathc异常)。

三、PooledDataSource

PooledDataSource是数据库连接的连接池实现。前面研究过线程池ThreadPoolExecutor实现,多个线程+队列。这里猜测应该也是用的实现差不多,使用队列实现,不同在于这里队列里的元素时connection。

1、变量与构造方法

  private final PoolState state = new PoolState(this);//连接池中连接存放的容器(两个队列)

  private final UnpooledDataSource dataSource;//装饰者模式,PooledDataSource是一个增强的UnpooledDataSource

  // OPTIONAL CONFIGURATION FIELDS
  protected int poolMaximumActiveConnections = 10;//连接池中最大活跃连接数
  protected int poolMaximumIdleConnections = 5;//连接池中最大闲置连接数
  protected int poolMaximumCheckoutTime = 20000;//连接池中闲置连接的过期时间
  protected int poolTimeToWait = 20000;//连接池获取连接的超时时间
  protected int poolMaximumLocalBadConnectionTolerance = 3;//连接池中坏连接容忍数
  protected String poolPingQuery = "NO PING QUERY SET";//暂不清
  protected boolean poolPingEnabled;//暂不清
  protected int poolPingConnectionsNotUsedFor;//暂不清

  private int expectedConnectionTypeCode;//连接池属性集合的hashCode,("" + url + username + password).hashCode();

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

2、主要实现

① 连接池connection的容器PoolState

public class PoolState {

  protected PooledDataSource dataSource;

  protected final List<PooledConnection> idleConnections = new ArrayList<PooledConnection>();//闲置连接的容器
  protected final List<PooledConnection> activeConnections = new ArrayList<PooledConnection>();//活跃连接的容器
  protected long requestCount = 0;
  protected long accumulatedRequestTime = 0;
  protected long accumulatedCheckoutTime = 0;
  protected long claimedOverdueConnectionCount = 0;
  protected long accumulatedCheckoutTimeOfOverdueConnections = 0;
  protected long accumulatedWaitTime = 0;
  protected long hadToWaitCount = 0;
  protected long badConnectionCount = 0;

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

  public synchronized long getRequestCount() {
    return requestCount;
  }

  public synchronized long getAverageRequestTime() {
    return requestCount == 0 ? 0 : accumulatedRequestTime / requestCount;
  }

  public synchronized long getAverageWaitTime() {
    return hadToWaitCount == 0 ? 0 : accumulatedWaitTime / hadToWaitCount;

  }

  public synchronized long getHadToWaitCount() {
    return hadToWaitCount;
  }

  public synchronized long getBadConnectionCount() {
    return badConnectionCount;
  }

  public synchronized long getClaimedOverdueConnectionCount() {
    return claimedOverdueConnectionCount;
  }

  public synchronized long getAverageOverdueCheckoutTime() {
    return claimedOverdueConnectionCount == 0 ? 0 : accumulatedCheckoutTimeOfOverdueConnections / claimedOverdueConnectionCount;
  }

  public synchronized long getAverageCheckoutTime() {
    return requestCount == 0 ? 0 : accumulatedCheckoutTime / requestCount;
  }


  public synchronized int getIdleConnectionCount() {
    return idleConnections.size();
  }

  public synchronized int getActiveConnectionCount() {
    return activeConnections.size();
  }

  @Override
  public synchronized String toString() {
    ...
  }

}

② 队列实现

出队:popConnection()

  public Connection getConnection(String username, String password) throws SQLException {
    return popConnection(username, password).getProxyConnection();
  }

  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 happend.
                     Wrap the bad connection with a new PooledConnection, this will help
                     to not intterupt current executing thread and give current thread a
                     chance to join the next competion for another valid/good database
                     connection. At the end of this loop, bad {@link @conn} will be set as null.
                   */
                  log.debug("Bad connection. Could not roll back");
                }  
              }
              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();
            }
            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;
  }

入队:pushConnection()

 

猜你喜欢

转载自www.cnblogs.com/wqff-biubiu/p/12466771.html