1、 涉及多张表的分页问题
我觉得有必要,专门拿出来讲讲在这次项目中获得的知识。这次的列表出现了超时问题,怎么解决的呢,因为数据很多,都是拿一条数据以需求ID为线索,去查询其他数据。首先七张表联合查询是最笨的一种方法,但查询的效率并不高。
1.1解决办法
找到所有表之间的共同点,他们都是通过一个字段把所有表联系起来的,因为搜索条件在每张表都会用到,分页的问题是一大难题,组装数据倒是很容易。有的人肯定就会说,直接把数据拼成一个list,用个工具类直接分页不就行了。这是一开始的做法,接口出现了严重超时问题。现在是找出每次搜索条件的中间点,比如根据标签名称查询列表,那么影响分页的就只涉及两到三张表。大概逻辑就是两三张表联合查询,表面上sql的搜索条件是标签名称,但实际上搜索的即是需求ID,废话少说,上代码。
service层
// 1.标签筛选
if (queryParam.getTagName() != null && !queryParam.getTagName().isEmpty()) {
log.info("标签筛选type:{}", JSON.toJSONString(queryParam.getTagName()));
Set<String> demandIds4 = tagDaoExtend.filterTags(queryParam.getTagName());
resultDemandIds = resultDemandIds.stream().filter(demandIds4::contains).collect(Collectors.toSet());
log.info("标签筛选后的type:{},demandIds4:{}", JSON.toJSONString(queryParam.getTagName()), JSON.toJSONString(demandIds4));
if (CollectionUtils.isEmpty(resultDemandIds)) {
return new Page<>(queryParam.getPage(), queryParam.getSize(), 0, productDemandDTOS);
}
}
dao层
/**
* 标签筛选
* @param tagName
* @return
*/
public Set<String> filterTags(List<String> tagName) {
return mapper.filterTags(tagName);
}
mapper层
Set<String> filterTags(@Param("tagName") List<String> tagName);
mapper.xml层
<select id="filterTags" resultType="java.lang.String">
select DISTINCT a.id
from TB_CRADLE_PRODUCT_DEMAND a
left join TB_CRADLE_TAG_RESOURCE b on a.id = b.resourceId
left join TB_CRADLE_TAG c on b.tagId = c.id and b.resourceType = c.tagType
WHERE 1 = 1
<if test="tagName !=null and tagName.size > 0">
<foreach collection="tagName" item="name" open="and c.name in (" separator="," close=")">
#{name}
</foreach>
</if>
</select>
2、根据某个字段分组,并把某种状态/类型依次排放并且按照更新时间排序
List<DocumentUserDTO> documentUserDTOS = new ArrayList<>();
Map<Integer, List<DocumentUserDTO>> collect = dtos.stream()
.sorted(Comparator.comparing(DocumentUserDTO::getUpdateTime).reversed())//更新时间倒序排列
.collect(Collectors.groupingBy(DocumentUserDTO::getStatus));//按状态分组
if (collect != null && collect.containsKey(DocumentUserStatusEnum.TO_BE_CONFIRMED.getValue())){
documentUserDTOS.addAll(collect.get(DocumentUserStatusEnum.TO_BE_CONFIRMED.getValue()));
}
if (collect != null && collect.containsKey(DocumentUserStatusEnum.ACTIVE.getValue())){
documentUserDTOS.addAll(collect.get(DocumentUserStatusEnum.ACTIVE.getValue()));
}
if (collect != null && collect.containsKey(DocumentUserStatusEnum.APPLICATION_REJECTED.getValue())){
documentUserDTOS.addAll(collect.get(DocumentUserStatusEnum.APPLICATION_REJECTED.getValue()));
}
3、代码实现搜索
数据量小的情况下:
if (CollectionUtils.isNotEmpty(queryParam.getCname())) {
long cnameCount = admin.stream().filter(e -> queryParam.getCname().contains(e)).count();
if (cnameCount == 0) {
// 当前循环的记录不满足管理员筛选条件,过滤,继续下一条
log.info("当前循环的记录不满足管理员筛选条件,过滤,继续下一条");
return;
}
}
List<String> cName = admin.stream().distinct().collect(Collectors.toList());
productDemandDTO.setCname(cName);
//文档类型筛选 and 无文件筛选
if (queryParam.getType() != null) {
if (queryParam.getType() != -1) {
long documentCount = documentDTOList.stream().filter(e -> e.getType().equals(queryParam.getType())).count();
if (documentCount == 0) {
// 当前循环的记录不满足文档类型筛选条件,过滤掉,继续下一条
log.info("当前循环的记录不满足文档类型筛选筛选条件,过滤,继续下一条");
return;
}
} else {
if (documentDTOList.size() != 0) {
log.info("当前循环的记录不满足文档类型筛选筛选条件,过滤,继续下一条");
return;
}
}
}
List<DocumentTypeDTO> documentTypeDTOs = new ArrayList<>();
for (DocumentDTO documentDTO : documentDTOList) {
DocumentTypeDTO documentTypeDTO = new DocumentTypeDTO();
documentTypeDTO.setId(documentDTO.getId());
documentTypeDTO.setType(documentDTO.getType());
documentTypeDTOs.add(documentTypeDTO);
}
productDemandDTO.setType(documentTypeDTOs);
4、根据某个字段去重,返回值为当前类
List<ProductDemandResult> demandResultList = productDemandResultList.stream().collect(Collectors.collectingAndThen(
Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(ProductDemandResult::getId))), ArrayList::new)
);
5、更新不了的想不通就别更新
换个思路,把原来的数据查出来删除,然后插入新的关联关系
//关联标签
if (StringUtils.isNotBlank(JSON.toJSONString(param.getTags()))) {
//1.查出原有需求对应标签
List<TagResource> tags = tagResourceDaoExtend.listByResourse(String.valueOf(productDemand.getId()), TagResourceType.DOCUMENT.getValue());
//2.删除
for (TagResource tag : tags) {
TagResourceExample tagResourceExample = new TagResourceExample();
tagResourceExample.createCriteria()
.andTagIdEqualTo(tag.getTagId())
.andResourceTypeEqualTo(TagResourceType.DOCUMENT.getValue())
.andResourceIdEqualTo(String.valueOf(productDemand.getId()));
tagResourceMapper.deleteByExample(tagResourceExample);
}
//3.插入新的关联关系
TagResourceSaveParam tagResourceSaveParam = new TagResourceSaveParam();
if (param.getTags() != null) {
for (Long tagId : param.getTags()) {
tagResourceSaveParam.setResourceId(String.valueOf(productDemand.getId()));
tagResourceSaveParam.setResourceType(TagResourceType.DOCUMENT.getValue());
tagResourceSaveParam.setOperator(user.getEmail());
tagResourceSaveParam.setTagId(tagId);
tagService.create(tagResourceSaveParam);
}
}
}
6、代码排序的实现方法
//根据时间和需求名称asc/desc
if (org.springframework.util.StringUtils.hasText(queryParam.getSortBy()) &&
org.springframework.util.StringUtils.hasText(queryParam.getDirection())) {
if ("updateTime".equals(queryParam.getSortBy())) {
productDemandDTOS.sort((o1, o2) -> {
if ("asc".equalsIgnoreCase(queryParam.getDirection())) {
return o1.getUpdateTime().compareTo(o2.getUpdateTime());
}
return o2.getUpdateTime().compareTo(o1.getUpdateTime());
});
} else if ("demandName".equals(queryParam.getSortBy())) {
productDemandDTOS.sort((o1, o2) -> {
if ("asc".equalsIgnoreCase(queryParam.getDirection())) {
return o1.getDemandName().toLowerCase().compareTo(o2.getDemandName().toLowerCase());
}
return o2.getDemandName().toLowerCase().compareTo(o1.getDemandName().toLowerCase());
});
}
}
7、封装数据
封装数据时,注意new对象的位置
//需求详情同步Jira排期计划相关
List<JiraUser> managers = new ArrayList<>();
if (jiraIssue != null) {
SchedulingPlanDTO schedulingPlanDTO = new SchedulingPlanDTO();
//计划提测时间
if (jiraIssue.getPlanTestTime() != null) {
schedulingPlanDTO.setPlanTestTime(jiraIssue.getPlanTestTime());
log.info("计划提测时间:{}", jiraIssue.getPlanTestTime());
}
//计划上线时间
if (jiraIssue.getPlanOnlineTime() != null) {
schedulingPlanDTO.setPlanOnlineTime(jiraIssue.getPlanOnlineTime());
log.info("计划上线时间:{}", jiraIssue.getPlanOnlineTime());
}
//1.先拿到所有的子任务
List<JiraIssueTask> jiraIssueTaskList = JSON.parseObject(jiraIssue.getSubtasks(), new TypeReference<List<JiraIssueTask>>() {
}.getType());
log.info("需求详情:{},对应的子任务列表:{}", id, jiraIssueTaskList);
//2.遍历查询每个子任务对应的任务类型、对应的经办人、每个子任务的预估时间
for (JiraIssueTask jiraIssueTask : jiraIssueTaskList) {
if (jiraIssueTask != null) {
for (int i = 0;i<jiraIssueTaskList.size() + 1;i++){
log.info("循环第:{}次,当前循环的Jira:{}",i,jiraIssueTask.getKey());
}
JiraDTO issue = jiraIssueService.getIssue(jiraIssueTask.getKey());
log.info("根据任务key:{},查询到的jira数据为:{}",jiraIssueTask.getKey(),issue);
//3.根据任务类型去过滤查询子任务中每个任务对应的经办人和预估时间
if (issue != null) {
if (StringUtils.isNotBlank(issue.getTasksType())) {
String tasksType = issue.getTasksType();
log.info("任务类型为:{}时",issue.getTasksType());
JiraUser jiraUser = new JiraUser();
//取得是jira对应的Id
jiraUser.setId(issue.getId());
//任务类型
if (tasksType != null) {
if (tasksType.equals(TaskTypeEnum.PRODUCT_TASK.getDesc()) ||
tasksType.equals(TaskTypeEnum.RESEARCH_TASK.getDesc()) ||
tasksType.equals(TaskTypeEnum.INTERACTION_TASK.getDesc())) {
JiraUserTask jiraUserTask = new JiraUserTask();
//产品经理类型
List<JiraIssueTask> product = new ArrayList<>();
jiraUser.setTaskType(RoleType.PRODUCTER.getDesc());
log.info("当前jira任务对应的人员:{}",RoleType.PRODUCTER.getDesc());
//经办人
List<String> assignee = new ArrayList<>();
assignee.add(issue.getAssignee());
jiraUserTask.setAssignee(assignee);
//筛选出类型为产品的任务
product.add(jiraIssueTask);
log.info("产品经理对应的任务:{}",product);
//任务
jiraUserTask.setSubtasks(product);
//共几天
jiraUserTask.setWorkDay(issue.getTotalTime()/(3600*8));
log.info("产品任务共:{}天",issue.getTotalTime());
log.info("任务相关数据:{}",jiraUserTask);
jiraUser.setJiraUserTask(jiraUserTask);
managers.add(jiraUser);
log.info("产品经理相关数据:{}",managers);
}
if (tasksType.equals(TaskTypeEnum.DEVELOP_TASK.getDesc())) {
JiraUserTask jiraUserTask = new JiraUserTask();
//开发人员类型
List<JiraIssueTask> develop = new ArrayList<>();
jiraUser.setTaskType(RoleType.DEVELOPER.getDesc());
log.info("当前jira任务对应的人员:{}",RoleType.DEVELOPER.getDesc());
//经办人
List<String> assignee = new ArrayList<>();
assignee.add(issue.getAssignee());
jiraUserTask.setAssignee(assignee);
//开发任务
develop.add(jiraIssueTask);
log.info("开发人员对应的任务:{}",develop);
jiraUserTask.setSubtasks(develop);
//共几天
jiraUserTask.setWorkDay(issue.getTotalTime()/(3600*8));
log.info("开发任务共:{}天",issue.getTotalTime());
jiraUser.setJiraUserTask(jiraUserTask);
managers.add(jiraUser);
log.info("开发人员相关数据:{}",managers);
}
if (tasksType.equals(TaskTypeEnum.TEST_TASK.getDesc())) {
JiraUserTask jiraUserTask = new JiraUserTask();
//测试人员
List<JiraIssueTask> test = new ArrayList<>();
jiraUser.setTaskType(RoleType.TESTER.getDesc());
log.info("当前jira任务对应的人员:{}",RoleType.TESTER.getDesc());
//经办人
List<String> assignee = new ArrayList<>();
assignee.add(issue.getAssignee());
jiraUserTask.setAssignee(assignee);
//过滤出相应的任务
test.add(jiraIssueTask);
log.info("测试人员对应的任务:{}",test);
jiraUserTask.setSubtasks(test);
//共几天
jiraUserTask.setWorkDay(issue.getTotalTime()/(3600*8));
log.info("测试任务共:{}天",issue.getTotalTime());
jiraUser.setJiraUserTask(jiraUserTask);
managers.add(jiraUser);
log.info("测试人员相关数据:{}",managers);
}
}
}
}
}
}
schedulingPlanDTO.setManagers(managers);
log.info("排期计划相关数据:{}",schedulingPlanDTO);
detailDTO.setSchedulingPlan(schedulingPlanDTO);
}
//关联需求
if (jiraIssue != null && jiraIssue.getIssuelinks() != null) {
List<Issuelinks> issuelinksList = JSON.parseObject(jiraIssue.getIssuelinks(), new TypeReference<List<Issuelinks>>() {
}.getType());
List<JiraIssueLinks> jiraIssueLinksList = new ArrayList<>();
if (CollectionUtils.isNotEmpty(issuelinksList)) {
for (Issuelinks issuelinks : issuelinksList) {
JiraIssueLinks jiraIssueLinks = new JiraIssueLinks();
jiraIssueLinks.setId(issuelinks.getId());
jiraIssueLinks.setKey(issuelinks.getOutwardIssue().getKey());
jiraIssueLinksList.add(jiraIssueLinks);
detailDTO.setDemandLinks(jiraIssueLinksList);
}
}
}
8、介绍一种String直接转自定义对象的方法,超级nice
想要转换的对象 对象名称= JSON.parseObject(字符串, new TypeReference<想要转换的对象>() {
}.getType());
例子:
List<Issuelinks> issuelinksList = JSON.parseObject(jiraIssue.getIssuelinks(), new TypeReference<List<Issuelinks>>() {
}.getType());
9、BigDecimal小数保留用法
jiraUserTask.setWorkDay(BigDecimal.valueOf(((double)issue.getTotalTime()/(3600*8))).setScale(1, RoundingMode.HALF_UP));