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级别 ...
随机推荐
- Learning ROS: Aboat URDF (Unified Robot Description Format)
Building a Visual Robot Model with URDF from Scratch roscore &# With $(find urdf_tutorial), this ...
- 三.Go微服务--令牌桶实现原理
1. 前言 在上一篇文章 Go微服务: 令牌桶 当中简单的介绍了令牌桶实现的原理,然后利用 /x/time/rate 这个库 10 行代码写了一个基于 ip 的 gin 限流中间件,那这个功能是怎么实 ...
- Android Jetpack基本架构之ViewModel+LiveData+DataBinding入门
前提:导入所有依赖,开启DataBinding app的build.gradle android { defaultConfig { ... dataBinding { enabled true } ...
- client-go实战之二:RESTClient
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
- 论文解读(BYOL)《Bootstrap Your Own Latent A New Approach to Self-Supervised Learning》
论文标题:Bootstrap Your Own Latent A New Approach to Self-Supervised Learning 论文方向:图像领域 论文来源:NIPS2020 论文 ...
- [NOIP2015 普及组] 扫雷游戏
[NOIP2015 普及组] 扫雷游戏 难度:入门 题目描述 扫雷游戏是一款十分经典的单机小游戏.在nn行mm列的雷区中有一些格子含有地雷(称之为地雷格),其他格子不含地雷(称之为非地雷格).玩家翻开 ...
- 关于PHP的方法参数类型约束
在之前的文章PHP方法参数的那点事儿中,我们讲过关于PHP方法参数的一些小技巧.今天,我们带来的是更加深入的研究一下PHP中方法的参数类型. 在PHP5之后,PHP正式引入了方法参数类型约束.也就是如 ...
- css3 animate转圈360旋转
.logo{ width:20px; height: 20px; background: red; -webkit-animation:haha1 .8s linear infinite; anima ...
- mysql 基础配置经验
创建库: 排序:utf8_unicode_ci和utf8_general_ci对中.英文来说没有实质的差别.utf8_general_ci校对速度快,但准确度稍差. 普遍的意思utf8_unicode ...
- 解决IE浏览器 点击子元素重复调用执行 mouseover 与mouseout兼容性问题
将函数配对换为下面2个就可以解决兼容性问题. mouseenter() mouseleave(0