一般在我们做后台管理的时候都需要加载一个树,当然也有更好的方法,一般后端都是直接请求一个接口然后返回一个树,树一般都是递归调用的,根据父级一层层的往下查询,然后大部人都是这么做的。
不知你们都是怎么做的反正我看到的后台管理的大部分都是怎么搞的,前面查询出父级的菜单,然后将父级所需的一些信息传到构造的递归函数中,然后接着查询下一级,这样一级级的往下查,最终构造成一个树。
前端根据这个树解析填充,但是一旦这个树的数据很大的时候,查询就非常的慢,查询慢我们就得优化吧,但是sql语句已经优化的差不多了,就是要把递归查询数据库优化掉。
首先我们想到是一次性查询所有的数据将数据放入到缓存中,那就写一个List集合将所有的数据都放到集合中,但是这个数据是实时变动的,你放到List的集合中他是不变的还行,但是一变动还是查询的原来的数据就做不到实时的改变了。而且集合放的数据过多还会造成内存溢出的问题。
将这个集合放到redis集合中,每一次查询都时候都重新设置下缓存,然后再查询,虽说这样第一次查询会很慢,但是后面的查询都会很快。但是问题又来了,这个后台管理是很多用户的,每个用户的菜单树都是不一样,这就很操蛋了,你还得对不同的用户都存一个树,每次改变都得设置下redis缓存,这并发一多还不得直接卡死啊。
经过前几种的思路后我们想着查询父级的时候根据父级的id查询每个用户的菜单树放入到redis中,然后在每个用户登录的时候刷新下自己的缓存,这个即解决了每个用户不同菜单树缓存的问题又解决了第一次查询很慢的问题。
虽然第三种方法看上去不错,但是这个又做不到实时查询菜单树的问题了,想想能不能每次有用户操作的时候都更新下对应的缓存呢?于是就写一个AOP切面对每个操作了菜单的方法进行redis缓存的更新,也是可以获取到不同的用户对其更改,这样就完美的解决了。
逻辑该怎么写呢?
这是大致的操作步骤,按这个筛选是没有问题的。至于AOP就很简单了这就不贴了,就是写个AOP注解对需要的方法进行操作即可。
这里还有个问题对于redis的设置值一般都喜欢这么写
public <T> void put(String key, T obj, long timeout, TimeUnit unit) {
redisTemplate.opsForValue().set(key, JSON.toJSONString(obj),timeout,unit);
}
这个写如果转换的obj里面的对象有值是空的他会直接的去掉,避免这个问题可以这么写
public <T> void put(String key, T obj, long timeout, TimeUnit unit) {
redisTemplate.opsForValue().set(key, JSON.toJSONString(obj, SerializerFeature.WriteMapNullValue),timeout,unit);
}