1. 目录层级数据返回问题
获取到一堆数据,诸如这种,需要向前端返回带层级的数据,从数据库获取数据的方式为递归sql:
with recursive t1 as(
select * from course_category where id = '1'
union ALL
select t2.* from course_category t2 join t1 where t1.id = t2.parentid
)
select * from t1
需要向前端返回的数据为List<CourseCategoryTreeDto>
CourseCategoryTreeDto:
包含当前子节点的列表的父节点信息和一个子节点列表:
对这些数据应该进行如下操作:
@Override
public List<CourseCategoryTreeDto> queryTreeNodes(String id) {
List<CourseCategoryTreeDto> courseCategoryTreeDtos = courseCategoryMapper.selectTreeNodes(id);
//先将list转成map,key是节点的id,value是CourseCategoryTreeDto对象,目的就是方便从map中获取节点,对map进行过滤,不需要根节点1,也即.filter()
Map<String, CourseCategoryTreeDto> idDtoMap = courseCategoryTreeDtos.stream().filter(item -> !item.getId().equals(id)).collect(Collectors.toMap(CourseCategory::getId, value -> value, (key1, key2) -> key2));
//new出需要返回的list
List<CourseCategoryTreeDto> categoryList = new ArrayList<>();
//从头遍历List<CourseCategoryTreeDto>,一边遍历一边找子节点放在父节点的childrenTreeNodes属性中
courseCategoryTreeDtos.stream().filter(item -> !item.getId().equals(id)).forEach(item -> {
if(item.getParentid().equals(id)){
categoryList.add(item);
}
CourseCategoryTreeDto courseCategoryTreeDto = idDtoMap.get(item.getParentid());
if(courseCategoryTreeDto!=null){
if(courseCategoryTreeDto.getChildrenTreeNodes() == null){
courseCategoryTreeDto.setChildrenTreeNodes(new ArrayList<>());
}
courseCategoryTreeDto.getChildrenTreeNodes().add(item);
}
});
return categoryList;
}
使用stream操作,支持多级
2. 数据库多线程中的乐观锁
//抢占任务 使用数据库的锁
boolean b = mediaFileProcessService.startTask(taskId);
if (!b) {
return;
}
视频任务处理场景:通过在status字段上加上 “处理中” 情况,在开始,将该字段的status字段进行更新的处理中,这样其它线程就抢占不到锁,
SQL语句:
update media_process m set m.status='4' where (m.status='1' or m.status='3') and m.fail_count<3 and m.id=#{id}
基于数据库实现分布锁
利用数据库主键唯一性的特点,或利用数据库唯一索引、行级锁的特点,多个线程同时去更新相同的记录,谁更新成功谁就抢到锁。
3.多线程问题
//启动size个线程的线程池
ExecutorService threadPool = Executors.newFixedThreadPool(size);
//计数器
CountDownLatch countDownLatch = new CountDownLatch(size);//可以使线程阻塞
CountDownLatch可以控制在一段函数中使用多线程执行任务时,可以阻塞当前函数,减为0时,函数结束。
countDownLatch.countDown();
保底策略
//等待,给一个充裕的超时时间,防止无限等待,到达超时时间还没有处理完成则结束任务
countDownLatch.await(30, TimeUnit.MINUTES);
4.xxljob
1.任务执行器根据配置的调度中心的地址,自动注册到调度中心
2.达到任务触发条件,调度中心下发任务
3.执行器基于线程池执行任务,并把执行结果放入内存队列中、把执行日志写入日志文件中
4.执行器消费内存队列中的执行结果,主动上报给调度中心
5.当用户在调度中心查看任务日志,调度中心请求任务执行器,任务执行器读取任务日志文件并返回日志详情