Mybatis一级缓存的锅
问题背景
项目开发中有一个树形数据结构,不像经典组织结构树、菜单级别树,我们这个树形结构是用户后期手动建立起来的关系。因此数据库表结构为两张表:数据记录表、记录关系表,通过业务规则限制,形成的树形结构像下面这样:
特殊之处就是树结构节点是有重复的
不复制重复节点

复制重复节点

项目要求前端展示、导出时使用复制重复节点的方式。开搞吧
Mybatis树结构查询
树结构查询,在mysql下当然是使用Mybatis框架提供的递归查询了。
- xml配置文件
<resultMap type="(...).OKRAlignTreeNode" id="TreeNodeResult">
<result property="id" column="objective_id" />
<result property="content" column="content" />
<result property="theOrder" column="the_order" />
<collection property="children" select="getChildren" column="objective_id" ofType="(...).OKRAlignTreeNode"/>
</resultMap>
<select id="getTree" parameterType="Map" resultMap="TreeNodeResult">
select
objective_id,content,the_order
from okr_objective oo
where oo.objective_id = #{id}
order by the_order
</select>
<select id="getChildren" resultMap="TreeNodeResult">
select objective_id,content,the_order
from
(select objective_id from okr_aline where parent_ids = #{objective_id} ) a
left join okr_objective oo on a.objective_id = oo.objective_id
order by b.the_order
</select>
- mapper文件
public interface OKRAlignExportMapper {
TreeNode getTree(Long objectiveId);
}
- 查询结果

树结构导出到Excel
关于树形结构数据导出,我参考这篇博客,并针对OKR的特点做了修改。
OKR对齐视图数据结构的特点是:
- 1.以本人的目标为中心,向左右两侧发散。
- 2.左侧是自己对齐的目标,以及对齐目标再次对齐的目标,递归到顶。
- 3.右侧是向自己对齐的目标,递归到底。
关于OKR对齐视图这种数据结构的导出,我们下篇博客会把完整的代码放上来,并分析一下。这里说一下导出这种树形结构数据的主要步骤:
- 1.计算每条数据的行列坐标,这里采用递归的算法,最终可以计算出父级节点需要合并的行数,以及Excel文件的最大列数。
- 2.根据行列坐标递归输出每条数据的值到Excel单元格。
Mybatis一级缓存导致的问题
首先我们来了解一下Mybatis一级缓存:
Mybatis对缓存提供支持,但是在没有配置的默认情况下,它只开启一级缓存,一级缓存只是相对于同一个SqlSession而言。所以在参数和SQL完全一样的情况下,我们使用同一个SqlSession对象调用一个Mapper方法,往往只执行一次SQL,因为使用SelSession第一次查询后,MyBatis会将其放在缓存中,以后再查询的时候,如果没有声明需要刷新,并且缓存没有超时的情况下,SqlSession都会取出当前缓存的数据,而不会再次发送SQL到数据库。
由于Mybatis的缓存机制,导致在出现重复的叶子节点时,虽然树结构正常构建,但是指向的是同一个java对象。因为是使用的Mybatis的递归查询,因此确认整个查询在一个SqlSession中执行完成,肯定是一级缓存导致的。这样会造成的后果,就是无法设置重复叶子节点的正确位置,因为指向同一个java对象,后遍历到的节点设置会覆盖前面的节点设置。
解决方案
既然确定是一级缓存导致的,那关闭或者清除一级缓存就行了吧。因为是框架的递归查询,因此无法
调用SqlSession的修改、添加、删除、commit(),close等清空一级缓存。那怎么办呢,笨办法了:
既然树的结构关系时正确的,只是重复节点指向了同一个java对象,那就遍历重建对象吧
/**
* 深度拷贝树结构
* @param node
* @return
*/
private static OKRAlignTreeNode deepCopyTree(OKRAlignTreeNode node){
OKRAlignTreeNode newNode = node.clone();
List<OKRAlignTreeNode> children = node.getChildren();
if(children!=null&&children.size()>0){
List<OKRAlignTreeNode> newChildren = new LinkedList<>();
for (OKRAlignTreeNode child:children){
if(child!=null){
OKRAlignTreeNode newChild = deepCopyTree(child);
newChildren.add(newChild);
}
}
newNode.setChildren(newChildren);
}
return newNode;
}
Mybatis一级缓存的锅的更多相关文章
- MyBatis 一级缓存与二级缓存
MyBatis一级缓存 MyBatis一级缓存默认开启,一级缓存为Session级别的缓存,在执行以下操作时一级缓存会清空 1.执行session.clearCache(); 2.执行CUD操作 3. ...
- MyBatis一级缓存引起的无穷递归
MyBatis一级缓存引起的无穷递归 引言: 最近在项目中参与了一个领取优惠劵的活动,当多个用户领取同一张优惠劵的时候,使用了数据库锁控制并发,起初的设想是:如果多个人同时领一张劵,第一个到达的人领取 ...
- mybatis一级缓存详解
mybatis缓存分为一级缓存,二级缓存和自定义缓存.本文重点讲解一级缓存 一:前言 在介绍缓存之前,先了解下mybatis的几个核心概念: * SqlSession:代表和数据库的一次会话,向用户提 ...
- 0065 MyBatis一级缓存与二级缓存
数据库中数据虽多,但访问频率却不同,有的数据1s内就会有多次访问,而有些数据几天都没人查询,这时候就可以将访问频率高的数据放到缓存中,就不用去数据库里取了,提高了效率还节约了数据库资源 MyBatis ...
- MyBatis 一级缓存避坑
MyBatis 一级缓存(MyBaits 称其为 Local Cache)无法关闭,但是有两种级别可选: package org.apache.ibatis.session; /** * @autho ...
- 关于mybatis 一级缓存引发的问题
场景: 由于在一个方法中存在多个不同业务操作 private void insertOrUpdateField(CompanyReport entity) { //计算并数据 calcReportDa ...
- MyBatis一级缓存(转载)
<深入理解mybatis原理> MyBatis的一级缓存实现详解 及使用注意事项 http://demo.netfoucs.com/luanlouis/article/details/41 ...
- MyBatis 一级缓存、二级缓存全详解(一)
目录 MyBatis 一级缓存.二级缓存全详解(一) 什么是缓存 什么是MyBatis中的缓存 MyBatis 中的一级缓存 初探一级缓存 探究一级缓存是如何失效的 一级缓存原理探究 还有其他要补充的 ...
- Mybatis一级缓存和二级缓存总结
1:mybatis一级缓存:级别是session级别的,如果是同一个线程,同一个session,同一个查询条件,则只会查询数据库一次 2:mybatis二级缓存:级别是sessionfactory级别 ...
随机推荐
- 手机端rem简单配置相关
手机端rem简单配置相关 1 <!DOCTYPE html> 2 <html xmlns="http://www.w3.org/1999/xhtml"> 3 ...
- 为开源项目 go-gin-api 增加后台任务模块
目录 任务管理界面 (WEB) 任务调度器 任务执行器 小结 推荐阅读 任务管理界面 (WEB) 支持在 WEB 界面 中对任务进行管理,例如:新增任务.编辑任务.启用/禁用任务.手动执行任务 等. ...
- Python之smtplib模块
工作中难免会出现自动发送电子邮件的需求,比如说做完自动化测试之后通过电子邮件的形式将结果反馈出来.Python中提供了标准库smtplib来解决这一问题,该模块定义了一个smtp客户端会话对象,能够将 ...
- Redis详解(三)——
redis https://www.cnblogs.com/zhangyinhua/p/14504717.html
- Configuration对象和SessionFactory会话池
一.加载核心配置文件方式 二.加载映射文件方式 三.SessionFactory相当于连接池 四.获取session会话 同一个线程中获取的session两种方法获取的是同一个session对象: 不 ...
- 三剑客之awk 逐行读取
目录: 一.awk工作原理 二.按行输出文本 三.按字段输出文本 四.通过管道,双引号调用shall命令 五.CPU使用率 六.使用awk 统计 httpd 访问日志中每个客户端IP的出现次数 一.a ...
- oracle table()函数
PL/SQL表---table()函数用法/* PL/SQL表---table()函数用法:利用table()函数,我们可以将PL/SQL返回的结果集代替table. oracle内存表在查询和报表的 ...
- 如何在win10中Java中JDK的安装和path,classpath的环境配置
1,第一步,不用说肯定是去下一个java JDK了.目前最新版本的java JDK应该是JDK 7.0,这个就自己去百度一下了,好多网站都可以找到.2,第二步就是安装JDK虚拟机了,按照它里面的提示一 ...
- python中reduce filter map lambda函数
lambda函数 python 使用 lambda 来创建匿名函数,lambda返回值是一个函数的地址,也就是函数对象. 语法:lambda [arg1 [,arg2,.....argn]]:expr ...
- Java定时任务Quartz
第一步:pom文件中添加依赖包 第二步:创建xml文件,名称为:spring-scheduler 路径如下图: 第三步:spring-scheduler配置详情 <!--创建任务--> & ...