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

  1. 最底层可以配置的是数据库自带的query cache,
  2. mybatis的一级缓存,默认情况下都处于开启状态,只能使用自带的PerpetualCache,无法配置第三方缓存
  3. mybatis的二级缓存,可以配置开关状态,默认使用自带的PerpetualCache,但功能比较弱,能够配置第三方缓存,
  4. service层的缓存配置,结合spring,可以灵活进行选择
  5. 针对实际业务情况,直接缓存部分html页面,直接返回给客户端。

在测试过程中,发现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. 返回到步骤b

通过以上步骤后发现,同一线程里面两次查询同一数据所使用的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都是同一个,故能用上缓存了

spring整合mybatis后,mybatis一级缓存失效的原因的更多相关文章

  1. spring管理hibernate,mybatis,一级缓存失效原因

    mybatis缓存:一级缓存和二级缓存 hibernate缓存:一级缓存和二级缓存 关于缓存: 缓存是介于物理数据源与应用程序之间,是对数据库中的数据复制一份临时放在内存中的容器, 其作用是为了减少应 ...

  2. MyBatis之一级缓存及其一级缓存失效

    定义: 一级缓存:本地缓存:与数据库同一次会话(sqlSession)期间查询到的数据会放在本地缓存中,如果以后要获取相同的数据直接从缓存中获取,不会再次向数据库查询数据一个SqlSession拥有一 ...

  3. MyBatis 延迟加载,一级缓存,二级缓存设置

    什么是延迟加载 resultMap中的association和collection标签具有延迟加载的功能. 延迟加载的意思是说,在关联查询时,利用延迟加载,先加载主信息.使用关联信息时再去加载关联信息 ...

  4. 【MyBatis学习12】MyBatis中的一级缓存

    缓存的作用是减轻数据库的压力,提高数据库的性能的.mybatis中提供了一级缓存和二级缓存,先来看一下两个缓存的示意图:   从图中可以看出: 一级缓存是SqlSession级别的缓存.在操作数据库时 ...

  5. [Java] Spring boot2 整合 Thymeleaf 后 去除模板缓存

    Spring boot2 整合 Thymeleaf 后 去除模板缓存 网上好多文章只是简单粗暴的说,在 application.properties  做如下配置即可: #Thymeleaf cach ...

  6. 阶段3 1.Mybatis_11.Mybatis的缓存_6 Mybatis中的一级缓存

    Mybatis中的一级缓存和二级缓存         一级缓存:             它指的是Mybatis中SqlSession对象的缓存.             当我们执行查询之后,查询的结 ...

  7. Mybatis进阶使用-一级缓存与二级缓存

    简介 缓存是一般的ORM 框架都会提供的功能,目的就是提升查询的效率和减少数据库的压力.跟Hibernate 一样,MyBatis 也有一级缓存和二级缓存,并且预留了集成第三方缓存的接口. 一级缓存 ...

  8. 讨论Spring整合Mybatis时一级缓存失效得问题

    问题 1.学习测试时发现了一级缓存并没有生效,先看案例: setting配置: <settings> <!-- (1) 提供可控制Mybatis框架运行行为的属性信息 --> ...

  9. 深入理解MyBatis中的一级缓存与二级缓存

    http://blog.csdn.net/weixin_36380516/article/details/73194758   先说缓存,合理使用缓存是优化中最常见的,将从数据库中查询出来的数据放入缓 ...

随机推荐

  1. MySQL --13 主从复制

    目录 一.主从复制简介 二.主从复制原理 三.主从复制搭建实战 四.主从复制基本故障处理 五.延时从库 企业案例: 模拟环境 一.主从复制简介 复制是 MySQL 的一项功能,允许服务器将更改从一个实 ...

  2. Vue实例与组件的关系

    所有的 Vue 组件都是 Vue 实例,可以看成Vue组件就是Vue实例的扩展. <div id="app"> <child></child> ...

  3. 爬虫技术:scrapy 知识点一

    ---恢复内容开始--- 1.scrapy框架 每一步的解释: step1:引擎从爬虫器获取要爬行的初始请求. step2:引擎在调度程序中调度请求,引擎把这个初始请求传递给调度器,并向调度器索要下一 ...

  4. 推荐Html Table和Markown互转的网站Table Convert Online

    网站名称:https://tableconvert.com/ 进入网站可以看到可以Table 转为Markdown.JSON.XML.SQL 多种格式 Table(4×5)定义Table的行数和列数: ...

  5. Delphi Win API 函数 MulDiv

    Delphi Win API 函数 MulDiv 原型:function MulDiv(nNumber, nNumerator, nDenominator: Integer): Integer; st ...

  6. 每天一个linux命令:more(13)

    more more命令是一个基于vi编辑器文本过滤器,它以全屏幕的方式按页显示文本文件的内容,支持vi中的关键字定位操作.more名单中内置了若干快捷键,常用的有H(获得帮助信息),Enter(向下翻 ...

  7. 【UNR #2】黎明前的巧克力 解题报告

    [UNR #2]黎明前的巧克力 首先可以发现,等价于求 xor 和为 \(0\) 的集合个数,每个集合的划分方案数为 \(2^{|S|}\) ,其中 \(|S|\) 为集合的大小 然后可以得到一个朴素 ...

  8. paper 150:GCC--GNU Compiler Collection(GNU编译器套件)

    gcc命令 编程开发            gcc命令使用GNU推出的基于C/C++的编译器,是开放源代码领域应用最广泛的编译器,具有功能强大,编译代码支持性能优化等特点.现在很多程序员都应用GCC, ...

  9. window使用

    运行命令 regedit #注册表编译器 firewall.cpl #打开防火墙配置 powershell #打开powershell control #打开控制面版 cnpa.cpl #打开网络设置 ...

  10. jenkins构建后操作archive the artfacts的用法

    参考: https://blog.csdn.net/liqiangeastsun/article/details/79062806 Jenkins构建完成存档 Archive the artifact ...