写点什么

聊聊 Mybatis 的数据源之 PooledDataSource

作者:周杰伦本人
  • 2022-11-23
    山东
  • 本文字数:2433 字

    阅读完需:约 8 分钟

聊聊 Mybatis 的数据源之 PooledDataSource

上篇文章中我们介绍了 Mybatis 的数据源模块的 DataSource 接口和它对应的实现类 UnpooledDataSource、PooledDataSource,这篇文章详细介绍一下 PooledDataSource


PooledDataSource 使用了数据库连接池可以实现数据库连接池的重复利用,还能控制连接数据库的连接上限,实现数据库连接的统一管理,缓存数据连接信息还能防止流量突发连接数据库不及时


PooledDataSource 有个 PoolState 状态,PoolState 里保存着数据库连接信息 PooledConnection,PooledConnection 实现 InvocationHandler 接口,重写 invoke 方法,显然这是一个代理类,使用了 JDK 的动态代理


   class PooledConnection implements InvocationHandler {        private static final Class<?>[] IFACES = new Class<?>[] { Connection.class };        public PooledConnection(Connection connection, PooledDataSource dataSource) {        this.hashCode = connection.hashCode();        this.realConnection = connection;        this.dataSource = dataSource;        this.createdTimestamp = System.currentTimeMillis();        this.lastUsedTimestamp = System.currentTimeMillis();        this.valid = true;        this.proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), IFACES, this);    }        @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        String methodName = method.getName();        if (CLOSE.equals(methodName)) {            dataSource.pushConnection(this);            return null;        }        try {            if (!Object.class.equals(method.getDeclaringClass())) {                checkConnection();            }            return method.invoke(realConnection, args);        } catch (Throwable t) {            throw ExceptionUtil.unwrapThrowable(t);        }    }        }
复制代码


我们看一看到构造方法中调用了 Proxy.newProxyInstance()方法来生成代理类,而重写 invoke 方法中如果是 close()就调用 pushConnection()方法直接把它放入连接池而不是关闭连接,其他情况调用 checkConnection()检查连接信息,代理类调用 realConnection()方法


下面就看一下 pushConnection()方法

PooledDataSource 的 pushConnection()方法

方法的功能就是把数据库连接放入连接池中


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++; } } }
复制代码


  1. 从活跃连接集合中删除该连接

  2. 如果 PooledConnection 有效,并且空闲连接数小于最大空闲连接数,就利用当前 PooledConnection 创建 PooledConnection,放入空闲连接数集合中,方便下次使用,关闭当前 PooledConnection 对象的数据库连接,并对当前 PooledConnection 对象设置无效,最后唤醒其他等待的线程。如果空闲连接数大于最大空闲连接数了就关闭连接,设置当前连接无效

  3. 如果 PooledConnection 无效,badConnectionCount 加一,这个 badConnectionCount 是记录无效的数据库连接信息的

总结

本篇文章主要介绍了 PooledConnection 和 PooledDataSource 的 pushConnection()方法,PooledConnection 用到了 jdk 的动态代理,生成 Connection 的实现类的代理类,拦截的逻辑中对于 close()方法没有真正关闭,而是把数据库连接信息放入连接池中供下次再使用,数据库连接信息放入连接池的过程是通过调用 PooledDataSource 的 pushConnection()来完成的,具体就是从活跃连接集合中删除这个连接,然后放入空闲连接数集合中并把当前连接设置为无效。

发布于: 刚刚阅读数: 2
用户头像

还未添加个人签名 2020-02-29 加入

公众号《周结论本人》,多平台优质博主

评论

发布
暂无评论
聊聊Mybatis的数据源之PooledDataSource_11月月更_周杰伦本人_InfoQ写作社区