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对连接的及时、统计、等运维层动态监控的功能。