mybatis数据库连接池

mybatis使用了一个PooledDataSource作为数据库连接池.

下面是它的一些变量。

  //有一个池状态
  private final PoolState state = new PoolState(this);

  //里面有一个UnpooledDataSource
  private final UnpooledDataSource dataSource;

  // OPTIONAL CONFIGURATION FIELDS
  //正在使用连接的数量
  protected int poolMaximumActiveConnections = 10;

  //空闲连接数
  protected int poolMaximumIdleConnections = 5;

  //在被强制返回之前,池中连接被检查的时间
  protected int poolMaximumCheckoutTime = 20000;

  //这是给连接池一个打印日志状态机会的低层次设置,还有重新 尝试获得连接,
  // 这些情况下往往需要很长时间 为了避免连接池没有配置时静默失 败)。
  protected int poolTimeToWait = 20000;

  //发送到数据的侦测查询,用来验证连接是否正常工作,并且准备 接受请求。
  //默认是“NO PING QUERY SET” ,这会引起许多数据库驱动连接由一 个错误信息而导致失败
  protected String poolPingQuery = "NO PING QUERY SET";

  //开启或禁用侦测查询
  protected boolean poolPingEnabled = false;

  //用来配置 poolPingQuery 多次时间被用一次
  protected int poolPingConnectionsNotUsedFor = 0;

  private int expectedConnectionTypeCode;

PooledDataSource保持了一个PooledState来缓存和处理和connection:

下面是它持有的变量,

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;

从这些变量我们基本上可以看出PooledDataSource的基本实现。可以看出连接池的核心是两个List<PooledConnection>。

PooledDataSource有这么几个方法来处理连接(最下面有带注释的源码)

一.PushConnection()

1.使用syschronized(state)来保证线程安全。

2.创建connection加入空闲连接缓存

二.PopConnection()

1.最外层是个死循环,不断从空闲连接缓存中获取连接,这里使用syschronized(state)来保证线程安全。

2.空闲连接缓存不为空则返回第一个,并在缓存中删除连接。

3.空闲连接缓存为空且活动连接数小于最大活动连接数(默认为10)。则创建并添加空闲连接。

4.活动连接已达上限时,选择checkouttime最长且大于poolMaximumCheckoutTime(默认20000ms)的连接,从缓存删除,并将connection设置为invalidate,

5.以上都不满足则线程wait一段时间(默认为20000ms),并统计一次失败,当失败次数大于最大空闲连接数+3(默认5+3)则抛出异常

三.PingConnection()

用来检测一个connection是否可用,默认不检查,当connection空闲时间大于阈值时(默认为0所以默认不检测)。

默认使用查询语句"NO PING QUERY SET"。

下面是PopConnection带注释的源码

private PooledConnection popConnection(String username, String password) throws SQLException {
    boolean countedWait = false;
    PooledConnection conn = null;
    long t = System.currentTimeMillis();
    int localBadConnectionCount = 0;

    //最外面是while死循环,如果一直拿不到connection,则不断尝试
    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) {
        	  //如果activeConnections太少,那就new一个PooledConnection
            // Can create new connection
            conn = new PooledConnection(dataSource.getConnection(), this);
            if (log.isDebugEnabled()) {
              log.debug("Created connection " + conn.getRealHashCode() + ".");
            }
          } else {
        	  //如果activeConnections已经很多了,那不能再new了
            // Cannot create new connection
        	  //取得activeConnections列表的第一个(最老的)
            PooledConnection oldestActiveConnection = state.activeConnections.get(0);
            long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
            if (longestCheckoutTime > poolMaximumCheckoutTime) {
            	//如果checkout时间过长,则这个connection标记为overdue(过期)
              // Can claim overdue connection
              state.claimedOverdueConnectionCount++;
              state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;
              state.accumulatedCheckoutTime += longestCheckoutTime;
              state.activeConnections.remove(oldestActiveConnection);
              if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {
                oldestActiveConnection.getRealConnection().rollback();
              }
              //删掉最老的连接,然后再new一个新连接
              conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);
              oldestActiveConnection.invalidate();
              if (log.isDebugEnabled()) {
                log.debug("Claimed overdue connection " + conn.getRealHashCode() + ".");
              }
            } else {
            	//如果checkout时间不够长,等待吧
              // Must wait
              try {
                if (!countedWait) {
                	//统计信息:等待+1
                  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) {
        	//如果已经拿到connection,则返回
          if (conn.isValid()) {
            if (!conn.getRealConnection().getAutoCommit()) {
              conn.getRealConnection().rollback();
            }
            conn.setConnectionTypeCode(assembleConnectionTypeCode(dataSource.getUrl(), username, password));
            //记录checkout时间
            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.");
            }
            //如果没拿到,统计信息:坏连接+1
            state.badConnectionCount++;
            localBadConnectionCount++;
            conn = null;
            if (localBadConnectionCount > (poolMaximumIdleConnections + 3)) {
            	//如果好几次都拿不到,就放弃了,抛出异常
              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;
  }

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
MyBatis是一个持久层框架,它并不直接提供数据库连接池的功能,而是依赖于其他的数据库连接池实现来管理数据库连接。 在MyBatis中,可以通过配置文件来指定使用的数据库连接池。常用的数据库连接池实现有: 1. Apache Commons DBCP:一个开源的数据库连接池实现,具有良好的性能和稳定性。 2. C3P0:另一个常用的开源数据库连接池实现,提供了更多的配置选项和监控功能。 3. HikariCP:一个高性能的数据库连接池实现,相对于DBCP和C3P0更为轻量级和快速。 你可以根据自己的需求选择适合的数据库连接池实现,并在MyBatis的配置文件中进行相关配置。一般情况下,你需要配置连接池的最大连接数、最小连接数、连接超时时间等参数。 例如,使用Apache Commons DBCP作为数据库连接池的示例配置如下: ```xml <dataSource type="org.apache.commons.dbcp2.BasicDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mydatabase"/> <property name="username" value="root"/> <property name="password" value="mypassword"/> <property name="initialSize" value="5"/> <property name="maxTotal" value="20"/> <property name="maxIdle" value="10"/> <property name="maxWaitMillis" value="10000"/> </dataSource> ``` 这是一个简单的配置示例,你可以根据自己的实际情况进行调整。在配置文件中,还可以设置其他属性,如连接池的验证语句、连接池的空闲对象清理策略等。 需要注意的是,MyBatis只是使用数据库连接池来管理数据库连接,并不负责连接池的具体实现。因此,你需要在项目中引入相应的数据库连接池实现,并将其配置到MyBatis的配置文件中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值