spring boot 对 mybatis 的自动配置

spring boot 自动配置 Mybatis

mybatis-spring-boot-autoconfigure 中的 spring.factories 中, 配置了 org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration 这个 @Configuration 类。

@org.springframework.context.annotation.Configuration
public class MybatisAutoConfiguration implements InitializingBean {
	@Bean   // SqlSessionFactoryBean 是 SqlSessionFactory 的一个实现
	public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
		SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
		... 设置一堆属性 ... 
		return factory.getObject();
	}
	@Bean   // sqlSessionTemplate 是 SqlSession 的一个实现
	public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
		return new SqlSessionTemplate(sqlSessionFactory);
	}

}

SqlSessionFactory 是怎么创建 SqlSession 的? (怎么创建SqlSessionTemplate的?)

1. 先来看看 SqlSessionFactoryBean 是什么
public class SqlSessionFactoryBean
implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
    public SqlSessionFactory getObject() throws Exception {
            if (this.sqlSessionFactory == null) {
                // 内部会返回 new DefaultSqlSessionFactory(configuration)
                afterPropertiesSet();
            }
            return this.sqlSessionFactory;
        }
    }
}

SqlSessionFactoryBean 实现了 FactoryBean 接口, 代表 SqlSessionFactoryBean 的实例不只是一个普通的bean对象,还可以产生 一个 bean 放到容器中。 该类的 getObject() 会返回 DefaultSqlSessionFactory

2. SqlSessionTemplate 如何产生

看到上面的 new SqlSessionTemplate(sqlSessionFactory) 创建了 SqlSessionTemplate,持有 DefaultSqlSessionFactory 的对象引用

3. 如何执行对一个的方法

SqlSessionTemplate 的构造函数会 JDK 动态代理生成 SqlSession 接口的代理对象 sqlSessionProxy。 SqlSessionTemplate 内部的所有 select 方法均由该代理对象执行

public class SqlSessionTemplate implements SqlSession, DisposableBean {
// 代理对象
private final SqlSession sqlSessionProxy;

public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) {
    ... ...
    // JDK 代理
    this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance(
        SqlSessionFactory.class.getClassLoader(),
        new Class[] { SqlSession.class }, // 接口
        new SqlSessionInterceptor()       // InvocationHandler 对象
        );
    }
}

InvocationHandler 是 new SqlSessionInterceptor(), 因此, 重点在 invoke() 方法的实现:

  • (1)获取 sqlSession 对象
    先从 ThreadLocal 中获取, 如果没有,使用 sessionFactory.openSession() 创建。这里的 sessionFactory 是上面创建的 DefaultSqlSessionFactory
  • (2)由 sqlSession 对象执行代理的方法 (如select, insert 等方法)
private class SqlSessionInterceptor implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // (1) 获取 SqlSession
        SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
        SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
        try {
            // (2) 放行由 DefaultSqlSession 执行 select 等方法
            Object result = method.invoke(sqlSession, args);
            // (3) 是否需要自动提交事务
            if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
            sqlSession.commit(true);
            }
            return result;
        } catch (Throwable t) {
        ... ...
    }
}

(1) 获取 sqlSession 对象

// DefaultSqlSessionFactory.java
public SqlSession openSession(ExecutorType execType) {
    return openSessionFromDataSource(execType, null, false);
}

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      final Executor executor = configuration.newExecutor(tx, execType);
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } 
    // ...
}
4. 每次执行 sql 语句开启一个 SqlSession 吗? SqlSession 是线程安全的吗?
  • 上面的代码分析中, SqlSession 会从 ThreadLocal 中先获取, 因此, SqlSession 是线程安全的. 不同线程的 sql 执行才会开启新的 SqlSession;
  • SqlSession 中包含一个Executor, 用来执行 sql 语句; 而 Executor 中包含一个 Transaction 用来表示事务. 这个 Transaction 是从 SpringManagedTransaction 中获取的, SpringManagedTransaction.commit() 调用的 connection.commit()
5. DefaultSqlSession 的各种 select 方法, 都是使用 Executor 接口完成的

真正的 Sql 执行者: Executor. Executor 包装了一级缓存和二级缓存

  • 一级缓存: 共享在一个 sqlSession 中, 如果 sql 语句相同, 会优先命中一级缓存
  • 二级缓存: 在多个 SQLSession 中共享, 开启二级缓存会使用 CachingExecutor 装饰 Executor
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值