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执行数据库操作,然后对执行的结果集处理,返回给应用层做展现处理
更多详情请参照以上文章
什么是 Druid?
Druid是Java语言中最好的数据库连接池。Druid能够提供强大的监控和扩展功能。
更多详情请参照以上文章:
分享源码相关maven依赖
<!-- mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
<!-- druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.0</version>
</dependency>
mybatis和druid部分配置
spring.application.name=think-in-spring-boot
server.port=8080
# 开启所有endpoint
management.endpoints.web.exposure.include= *
# mybatis相关配置
mybatis.mapper-locations=classpath*:/mapper/*.xml
mybatis.type-aliases-package=think.in.spring.boot.model
mybatis.type-handlers-package=think.in.spring.boot.typehandler
mybatis.configuration.map-underscore-to-camel-case=true
mybatis.configuration.default-fetch-size=100
mybatis.configuration.default-statement-timeout=30
# 日志级别
debug=true
logging.level.root=info
logging.level.tk.mybatis.springboot.mapper=trace
# DataSource相关配置
spring.datasource.url=jdbc:mysql://localhost:3306/testdb
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
# druid相关配置
spring.datasource.druid.initial-size=1
spring.datasource.druid.min-idle=1
spring.datasource.druid.max-active=20
spring.datasource.druid.test-on-borrow=true
spring.datasource.druid.stat-view-servlet.allow=true
源码分析
自动装配过程
源码入口
spring.factories
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
加载过程
org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#selectImports()
装配org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
装配条件
@org.springframework.context.annotation.Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnBean(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
// 最先装配 即:Sqlsession要持有 DataSource 和 Configuration
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class MybatisAutoConfiguration {
包含内容
@ConditionalOnClass SqlSessionFactory.class
DataSource
@AutoConfigureAfter DataSourceAutoConfiguration.class
SqlSessionFactoryBean
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
// 后处理器
public void afterPropertiesSet() throws Exception {
// 此处主要是创建默认的 DefaultSqlSessionFactory、DefaultSqlSession 非线程安全
this.sqlSessionFactory = buildSqlSessionFactory();
}
// 监听ApplicationEvent
public void onApplicationEvent(ApplicationEvent event) {
if (failFast && event instanceof ContextRefreshedEvent) {
this.sqlSessionFactory.getConfiguration().getMappedStatementNames();
}
}
}
DataSourceAutoConfiguration
@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class,
DataSourceInitializationConfiguration.class })
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 })
//可以通过配置spring.datasource自动装配上述几种数据库连接池
protected static class PooledDataSourceConfiguration {
}
}
SqlSessionTemplate
@Bean
@ConditionalOnMissingBean
// 线程安全的,Spring's Transaction Manager
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
ExecutorType executorType = this.properties.getExecutorType();
if (executorType != null) {
return new SqlSessionTemplate(sqlSessionFactory, executorType);
} else {
return new SqlSessionTemplate(sqlSessionFactory);
}
// 创建SqlSessionTemplate
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,PersistenceExceptionTranslator exceptionTranslator) {
this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator;
// 创建代理对象由代理对象执行数据库操作
this.sqlSessionProxy = (SqlSession) newProxyInstance(
SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class },
new SqlSessionInterceptor());
}
// 代理对象,具体执行的操作
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 获取 sqlsession,重点
SqlSession sqlSession = getSqlSession(
SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType,
SqlSessionTemplate.this.exceptionTranslator);
try {
Object result = method.invoke(sqlSession, args);
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
// 强制提交
sqlSession.commit(true);
}
return result;
} finally {
if (sqlSession != null) {
//关闭 session连接
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
DataSource 数据源配置类
/**
* @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;
}
// 不使用线程池实现
// @Bean
// public DataSource dataSource() throws SQLException {
// UnpooledDataSource dataSource = new UnpooledDataSource("com.mysql.jdbc.Driver", "jdbc:mysql://localhost:3306/testdb", "root", "123456");
// return dataSource;
// }
// // 采用 MyBatis 默认支持的线程池
// @Bean
// public DataSource dataSource() throws SQLException {
// PooledDataSource dataSource = new PooledDataSource("com.mysql.jdbc.Driver", "jdbc:mysql://localhost:3306/testdb", "root", "123456");
// return dataSource;
// }
// @Bean
// @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
// public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
// return new SqlSessionTemplate(sqlSessionFactory);
// }
// druid 相关 filter 和 servlet 配置
// @Bean
// public ServletRegistrationBean statViewServlet(){
// ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(),"/druid/*");
// servletRegistrationBean.addInitParameter("allow","127.0.0.1"); //设置ip白名单
// servletRegistrationBean.addInitParameter("deny","192.168.0.19");//设置ip黑名单,优先级高于白名单
// //设置控制台管理用户
// servletRegistrationBean.addInitParameter("loginUsername","root");
// servletRegistrationBean.addInitParameter("loginPassword","root");
// //是否可以重置数据
// servletRegistrationBean.addInitParameter("resetEnable","false");
// return servletRegistrationBean;
// }
//
// @Bean
// public FilterRegistrationBean statFilter(){
// //创建过滤器
// FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());
// //设置过滤器过滤路径
// filterRegistrationBean.addUrlPatterns("/*");
// //忽略过滤的形式
// filterRegistrationBean.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
// return filterRegistrationBean;
// }
}
执行查询操作
http://localhost:8080/find-all-courses
think.in.spring.boot.mapper.CourseMapper#findAllCourses
DynamicAdvisedInterceptor#intercept
ReflectiveMethodInvocation#proceed
TransactionInterceptor#invoke
TransactionAspectSupport#invokeWithinTransaction
TransactionAspectSupport#createTransactionIfNecessary
### 支持 org.springframework.transaction.jta.JtaTransactionManager
### 和 org.springframework.jdbc.datasource.DataSourceTransactionManager
AbstractPlatformTransactionManager#getTransaction
DataSourceTransactionManager#doGetTransaction
TransactionSynchronizationManager#getResource
TransactionSynchronizationManager#doGetResource
DataSourceTransactionManager#doBegin
TransactionSynchronizationManager#bindResource
AbstractPlatformTransactionManager#prepareSynchronization
TransactionSynchronizationManager#initSynchronization
### 执行细节
MapperProxy#invoke
MapperMethod#execute
SqlSessionTemplate#selectList()
SqlSessionTemplate.SqlSessionInterceptor#invoke
SqlSessionUtils#getSqlSession()
CachingExecutor#query()
BaseExecutor#query()
SqlSessionUtils#closeSqlSession
BaseExecutor#close
TransactionSynchronizationManager#doUnbindResource
SqlSessionUtils#getSqlSession()
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
// 查询 Resource 中的 SqlSessionHolder
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
// 从 holder 去取 SqlSession 存在则返回
SqlSession session = sessionHolder(executorType, holder);
if (session != null) {
return session;
}
// 创建 SqlSession
session = sessionFactory.openSession(executorType);
// 注册 SqlSessionHolder
registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
// 返回 SqlSession
return session;
}
Sqlsession连接管理
//管理每个线程的资源和事务同步的代理实现
// 以列表的方式支持资源同步,事务同步必须由事务激活和停用管控 {@link #initSynchronization()} and {@link #clearSynchronization()}
// 事务支持与否主要在于 {@link #isSynchronizationActive}
public abstract class TransactionSynchronizationManager {
private static final Log logger = LogFactory.getLog(TransactionSynchronizationManager.class);
private static final ThreadLocal<Map<Object, Object>> resources =
new NamedThreadLocal<>("Transactional resources");
private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
new NamedThreadLocal<>("Transaction synchronizations");
private static final ThreadLocal<String> currentTransactionName =
new NamedThreadLocal<>("Current transaction name");
private static final ThreadLocal<Boolean> currentTransactionReadOnly =
new NamedThreadLocal<>("Current transaction read-only status");
private static final ThreadLocal<Integer> currentTransactionIsolationLevel =
new NamedThreadLocal<>("Current transaction isolation level");
private static final ThreadLocal<Boolean> actualTransactionActive =
new NamedThreadLocal<>("Actual transaction active");
具体相关步骤如下
本次分享包含 MyBatis 在 Spring Boot 中的自动装配,相关组件有 DataSource(数据源)、SqlSessionFactory(sqlsession工厂类)、SqlSessionTemplate(sqlsession连接器)、SqlSessionUtils(工具类)等,主要分析的部分包括 SqlSession 的创建、管理、复用、销毁,后续对数据库操作的执行的相关流程,由于集成了druid,使用其作为数据库连接池所以在初始化Datasource和事务管理器,将在下篇文章详细分析,敬请关注。
更多优秀文章
java界的小学生
https://blog.csdn.net/shang_xs
更多MyBatis相关
MyBatis深入理解和使用-MyBatis缓存体系
https://blog.csdn.net/shang_xs/article/details/86656353
MyBatis深入理解和使用-MyBatis缓存体系
https://blog.csdn.net/shang_xs/article/details/86656353
MyBatis深入理解和使用-TypeHandler
https://blog.csdn.net/shang_xs/article/details/86656173
Spring结合Tomcat和Jndi实现数据源外部化配置
https://blog.csdn.net/shang_xs/article/details/90599810
Spring结合Jboss和Jndi实现数据源外部化配置
https://blog.csdn.net/shang_xs/article/details/90610242
深入学习和理解Servlet(一)
https://blog.csdn.net/shang_xs/article/details/90371068
深入学习和理解Servlet(二)
https://blog.csdn.net/shang_xs/article/details/90376489
完整代码和相关依赖请见GitHub
https://github.com/dwyanewede/spring-boot/tree/master/spring-webmvc/src/main