一般来说,可以在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都是同一个,故能用上缓存了
热门工具 换一换