MyBatis 源码解析之SqlSession

4 篇文章 0 订阅
2 篇文章 0 订阅

openSession
MyBatis 在解析完配置文件后生成了一个 DefaultSqlSessionFactory 对象,后续执行 SQL 请求的时候都是调用其 openSession 方法获得 SqlSessison,相当于一个 SQL 会话。 SqlSession 提供了操作数据库的一些方法,如 select、update 等。
先看一下 DefaultSqlSessionFactory 的 openSession 的代码:

public SqlSession openSession() {
  return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);} 

private SqlSession openSessionFromDataSource(ExecutorType execType, 
TransactionIsolationLevel level, boolean autoCommit) {
  Transaction tx = null;  
  try {  
       // 从 configuration 取出配置    
       final Environment environment = configuration.getEnvironment();    
       final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);    
       tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);    
       // 每个 SqlSession 都有一个单独的 Executor 对象    
       final Executor executor = configuration.newExecutor(tx, execType, autoCommit);    
       // 返回 DefaultSqlSession 对象    
       return new DefaultSqlSession(configuration, executor);  
   } catch (Exception e) {    
       closeTransaction(tx); // may have fetched a connection so lets call close()    
       throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);  
   } finally {    
       ErrorContext.instance().reset();  
   }
}

主要代码在 openSessionFromDataSource,首先是从 Configuration 中取出相关的配置,生成 Transaction,接着又创建了一个 Executor,最后返回了 DefaultSqlSession 对象。
这里的 Executor 是什么呢?它其实是一个执行器,SqlSession 的操作会交给 Executor 去执行。MyBatis 的 Executor 常用的有以下几种:
SimpleExecutor: 默认的 Executor,每个 SQL 执行时都会创建新的 Statement
ResuseExecutor: 相同的 SQL 会复用 Statement BatchExecutor: 用于批处理的
Executor CachingExecutor: 可缓存数据的 Executor,用代理模式包装了其它类型的 Executor
了解了 Executor 的类型后,看一下 configuration.newExecutor(tx, execType, autoCommit) 的代码:

public Executor newExecutor(Transaction transaction, ExecutorType executorType) { 
 // 默认是 SimpleExecutor  
 executorType = executorType == null  defaultExecutorType : 
 executorType;  executorType = executorType == null  ExecutorType.SIMPLE : executorType;  
 Executor executor;  
 if (ExecutorType.BATCH == executorType) {  
   executor = new BatchExecutor(this, transaction);  
} else if (ExecutorType.REUSE == executorType) {  
  executor = new ReuseExecutor(this, transaction);  
} else {  
  executor = new SimpleExecutor(this, transaction);  
}  
// 默认启动缓存  
if (cacheEnabled) {  
  executor = new CachingExecutor(executor);  
}  
executor = (Executor) interceptorChain.pluginAll(executor);  
return executor;

MyBatis 默认启用一级缓存,即同一个 SqlSession 会共用同一个缓存,上面代码最终返回的是 CachingExecutor。
getMapper
在创建了 SqlSession 之后,下一步是生成 Mapper 接口的代理类,代码如下:

public <t> T getMapper(Class<t> type) {
   return 
configuration.<t>getMapper(type, this); 
   }</t></t></t>

可以看出是从 configuration 中取得 Mapper,最终调用了 MapperProxyFactory 的 newInstance。MapperProxyFactory 在上一篇文章已经分析过,它是为了给 Mapper 接口生成代理类,其中关键的拦截逻辑在 MapperProxy 中,下面是其 invoke 方法:

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  try {
      // 过滤一些不需要被代理的方法    
      if (Object.class.equals(method.getDeclaringClass())) {   
         return method.invoke(this, args);    
      } else if (isDefaultMethod(method)) {   
         return invokeDefaultMethod(proxy, method, args);    
      }  
   } catch (Throwable t) {
       throw ExceptionUtil.unwrapThrowable(t);  
   }  
   // 从缓存中获取 MapperMethod 然后调用  
   final MapperMethod mapperMethod = cachedMapperMethod(method);  
   return mapperMethod.execute(sqlSession, args);
}

MapperProxy 中调用了 MapperMethod 的 execute,下面是部分代码:

public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;    
    switch (command.getType()) {
          case INSERT: {      
          Object param = method.convertArgsToSqlCommandParam(args);    
              result = rowCountResult(sqlSession.insert(command.getName(), param));        
              break;      
          }      
          case UPDATE: {     
             Object param = method.convertArgsToSqlCommandParam(args);        
             result = rowCountResult(sqlSession.update(command.getName(), param));        
             break;      
         }      
         case DELETE: {     
            Object param = method.convertArgsToSqlCommandParam(args);        
            result = rowCountResult(sqlSession.delete(command.getName(), param));        
            break;      
         }      
         case SELECT:
         if (method.returnsVoid() && method.hasResultHandler()) {          
               executeWithResultHandler(sqlSession, args);          
               result = null;

可以看出,最终调用了 SqlSession 的对应方法,也就是 DefaultSqlSession 中的方法。
select
先看一下 DefaultSqlSession 中 select 的代码:

public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
  try {
      MappedStatement ms = configuration.getMappedStatement(statement);    
      executor.query(ms, wrapCollection(parameter), rowBounds, handler);  
 } catch (Exception e) { 
    throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);  
 } finally {  
   ErrorContext.instance().reset();  
 }
}

select 中调用了 executor 的 query,上面提到,默认的 Executor 是 CachingExecutor,看其中的代码:

@Override
  public <e> List<e> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {  
    BoundSql boundSql = ms.getBoundSql(parameterObject);   
     // 获取缓存的key    
     CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);    
     return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);  }public <e> List<e> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)   
      throws SQLException {    
    // 获取缓存    
    Cache cache = ms.getCache();    
    if (cache != null) {    
      flushCacheIfRequired(ms);    
      if (ms.isUseCache() && resultHandler == null) {
      ensureNoOutParams(ms, boundSql);        
      @SuppressWarnings("unchecked")        
      List<e> list = (List<e>) tcm.getObject(cache, key);        
      if (list == null) {      
          list = delegate.<e> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);          
          tcm.putObject(cache, key, list); // issue #578 and #116        
      }        
      return list;      
   }    
 }    
 // 调用代理对象的缓存    
 return delegate.<e> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);  
}</e></e></e></e></e></e></e></e>

首先检查缓存中是否有数据,如果没有再调用代理对象的 query,默认是 SimpleExecutor。Executor 是一个接口,下面有个实现类是 BaseExecutor,其中实现了其它 Executor 通用的一些逻辑,包括 doQuery 以及 doUpdate 等,其中封装了 JDBC 的相关操作。
update
update 的执行与 select 类似, 都是从 CachingExecutor 开始,看代码:

@Override
public int update(MappedStatement ms, Object parameterObject) throws SQLException {
  // 检查是否需要刷新缓存  
  flushCacheIfRequired(ms);  
  // 调用代理类的 update  
  return delegate.update(ms, parameterObject);
}

update 会使得缓存的失效,所以第一步是检查是否需要刷新缓存,接下来再交给代理类去执行真正的数据库更新操作。
对标阿里P6架构师
想要获取更多学习资料,可以扫码进群或关注公众号
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值