druid 数据连接getConnection学习

84 篇文章 23 订阅

上一篇看了druid的初始化方法。本篇继续看连接获取的源码。基于1.1.6版本

 public DruidPooledConnection getConnection(long maxWaitMillis) throws SQLException {
        init(); //先初始化,已经初始化的跳过

        if (filters.size() > 0) { //判断过滤链,最终还是调用了getConnectionDirect

            FilterChainImpl filterChain = new FilterChainImpl(this);
            return filterChain.dataSource_connect(this, maxWaitMillis);
        } else {
            return getConnectionDirect(maxWaitMillis);
        }
    }

判断过滤链,有就用,没有就getConnectionDirect  

 public DruidPooledConnection getConnectionDirect(long maxWaitMillis) throws SQLException {
        int notFullTimeoutRetryCnt = 0;
        for (;;) {
            // handle notFullTimeoutRetry
            DruidPooledConnection poolableConnection;
            try {
                poolableConnection = getConnectionInternal(maxWaitMillis);
            } catch (GetConnectionTimeoutException ex) {
                if (notFullTimeoutRetryCnt <= this.notFullTimeoutRetryCount && !isFull()) {
                    notFullTimeoutRetryCnt++;
                    if (LOG.isWarnEnabled()) {
                        LOG.warn("get connection timeout retry : " + notFullTimeoutRetryCnt);
                    }
                    continue;
                }
                throw ex;
            }
 if (testOnBorrow) {//取得对象验证有效性,还有空闲检测有效性testWhileIdle
}
  if (removeAbandoned) {//检测去除无效链接
}

核心方法是getConnectionInternal ,下面是一些根据配置进行的有效性检测。可以理解为什么配置的参数怎么起的作用。

 private DruidPooledConnection getConnectionInternal(long maxWait) throws SQLException {
       //校验,忽略
................... 
            try {
                lock.lockInterruptibly();
            } catch (InterruptedException e) {
                connectErrorCountUpdater.incrementAndGet(this);
                throw new SQLException("interrupt", e);
            }

            try {
                if (maxWaitThreadCount > 0
                        && notEmptyWaitThreadCount >= maxWaitThreadCount) {
                    connectErrorCountUpdater.incrementAndGet(this);
                    throw new SQLException("maxWaitThreadCount " + maxWaitThreadCount + ", current wait Thread count "
                            + lock.getQueueLength());
                }
                
                /**异常抛出 。。。。*/
                connectCount++;

              
                if (maxWait > 0) {
                    holder = pollLast(nanos);
                } else {
                    holder = takeLast();
                }           
....

        holder.incrementUseCount();
        DruidPooledConnection poolalbeConnection = new DruidPooledConnection(holder);
        return poolalbeConnection;
    }

精简了代码,保留主要逻辑,先从连接池takelast()中取出DruidConnectionHolder,然后再封装成DruidPooledConnection对象返回。再看看取holder的方法:

DruidConnectionHolder takeLast() throws InterruptedException, SQLException {
        try {
            while (poolingCount == 0) {
                emptySignal(); // send signal to CreateThread create connection

                if (failFast && failContinuous.get()) {
                    throw new DataSourceNotAvailableException(createError);
                }

                notEmptyWaitThreadCount++;
                if (notEmptyWaitThreadCount > notEmptyWaitThreadPeak) {
                    notEmptyWaitThreadPeak = notEmptyWaitThreadCount;
                }
                try {
                    notEmpty.await(); // signal by recycle or creator
                } finally {
                    notEmptyWaitThreadCount--;
                }
                notEmptyWaitCount++;

                if (!enable) {
                    connectErrorCountUpdater.incrementAndGet(this);
                    throw new DataSourceDisableException();
                }
            }
        } catch (InterruptedException ie) {
            notEmpty.signal(); // propagate to non-interrupted thread
            notEmptySignalCount++;
            throw ie;
        }

        decrementPoolingCount();
        DruidConnectionHolder last = connections[poolingCount];
        connections[poolingCount] = null;

        return last;
    }

这里是使用了Condition,在DruidAbstractDataSource里。

 protected Condition                                notEmpty;

    protected Condition                                empty;

大致逻辑:先判断池中的连接数,如果到0了,那么本线程就得被挂起,同时释放empty信号,并且等待notEmpty的信号,就是notEmpty.await()。 下面是如果有连接,就取出数组的最后一个,同时--poolingCount。

补充下等待notEmpty,就是CreateConnectionTask往datasource的连接池put的时候

private boolean put(DruidConnectionHolder holder) {
        lock.lock();
        try {
            if (poolingCount >= maxActive) {
                return false;
            }
            connections[poolingCount] = holder;
            incrementPoolingCount();

            if (poolingCount > poolingPeak) {
                poolingPeak = poolingCount;
                poolingPeakTime = System.currentTimeMillis();
            }

            notEmpty.signal();
            notEmptySignalCount++;

            if (createScheduler != null) {
                createTaskCount--;

                if (poolingCount + createTaskCount < notEmptyWaitThreadCount //
                    && activeCount + poolingCount + createTaskCount < maxActive) {
                    emptySignal();
                }
            }
        } finally {
            lock.unlock();
        }
        return true;
    }

基本上理解了druid数据库连接池获取连接的过程。这里有几个类关系需要补充下。

DruidDataSource 有数组    private volatile DruidConnectionHolder[] connections;

createStatement,commit,roolback等都是使用DruidPooledConnection 有holder,thread

   protected volatile   DruidConnectionHolder holder;

   protected final      Thread                ownerThread;

holder里面有数据库连接跟数据源

protected final Connection                    conn;

    protected final DruidAbstractDataSource       dataSource;

**************************

druid两大块里面,只看了pool部分,sql解析器还没看。用了很多juc底层的函数。过滤链部分如何实现还没看。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
非常抱歉,我的回答仍然有误。在MyBatis中,我们可以使用DruidDataSource作为数据源,但是SqlSessionFactoryBuilder的build()方法并不支持直接传入DruidDataSource.getConnection()方法返回的Connection对象,也没有build(Connection)方法。 正确的做法是,通过DruidDataSource获取一个Connection对象,然后将它作为参数传递给MyBatis的TransactionFactory对象的createTransaction()方法,再将返回的Transaction对象作为参数传递给SqlSessionFactoryBuilder的build()方法。代码示例如下: ```java DruidDataSource dataSource = new DruidDataSource(); // 配置数据源 // ... Connection connection = dataSource.getConnection(); TransactionFactory transactionFactory = new JdbcTransactionFactory(); Transaction transaction = transactionFactory.createTransaction(connection); Environment environment = new Environment("development", transactionFactory, transaction); Configuration configuration = new Configuration(environment); // 配置MyBatis // ... SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(configuration); SqlSession sqlSession = sessionFactory.openSession(); ``` 在上述代码中,我们通过DruidDataSource获取一个Connection对象,然后将它作为参数传递给JdbcTransactionFactory的createTransaction()方法,创建一个Transaction对象。然后,我们创建一个Environment对象,将TransactionFactory和Transaction对象作为参数传递进去。接着,我们创建一个Configuration对象,并将Environment对象作为参数传递进去。在Configuration对象中,我们可以配置MyBatis的各种参数,例如Mapper映射文件、插件等。最后,我们使用SqlSessionFactoryBuilder的build()方法来构建SqlSessionFactory对象,并使用openSession()方法打开一个新的SqlSession对象。 需要注意的是,上述代码中的JdbcTransactionFactory是MyBatis提供的一个TransactionFactory实现类,用于创建基于JDBC连接的事务。如果我们使用其他的事务管理器,例如Spring的事务管理器,则需要使用对应的TransactionFactory实现类。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值