在搜索地区页面时,优化数据的响应效果
原先业务逻辑为搜索所有的area表中的数据,使用sql查询找出子节点和父节点之间的关系
select t1.*, (select t2.area_name from sys_area t2 where t2.id = t1.pid) parentName from sys_area t1 where t1.deleted = 0
po和vo的设计如下,由于po与vo在数据交互的过程中会互相转化,所以需要让po和vo存在的字段一一对应,由于上级名称不存在与数据库表中,所以需要使用@TableField(exist = false)标记
//vo如下:
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(description = "地区")
public class SysAreaVO extends TreeNode<SysAreaVO> {
@Schema(description = "地区名称", required = true)
@NotBlank(message = "地区名称不能为空")
private String areaName;
@Schema(description = "创建时间")
@JsonFormat(pattern = DateUtils.DATE_TIME_PATTERN)
private Date createTime;
@Schema(description = "上级名称")
private String parentName;
}
//po如下:
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("sys_area")
public class SysAreaEntity extends BaseEntity {
/**
* 上级ID
*/
private Long pid;
/**
* 地区编号
*/
private String areaCode;
/**
* 地区名称
*/
private String areaName;
/**
* 上级名称
*/
@TableField(exist = false)
private String parentName;
}
通过service层的方法调用tree的工具类,将表中的数据由扁平结构转化为树结构,再将树结构的数据响应给前端
//数据打包
public List<SysAreaVO> getList() {
Map<String, Object> params = new HashMap<>();
// 数据权限
params.put(Constant.DATA_SCOPE, getDataScope("t1", "id"));
// 机构列表
List<SysAreaEntity> entityList = baseMapper.getList(params);
//po转vo
List<SysAreaVO> sysAreaVOS = BeanUtil.copyToList(entityList, SysAreaVO.class);
//扁平化转树
List<SysAreaVO> build = TreeUtils.build(sysAreaVOS);
return build;
}
//封装的工具类
public class TreeUtils {
/**
* 根据pid,构建树节点
*/
public static <T extends TreeNode<T>> List<T> build(List<T> treeNodes, Long pid) {
// pid不能为空
AssertUtils.isNull(pid, "pid");
List<T> treeList = new ArrayList<>();
for (T treeNode : treeNodes) {
if (pid.equals(treeNode.getPid())) {
treeList.add(findChildren(treeNodes, treeNode));
}
}
return treeList;
}
/**
* 查找子节点
*/
private static <T extends TreeNode<T>> T findChildren(List<T> treeNodes, T rootNode) {
for (T treeNode : treeNodes) {
if (rootNode.getId().equals(treeNode.getPid())) {
rootNode.getChildren().add(findChildren(treeNodes, treeNode));
}
}
return rootNode;
}
/**
* 构建树节点
*/
public static <T extends TreeNode<T>> List<T> build(List<T> treeNodes) {
List<T> result = new ArrayList<>();
// list转map
Map<Long, T> nodeMap = new LinkedHashMap<>(treeNodes.size());
for (T treeNode : treeNodes) {
nodeMap.put(treeNode.getId(), treeNode);
}
for (T node : nodeMap.values()) {
T parent = nodeMap.get(node.getPid());
if (parent != null && !(node.getId().equals(parent.getId()))) {
parent.getChildren().add(node);
continue;
}
result.add(node);
}
return result;
}
}
最后controller层接受请求,并且调用list方法,给前端响应树型的数据
@GetMapping("list")
@Operation(summary = "列表")
@PreAuthorize("hasAuthority('sys:area:list')")
public Result<List<SysAreaVO>> list() {
List<SysAreaVO> list = sysareaService.getList();
return Result.ok(list);
}
响应数据如下图所示:数据展示为一颗树,点击叶子节点不会再发送请求
但是上述业务逻辑有一个问题就是整表进行查询,在表中数据特别多的时候,首次进入页面的数据速度特别的慢
业务优化:使用懒查询解决一次性查找数据库表数据过慢的问题
对sql语句的优化
select t1.* ,(select t2.area_name from sys_area t2 where t2.id = t1.pid) as parentName from sys_area t1 where t1.deleted = 0 and t1.pid=#{pid}
vo和po修改后的代码,vo通过判断是否有子节点来进一步获取数据
//vo
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(description = "地区")
public class SysAreaVO extends TreeNode<SysAreaVO> {
@Schema(description = "地区名称", required = true)
@NotBlank(message = "地区名称不能为空")
private String areaName;
@Schema(description = "创建时间")
@JsonFormat(pattern = DateUtils.DATE_TIME_PATTERN)
private Date createTime;
@Schema(description = "上级名称")
private String parentName;
@Schema(description = "是否有子节点")
private boolean hasChildren;
}
//po
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("sys_area")
public class SysAreaEntity extends BaseEntity {
/**
* 上级ID
*/
private Long pid;
/**
* 地区编号
*/
private String areaCode;
/**
* 地区名称
*/
private String areaName;
/**
* 上级名称
*/
@TableField(exist = false)
private String parentName;
}
service层的方法,getSubcount里的MyBatis-Plus和sql中的t1.pid=#{pid}相对应
//与原先一致
public List<SysAreaVO> getList(Long pid) {
Map<String, Object> params = new HashMap<>();
params.put("pid",pid);
// 数据权限
//params.put(Constant.DATA_SCOPE, getDataScope("t1", "id"));
// 区域列表
List<SysAreaEntity> entityList = baseMapper.getList(params);
//po转vo
List<SysAreaVO> sysAreaVOS = BeanUtil.copyToList(entityList, SysAreaVO.class);
List<SysAreaVO> build = TreeUtils.build(sysAreaVOS);
return build;
}
//获取具有指定父ID的子实体数量
@Override
public Long getSubcount(Long id){
QueryWrapper<SysAreaEntity> queryWrapper = new QueryWrapper();
queryWrapper.eq("pid",id);
Long subcount=baseMapper.selectCount(queryWrapper);
return subcount;
}
controller层的方法
@RestController
@RequestMapping("sys/area")
@Tag(name = "地区管理")
@AllArgsConstructor
public class SysAreaController {
private final SysAreaService sysareaService;
@GetMapping("list")
@Operation(summary = "列表")
@PreAuthorize("hasAuthority('sys:area:list')")
public Result<List<SysAreaVO>> list(@RequestParam(value = "id",defaultValue = "86") Long id) {
List<SysAreaVO> list = sysareaService.getList(id);
list.forEach(e->{
//查询父级是否有子级
Long subcount=sysareaService.getSubcount(id);
if(subcount>0) {
//将vo的HasChildren置为true
e.setHasChildren(true);
}
//因为后面我在sql中映射了,所以不用再单独查了
//SysAreaEntity parentEntity = sysarseaService.getById(e.getPid());
//e.setParentName(parentEntity.getAreaName());
});
return Result.ok(list);
}
响应数据如下图所示:数据仅展示一级的所有叶子,点击某个叶子节点再次发送请求获得下一级的叶子