一、概述

  一般来说,可以在5个方面进行缓存的设计:

    1、最底层可以配置的是数据库自带的query cache,

    2、mybatis的一级缓存,默认情况下都处于开启状态,只能使用自带的PerpetualCache,无法配置第三方缓存

    3、mybatis的二级缓存,可以配置开关状态,默认使用自带的PerpetualCache,但功能比较弱,能够配置第三方缓存,

    4、service层的缓存配置,结合spring,可以灵活进行选择

    5、针对实际业务情况,直接缓存部分html页面,直接返回给客户端。

1.1、一级缓存失效【结合spring后失效,service加上@Transactional后可以使用】

  在测试过程中,发现mybatis的一级缓存没有起作用,失效了。经过调研,发现是由于以下原因引起的:

    1.mybatis的一级缓存生效的范围是sqlsession,是为了在sqlsession没有关闭时,业务需要重复查询相同数据使用的。一旦sqlsession关闭,则由这个sqlsession缓存的数据将会被清空。

    2.spring对mybatis的sqlsession的使用是由template控制的,sqlSessionTemplate又被spring当作resource放在当前线程的上下文里(threadlocal),spring通过mybatis调用数据库的过程如下:

1、我们需要访问数据
2、spring检查到了这种需求,于是去申请一个mybatis的sqlsession(资源池),并将申请到的sqlsession与当前线程绑定,放入threadlocal里面
3、sqlSessionTemplate从threadlocal获取到sqlsession,去执行查询
4、查询结束,清空threadlocal中与当前线程绑定的sqlsession,释放资源
5、我们又需要访问数据
6、返回到步骤2

  通过以上步骤后发现,同一线程里面两次查询同一数据所使用的sqlsession是不相同的,所以,给人的印象就是结合spring后,mybatis的一级缓存失效了。

  而在spring中一般都是用sqlSessionTemplate,如下

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:configuration.xml" />
<property name="mapperLocations">
<list>
<value>classpath*:com/hejb/sqlmap/*.xml</value>
</list>
</property>
</bean>
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg ref="sqlSessionFactory" />
</bean>

在SqlSessionTemplate中执行SQL的session都是通过sqlSessionProxy来,sqlSessionProxy的生成在构造函数中赋值,如下:

this.sqlSessionProxy = (SqlSession) newProxyInstance(
    SqlSessionFactory.class.getClassLoader(),
    new Class[] { SqlSession.class },
    new SqlSessionInterceptor());

sqlSessionProxy通过JDK的动态代理方法生成的一个代理类,主要逻辑在InvocationHandler对执行的方法进行了前后拦截,主要逻辑在invoke中,包好了每次执行对sqlsesstion的创建,commit,关闭

代码如下:

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)) {
     // 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);
    }
   }
  }
 }

因为每次都进行创建,所以就用不上sqlSession的缓存了.

对于开启了事务为什么可以用上呢, 跟入getSqlSession方法

如下:

public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
  notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
  notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);
  SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
  // 首先从SqlSessionHolder里取出session
  SqlSession session = sessionHolder(executorType, holder);
  if (session != null) {
   return session;
  }
  if (LOGGER.isDebugEnabled()) {
   LOGGER.debug("Creating a new SqlSession");
  }
  session = sessionFactory.openSession(executorType);
  registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
  return session;
 }

在里面维护了个SqlSessionHolder,关联了事务与session,如果存在则直接取出,否则则新建个session,所以在有事务的里,每个session都是同一个,故能用上缓存了

发的

java-mybaits-014-数据库缓存设计【querycache、mybatis一级缓存、二级缓存】的更多相关文章

  1. Mybatis一级、二级缓存

      Mybatis一级.二级缓存   一级缓存 首先做一个测试,创建一个mapper配置文件和mapper接口,我这里用了最简单的查询来演示. <mapper namespace="c ...

  2. 170214、mybatis一级和二级缓存

    mybatis一级缓存是指在内存中开辟一块区域,用来保存用户对数据库的操作信息(sql)和数据库返回的数据,如果下一次用户再执行相同的请求, 那么直接从内存中读数数据而不是从数据库读取. 其中数据的生 ...

  3. MyBatis 一级、二级缓存

    一级 默认session就有一级缓存,session有C/U/D操作或者session.clearCache();session.commit();都会清空缓存: 二级在mapper.xml中添加&l ...

  4. Mybatis使用Redis二级缓存

    在Mybatis中允许开发者自定义自己的缓存,本文将使用Redis作为Mybatis的二级缓存.在Mybatis中定义二级缓存,需要如下配置: 1. MyBatis支持二级缓存的总开关:全局配置变量参 ...

  5. Spring + MySQL + Mybatis + Redis【二级缓存】执行流程分析

    一级缓存基于 PerpetualCache 的 HashMap 本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该Session中的所有 Cache 就 ...

  6. Mybatis自定义分布式二级缓存实现与遇到的一些问题解决方案!

    先说两句: 我们都知道Mybatis缓存分两类: 一级缓存(同一个Session会话内) & 二级缓存(基于HashMap实现的以 namespace为范围的缓存) 今天呢, 我们不谈一级缓存 ...

  7. 【mybatis源码学习】mybtias一级,二级缓存

    转载:https://www.cnblogs.com/ysocean/p/7342498.html mybatis 为我们提供了一级缓存和二级缓存,可以通过下图来理解: ①.一级缓存是SqlSessi ...

  8. Spring + MySQL + Mybatis + Redis【二级缓存】

    一.Redis环境 Redis 官网 :http://redis.io/ windows下载:https://github.com/dmajkic/redis/downloads 1.文件解压缩 2. ...

  9. 【MyBatis学习13】MyBatis中的二级缓存

    1. 二级缓存的原理 前面介绍了,mybatis中的二级缓存是mapper级别的缓存,值得注意的是,不同的mapper都有一个二级缓存,也就是说,不同的mapper之间的二级缓存是互不影响的.为了更加 ...

  10. mybatis整合redis二级缓存

    mybatis默认开启了二级缓存功能,在mybatis主配置文件中,将cacheEnabled设置成false,则会关闭二级缓存功能 <settings> <!--二级缓存默认开启, ...

随机推荐

  1. Mongodb的安装--简单快速

    由于需要在服务器安装mongodb,所以就对Mongodb的安装进行了了研究,在了解安装过程之前,先了解一下Mongodb,Mongodb是什么? 是什么? MongDB是结余关系数据库和非关系数据库 ...

  2. discuz论坛门户资讯入库接口【原创】

    最近想打造一个社区门户站点,所以写了这个入库接口,可以对接数据入库. <?php /* * Discuz x3.2 门户免登陆发布接口 * 2018-08-10 * Copyright 68xi ...

  3. CSS 相邻元素选择器

    相邻兄弟选择器(Adjacent sibling selector)可选择紧接在另一元素后的元素,且二者有相同父元素.选择相邻兄弟 如果需要选择紧接在另一个元素后的元素,而且二者有相同的父元素,可以使 ...

  4. 应该知道的linux命令

    常用命令 1.在compose Bar下可以对多个服务器同时进行操作.选择To All Sessions 2. 查看JAVA进程: ps -ef | grep java ps auxf | grep ...

  5. php自定义函数之静态变量

    如果我想知道函数被调用了多少次怎么办?在没有学习静态变量的时候,我们没有好的办法来解决. 静态变量的特点是:声明一个静态变量,第二次调用函数的时候,静态变量不会再初始化变量,会在原值的基础上读取执行. ...

  6. regedit系统注册表,msconfig系统配置

    msconfig msconfig即系统配置实用程序,是Microsoft System Configuration的缩写.是在开始菜单里运行中输入然后确认就可以找到程序开启或者禁用, 可以帮助电脑禁 ...

  7. 解决idea中maven默认jdk为1.5的问题 : IntelliJ IDEA 源值1.5已过时,将在未来所有版本中删除

    解决idea中maven默认jdk为1.5的问题 最近运行总是报警告: IntelliJ IDEA 源值1.5已过时,将在未来所有版本中删除 发现是jdk版本问题, 即使自己修改structure中的 ...

  8. CodeForeces 842d Vitya and Strange Lesson ——(带lazy标记的01字典树)

    给一个序列,每次操作对这个序列中的所有数异或一个x,问每次操作完以后整个序列的mex值. 做法是去重后构建01字典树,异或x就是对root加一个x的lazy标志,每次pushDown时如果lazy的这 ...

  9. Java 线程之间的通讯,等待唤醒机制

    public class ThreadNotifySample { public static void main(String[] args) { // Res res = new Res(); / ...

  10. 记录vsCode配置node开发环境

    环境:win10,idea:vscode 1:安装Visual Studio Code 下一步下一步.(很早之前就安装了). 2:安装node. http://nodejs.cn/download/  ...