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

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能够提供强大的监控和扩展功能。

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

分享源码相关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");

具体相关步骤如下

1558595047460

本次分享包含 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

公众号推荐

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值