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

* 最底层可以配置的是数据库自带的query cache,
* mybatis的一级缓存,默认情况下都处于开启状态,只能使用自带的PerpetualCache,无法配置第三方缓存
* mybatis的二级缓存,可以配置开关状态,默认使用自带的PerpetualCache,但功能比较弱,能够配置第三方缓存,
* service层的缓存配置,结合spring,可以灵活进行选择
* 针对实际业务情况,直接缓存部分html页面,直接返回给客户端。
在测试过程中,发现mybatis的一级缓存没有起作用,失效了。经过调研,发现是由于以下原因引起的:


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


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

* 我们需要访问数据
*
spring检查到了这种需求,于是去申请一个mybatis的sqlsession(资源池),并将申请到的sqlsession与当前线程绑定,放入threadlocal里面
* sqlSessionTemplate从threadlocal获取到sqlsession,去执行查询
* 查询结束,清空threadlocal中与当前线程绑定的sqlsession,释放资源
* 我们又需要访问数据
* 返回到步骤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都是同一个,故能用上缓存了

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:ixiaoyang8@qq.com
QQ群:637538335
关注微信