Mybatis内部模块之---数据源模块
1.应用的设计模式
因为在创建数据源的时候,对象是一个非常复杂的对象,所以采用了的是工厂模式
2.数据源的创建分类
在Mybatis框架中,涉及到的数据源,常用的是两个,PooledDataSource(数据源连接池),UnpooledDataSource(非连接池数据源)
来源:PooledDataSource 是通过 PooledDataSourceFactory创建的,UnPooledDataSource 是通过UnPooledDataSourceFactory创建的;
3.特殊点
3.1 UnpooledDataSource每次都会创建一个新的连接
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); } //从这个代码可以看出,unpooledDatasource获取连接的方式和手动获取连接的方式是一样的 private Connection doGetConnection(Properties properties) throws SQLException { initializeDriver(); Connection connection = DriverManager.getConnection(url, properties); //设置事务是否自动提交,事务的隔离级别 configureConnection(connection); 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); } // 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.registerDriver(new DriverProxy(driverInstance)); registeredDrivers.put(driver, driverInstance); } catch (Exception e) { throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e); } } }
3.2 下面我们主要的看一下PooledDataSource ,带有连接池的数据源
在过程中,我们涉及到了两个重要的参数
PooledConnection 使用动态代理封装了真正的数据库连接对象,增强的是关闭方法;
PoolState 管理的是 PooledConnection 对象组件的状态,其中有两个变量 idleConnections(空闲的连接池资源集合) 和 activeConnections(活跃的连接池资源集合),
下面我们看一看连接池中在获取连接的时候流程:
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 {// 没有空闲连接 if (state.activeConnections.size() < poolMaximumActiveConnections) {//判断活跃连接池中的数量是否大于最大连接数 // 没有则可创建新的连接 conn = new PooledConnection(dataSource.getConnection(), this); if (log.isDebugEnabled()) { log.debug("Created connection " + conn.getRealHashCode() + "."); } } else {// 如果已经等于最大连接数,则不能创建新连接 //获取最早创建的连接 PooledConnection oldestActiveConnection = state.activeConnections.get(0); long longestCheckoutTime = oldestActiveConnection.getCheckoutTime(); if (longestCheckoutTime > poolMaximumCheckoutTime) {//检测是否已经以及超过最长使用时间 // 如果超时,对超时连接的信息进行统计 state.claimedOverdueConnectionCount++;//超时连接次数+1 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 { // 无空闲连接,最早创建的连接没有失效,无法创建新连接,只能阻塞 try { if (!countedWait) { state.hadToWaitCount++;//连接池累计等待次数加1 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++;//累计的获取无效连接次数+1 localBadConnectionCount++;//当前获取无效连接次数+1 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."); } } } } }
下面我们看看回收连接的时候的情况
//回收连接资源 protected void pushConnection(PooledConnection conn) throws SQLException { synchronized (state) {//回收连接必须是同步的 state.activeConnections.remove(conn);//从活跃连接池中删除此连接 if (conn.isValid()) { //判断闲置连接池资源是否已经达到上限 if (state.idleConnections.size() < poolMaximumIdleConnections && conn.getConnectionTypeCode() == expectedConnectionTypeCode) { //没有达到上限,进行回收 state.accumulatedCheckoutTime += conn.getCheckoutTime(); if (!conn.getRealConnection().getAutoCommit()) { conn.getRealConnection().rollback();//如果还有事务没有提交,进行回滚操作 } //基于该连接,创建一个新的连接资源,并刷新连接状态 PooledConnection 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++; } } }