上一篇 <<<sqlSession.selectOne底层实现原理
下一篇 >>>Mybatis二级缓存知识汇总
1.mybatis一级缓存底层是如何实现的
一级缓存使用了PerpetualCache,内置hashmap,以namespace和sql语句以及参数等信息作为key存储的。该缓存是本地缓存,存放在内存中,session关闭后自动消失,不共享。
2.PerpetualCache的作用
PerpetualCache是我们的一级缓存,以参数和sql语句等作为key,存储在本地内存中,当有增删改和session关闭的时候都会清空。
能减少数据库的请求,加快访问速度。
3.mybaytis一级缓存存在哪些问题
a、线程不安全
b、集群的时候会产生脏读
4.如何禁止Mybatis一级缓存
方案1 在sql语句上 随机生成 不同的参数 存在缺点:map集合可能爆 内存溢出的问题
方案2 开启二级缓存
方案3 使用sqlSession强制清除缓存
方案4 创建新的sqlSession连接。
5.一级缓存何时清除?
1.提交事务的时候
sqlSession.commit();//提交事务 把一级缓存中数据给清除掉的
@Override
public void clearLocalCache() {
if (!closed) {
localCache.clear();// 一级缓存对应的 map集合中数据 清空
localOutputParameterCache.clear();
}
2.执行 insert、delete、update语句的时候
3.事务注解不存在的情况下
a.在未开启事务的情况之下,每次查询spring都会关闭旧的sqlSession而创建新的sqlSession,因此此时的一级缓存是没有启作用的
b.在开启事务的情况之下,spring模板使用threadLocal获取当前资源绑定同一个sqlSession,因此此时一级缓存是有效的
@GetMapping("/getByUserName")
@Transactional
public List<UserEntity> getByUserName(String name) {
List<UserEntity> userList1 = userMapper.getByUserName(name);// 查询db
List<UserEntity> userList2 = userMapper.getByUserName(name);// 走一级缓存,若没有注解则不会走一级缓存
return userList2;
}
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 {
// 使用反射机制调用SqlSession中方法
Object result = method.invoke(sqlSession, args);
// 如果没有开启事务的情况下
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
// 清除一级缓存
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 {
// 判断如果sqlSession != null
if (sqlSession != null) {
// 关闭closeSqlSession
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
// 判断如果当前线程有开启事务的情况下
if ((holder != null) && (holder.getSqlSession() == session)) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Releasing transactional SqlSession [" + session + "]");
}
// 没有关闭sqlsession【这里用到了threadlocal】
holder.released();
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Closing non transactional SqlSession [" + session + "]");
}
//没有开启事务情况下 关闭session
session.close();
}
6.一级缓存可以关闭吗?
默认是不可以关闭的,但源码中有判断配置项,若存在配置,则有先存储后删除的操作
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
默认是SESSION,当高CPU时可加此配置解决
<setting name="localCacheScope" value="STATEMENT"/>
推荐阅读:
<<<Mybatis的整体执行原理图解
<<<SqlSessionFactory的创建过程原理
<<<SqlSession的创建过程
<<<sqlSession如何获得具体的Mapper接口信息
<<<userMapper.getUser(1);底层实现原理
<<<sqlSession.selectOne底层实现原理
<<<Mybatis二级缓存知识汇总
<<<Springboot整合Mybatis二级缓存
<<<Mybatis常见面试题
<<<Mybatis拦截器原理及实例