Mybatis核心-SqlSession、Executor

本文探讨了策略模式在Mybatis Executor中的应用,展示了模板方法模式如何组织数据库操作,并深入解析了SqlSession和二级缓存的原理。重点剖析了CachingExecutor的装饰者模式在二级缓存中的作用及注意事项。
摘要由CSDN通过智能技术生成

一、策略模式

它定义了算法家族,分别封装起来,让它们之间可以相互替换。此模式让算法的变化不会影响到使用算法的客户。

DefaultSqlSession是客户角色,Executor是策略接口角色,Executor实现类是策略实现类角色。

二、模板方法模式

定义一个操作中的算法骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

BaseExecutor对应模板方法模式中的抽象类,BaseExecutor实现类对象模板方法模式中的实现类。

三、SqlSession

数据库会话接口,面向客户端,提供数据库常用操作的api,最终会委托executor来执行实际的数据库操作。SqlSession的实现类SqlSessionTemplate,DefaultSqlSession。springboot项目中,先是使用SqlSessionTemplate(DefaultSqlSession的代理类),再使用DefaultSqlSession。

  private class SqlSessionInterceptor implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      //获取DefaultSqlSession,开启了事务时则拿到的是同一个sqlSession对象,否则每次数据库操作都需要新创建一个sqlSession
      SqlSession sqlSession = getSqlSession(
          SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType,
          SqlSessionTemplate.this.exceptionTranslator);
      try {
        //调用DefaultSqlSession中的对应方法
        Object result = method.invoke(sqlSession, args);
        if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
          // force commit even on non-dirty sessions because some databases require
          // a commit/rollback before calling close()
          sqlSession.commit(true);
        }
        return result;
      } catch (Throwable t) {
        Throwable unwrapped = unwrapThrowable(t);
        if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
          // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
          sqlSession = null;
          Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
          if (translated != null) {
            unwrapped = translated;
          }
        }
        throw unwrapped;
      } finally {
        if (sqlSession != null) {
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
        }
      }
    }
  }
  public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {

    notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
    notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);

    //开启了事务则直接从事务同步管理器中拿sqlSession
    SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);

    SqlSession session = sessionHolder(executorType, holder);
    if (session != null) {
      return session;
    }

    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("Creating a new SqlSession");
    }

    //否则创建一个sqlSession
    session = sessionFactory.openSession(executorType);

    registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);

    return session;
  }
  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);
      //创建executor的代理
      final Executor executor = configuration.newExecutor(tx, execType);
      //创建DefaultSqlSession对象
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } 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();
    }
  }
  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    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
      executor = new SimpleExecutor(this, transaction);
    }
    if (cacheEnabled) {
      //二级缓存,使用了装饰者模式
      executor = new CachingExecutor(executor);
    }
    //如果使用到了插件,创建代理对象
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

四、Executor

Executor是mybatis的核心接口,定义了数据库的基本操作。使用到了模板方法模式和装饰者模式。

mybatis中的一级缓存

一级缓存是会话级别缓存SqlSession,默认开启。每一次数据库操作都会开启一个新的SqlSession,因此一级缓存是不生效的。开启事务时,多个数据库操作共用一个SqlSession,一级缓存有效。一级缓存使用的是PerpetualCache,缓存的key是CacheKey。

mybatis的二级缓存

二级缓存是基于namespace(mapper的全路径名)的,即同一个namespace下的所有MappedStatement共享同一个Cache。当开启了二级缓存,数据的查询流程:二级缓存-》一级缓存-》数据库。

TransactionalCache:保存在某个SqlSession的某个事务中需要向二级缓存中添加的缓存数据。TransactionalCacheManager:管理CachingExecutor使用的二级缓存对象。

 public class CachingExecutor implements Executor {

  //底层使用的Executor,通常时SimpleExecutor
  private final Executor delegate;
  //事务缓存管理器,Map<Cache, TransactionalCache> transactionalCaches属性中,key为二级缓存。
  private final TransactionalCacheManager tcm = new TransactionalCacheManager();  




  @Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
    //Cache为二级缓存
    //在同一个namespace下,不同的MappedStatement 共享同一个cache。
    //创建MappedStatement时,会引入当前namespace下的cache。
    //Configuration中的Map<String, Cache> caches记录所有的二级缓存信息,key为namespace。
    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.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    }
    return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }
}



  

需要配置三步:

1.application.yml中配置cacheEnable:true,这个是二级缓存的总开关,默认true。

2.xml中配置<cache />节点,使用到了Cache的实现类,装饰者模式。

3.<select>节点使用useCache属性,默认true。

mybatis的二级缓存不建议使用

二级缓存,使用装饰者模式包装的Cache接口,多表关联查询时会出现脏数据。mybatis的二级缓存是基于namespace的(Cache的id就是namespace值),多表查询语句所在的namspace无法感应到其他namespace中的语句对多表查询中涉及的表进行的修改,引发脏数据问题。

Executor实现类

BaseExecutor:使用了模板方法,主要提供了缓存管理和事务管理的基本功能,其他子类继承该抽象类完成数据库操作。

BatchExecutor:mybatis的批处理,批量插入/更新/删除。

ReuseExecutor:提供了Statement的重用功能,以sql作为key查找Statement对象,存在就使用,不存在就创建。

SimpleExecutor:简单的executor,常用的普通数据库操作。

CachingExecutor:使用装饰者模式,二级缓存,应用级别的缓存。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值