MyBatis集成Druid实现数据库线程池管理(二)

什么是 MyBatis

MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

MyBatis主要组成部分

  • configuration
  • sqlsession
  • executor

sqlsession解析配置的configuration(注解标注的SQL或者是xml配置文件),使用相应的executor执行数据库操作,然后对执行的结果集处理,返回给应用层做展现处理

更多详情请参照以上文章

MyBatis深入理解和使用-MyBatis缓存体系

MyBatis深入理解和使用-TypeHandler

MyBatis深入理解和使用-MyBatis

什么是 Druid?

Druid是Java语言中最好的数据库连接池。Druid能够提供强大的监控和扩展功能。

更多详情请参照以上文章:

其余相关知识,请参见上篇文章 MyBatis集成Druid实现数据库线程池管理(一)

自动装配过程
spring.factories
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration

DataSourceAutoConfiguration 自动装配实现

@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class,
      DataSourceInitializationConfiguration.class })// 初始化DataSource信息
public class DataSourceAutoConfiguration {
    @Configuration
	@Conditional(PooledDataSourceCondition.class)
	@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
	@Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,
			DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.Generic.class,
			DataSourceJmxConfiguration.class })
	protected static class PooledDataSourceConfiguration {

	}
}

由于采用的是Druid,故新增配置,配置源参见上篇文章

/**
 * @ClassName: DataSource
 * @Description: DataSource 数据源配置类,可选择 DruidDataSource、UnpooledDataSource、PooledDataSource
 * @Author: 尚先生
 * @CreateDate: 2019/5/22 12:44
 * @Version: 1.0
 */
@Configuration
public class DataSourceConfiguration {
    @Value("${spring.druid.filters}")
    private String filters;
    // 采用 Druid 实现线程池
    @Bean
    @ConfigurationProperties(prefix = "spring.druid")
    public DataSource dataSource() throws SQLException {
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setFilters(filters);
        return druidDataSource;
    }
}

DruidDataSource代码分析

public class DruidDataSource extends DruidAbstractDataSource implements DruidDataSourceMBean, ManagedDataSource, Referenceable, Closeable, Cloneable, ConnectionPoolDataSource, MBeanRegistration {
    public DruidDataSource(boolean fairLock){
        // 默认采用非公平锁机制
        super(fairLock);
        // 配置数据源属性信息
        configFromPropety(System.getProperties());
    }
}

TransactionAutoConfiguration自动装配实现

@Configuration
@ConditionalOnClass(PlatformTransactionManager.class)
@AutoConfigureAfter({ JtaAutoConfiguration.class, HibernateJpaAutoConfiguration.class,
      // 数据库事务的实现
      DataSourceTransactionManagerAutoConfiguration.class,
      Neo4jDataAutoConfiguration.class })
@EnableConfigurationProperties(TransactionProperties.class)
public class TransactionAutoConfiguration {

    
}

开始查询处理

AbstractPlatformTransactionManager#getTransaction
DataSourceTransactionManager#doGetTransaction
DataSourceTransactionManager#doBegin
DruidDataSource#getConnection
DruidDataSource#init

在指定时间内获取连接,如果超时 new DruidPooledConnection(holder)

private DruidPooledConnection getConnectionInternal(long maxWait) throws SQLException {
    try {
        lock.lockInterruptibly();
    } catch (InterruptedException e) {
        connectErrorCount.incrementAndGet();
        throw new SQLException("interrupt", e);
    }
    try {
        if (maxWait > 0) {
            holder = pollLast(nanos);
        } else {
            holder = takeLast();
        }

    } catch (InterruptedException e) {
        connectErrorCount.incrementAndGet();
        throw new SQLException(e.getMessage(), e);
    } catch (SQLException e) {
        connectErrorCount.incrementAndGet();
        throw e;
    } finally {
        lock.unlock();
    }

    holder.incrementUseCount();

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

初始化DruidDataSource

public void init() throws SQLException {
        if (inited) {
            return;
        }
    	// Lock 实现锁机制
        final ReentrantLock lock = this.lock;
        try {
            lock.lockInterruptibly();
        } catch (InterruptedException e) {
            throw new SQLException("interrupt", e);
        }
    	// 保证值初始化一次
        boolean init = false;
        try {
            if (inited) {
                return;
            }
           // 设置 reset 开关
            dataSourceStat.setResetStatEnable(this.resetStatEnable);

            // DruidDataSource保存DruidConnectionHolder的数组,保存所有的数据库连接
            // DruidConnectionHolder持有数据库连接,还有所在的DataSource等
            // DruidPooledConnection持有DruidConnectionHolder,所在线程等
            connections = new DruidConnectionHolder[maxActive];
            evictConnections = new DruidConnectionHolder[maxActive];
            keepAliveConnections = new DruidConnectionHolder[maxActive];

            SQLException connectError = null;

            boolean asyncInit = this.asyncInit && createScheduler == null;
            if (!asyncInit) {
                try {
                    // 初始化 connection
                    for (int i = 0, size = getInitialSize(); i < size; ++i) {
                        PhysicalConnectionInfo pyConnectInfo = createPhysicalConnection();
                        DruidConnectionHolder holder = new DruidConnectionHolder(this, pyConnectInfo);
                        connections[poolingCount] = holder;
                        incrementPoolingCount();
                    }
            createAndLogThread();
            createAndStartCreatorThread();
            createAndStartDestroyThread();

            initedLatch.await();
            init = true;
            if (keepAlive) {
                // 异步进行最小任务数处理
                if (createScheduler != null) {
                    for (int i = 0; i < minIdle; ++i) {
                        createTaskCount++;
                        CreateConnectionTask task = new CreateConnectionTask();
                        this.createSchedulerFuture = createScheduler.submit(task);
                    }
                } else {
                    this.emptySignal();
                }
            }
        finally {
            // 标记初始化完成
            inited = true;
            // 释放锁
            lock.unlock();
        }
    }

超时后的连接获取

// 使用Lock-Condition
DruidConnectionHolder takeLast() throws InterruptedException, SQLException {
        try {
            while (poolingCount == 0) {
                emptySignal(); // 发出信号去创建连接
                notEmptyWaitThreadCount++;
                if (notEmptyWaitThreadCount > notEmptyWaitThreadPeak) {
                    notEmptyWaitThreadPeak = notEmptyWaitThreadCount;
                }
                try {
                    notEmpty.await(); //发出信号去复用或者创建连接
                } finally {
                    notEmptyWaitThreadCount--;
                }
                notEmptyWaitCount++;

                if (!enable) {
                    connectErrorCount.incrementAndGet();
                    throw new DataSourceDisableException();
                }
            }
        } catch (InterruptedException ie) {
            notEmpty.signal(); // 传播 non-interrupted 线程
            notEmptySignalCount++;
            throw ie;
        }

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

        return last;
    }
DruidPooledConnection#close
public void close() throws SQLException {
    // 如果为 true 表示已经关闭    
    if (this.disable) {
            return;
        }
        if (filters.size() > 0) {
            FilterChainImpl filterChain = new FilterChainImpl(dataSource);
            filterChain.dataSource_recycle(this);
        } else {
            // 回收连接处理
            recycle();
        }
        this.disable = true;
    }

1.DruidDataSource持有一个DruidConnectionHolder的数组,保存所有的数据库连接

private volatile DruidConnectionHolder[] connections;  // 注意这里的volatile

2.DruidConnectionHolder持有数据库连接,还有所在的DataSource等

private final DruidAbstractDataSource dataSource;
private final Connection  conn;
// 成员变量,可以做缓存,也可以做统计
private PreparedStatementPool statementPool;
private final List<Statement> statementTrace = new ArrayList<Statement>(2);

3.DruidPooledConnection持有DruidConnectionHolder,所在线程等

protected volatile DruidConnectionHolder holder;
private final Thread ownerThread;

总结

本次分享主要包含事务管理PlatformTransactionManager和DruidDataSource的装配和源码分析,在事务控制范围内对sqlsession连接的合理使用,用DruidDataSource以数组形式保存DruidConnectionHolder,用DruidConnectionHolder保存所持有的数据库连接以及数据源DataSource相关信息,用DruidPooledConnection保存当前线程信息和DruidConnectionHolder数组等,通过上述组合关系达到能动态控制连接数和sqlSession共享的问题,而且可以实现druid对连接的及时、统计、等运维层动态监控的功能。