list转为树,树拉平
- 业务需求
- oracle实现树结构
- 1、**Controller.java层** :前端调此处请求
- 2、**service层:** 逻辑结构 (zbjcpjService.java),重点:this.entityMapper.queryZbjcpjTree接口
- 3、**mapper层**:sql语句(数据库交互)(LwZbjcpjMapper.java---接口 )
- 4、**pjpc.md文件** 某业务相关sql语句
- pgsql实现树结构
- 1、**Controller.java层**
- 2、**service层**
- 3、**mapper层**无改变,接口名不修改
- 4、**pjpc.md文件**
- 树其他链接
业务需求
数据库原本为oracle后转为postgresql(后续简称为pgsql),原本使用start with …connect by prior在pgsql中无法使用,转为RECURSIVE但无法达到前者父子结构排序结果集(需求:查询所有子节点),sql语句转为java代码实现。
需求:打分:小项可打分,小项的父自动计算分数,前端要显示扁平list
界面如下:
开发语言:
springboot+beetsql+vue
oracle实现树结构
1、Controller.java层 :前端调此处请求
/**
* 获取监测指标树形
*/
@RequestMapping(path = "${mda.model.name}/standard/entity/pjzbtx/lwPjdx/getZbjcpjTreeList", method = RequestMethod.POST)
public IPage<LwZbjcpj> getZbjcpjTreeList(@RequestBody PageDTO<LwZbjcpj, LwZbjcpjQuery> request) {
return zbjcpjService.getZbjcpjTreeList(request);
}
2、service层: 逻辑结构 (zbjcpjService.java),重点:this.entityMapper.queryZbjcpjTree接口
public IPage<LwZbjcpj> getZbjcpjTreeList(PageDTO<LwZbjcpj, LwZbjcpjQuery> request) {
Page<LwZbjcpj> page = new Page<>();
page.setCurrent(request.getPageNo());
page.setSize(request.getPageSize());
String pjpcdxId = request.getSimpleCondition().getPjpcdxId();
List<LwZbjcpj> zbjcpjTreeList = this.entityMapper.queryZbjcpjTree(pjpcdxId);
page.setTotal(zbjcpjTreeList.size());
page.setRecords(zbjcpjTreeList);
return page;
}
3、mapper层:sql语句(数据库交互)(LwZbjcpjMapper.java—接口 )
这里的queryZbjcpjTree接口对应pjpc.md中写的sql语句命名为queryZbjcpjTree
@Mapper
@SqlResource("pjpc")
public interface LwZbjcpjMapper extends BaseExtMapper<LwZbjcpj> {
public List<LwZbjcpj> queryZbjcpjTree(@Param("pjpcdxId") String pjpcdxId);
public List<String> queryZbjcpjParentId(@Param("pjpcdxId") String pjpcdxId, @Param("id") String id);
}
4、pjpc.md文件 某业务相关sql语句
queryZbjcpjTree
===
*根据批次对象id查所有子节点
with query as
(select zbjc.id,
zbjc.pjzb_id,
zbjc.pjpcdx_id,
mxzb.zbmc,
mxzb.sjzb,
mxzb.zbcx,
zbjc.pjfs,
zbjc.xzfs,
zbjc.zpfs
from lw_zbjcpj zbjc
left join lw_pjmxzb mxzb
on zbjc.pjzb_id = mxzb.id
where zbjc.pjpcdx_id = #{pjpcdxId})
select t.*, level,CONNECT_BY_ISLEAF AS isleaf
from query t
start with sjzb = '0'
connect by prior pjzb_id = sjzb
queryZbjcpjParentId
===
*根据批次对象id和id查所有父节点id
with query as
(select zbjc.id,
zbjc.pjzb_id,
zbjc.pjpcdx_id,
mxzb.zbmc,
mxzb.sjzb,
mxzb.zbcx,
zbjc.pjfs,
zbjc.xzfs,
zbjc.zpfs
from lw_zbjcpj zbjc
left join lw_pjmxzb mxzb
on zbjc.pjzb_id = mxzb.id
where zbjc.pjpcdx_id = #{pjpcdxId})
select id
from query
start with id = #{id}
connect by prior sjzb = pjzb_id
pgsql实现树结构
构造树节点类:尤其重要,树结构,关键属性:
private String id;
private String pid;
private String name;
private List<TreeNode> children;
@Data
@EqualsAndHashCode(callSuper = false)
public class ZbjcpjTreeNode {
private String id;
private String pjzbId; // 子
private String sjzb; // 父
private String zbmc;
private String pjfs;
private String xzfs;
private String zpfs;
private int level;
private int isleaf;
List<ZbjcpjTreeNode> children;
}
1、Controller.java层
/**
* 获取监测指标树形
*/
@RequestMapping(path = "${mda.model.name}/standard/entity/pjzbtx/lwPjdx/getZbjcpjTreeList", method = RequestMethod.POST)
public IPage<ZbjcpjTreeNode> getZbjcpjTreeList(@RequestBody PageDTO<LwZbjcpj, LwZbjcpjQuery> request) {
return zbjcpjService.getZbjcpjTreeList(request);
}
2、service层
解析:getTreeNode
把实体类转为有children的树结构;this.entityMapper.queryZbjcpjTree
pgsql查回的具有父子关系无树形的结果集;接下来遍历结果集转为树结构(含children的list);先序遍历树即可得到扁平数据列表
treeToList
树转扁平list, flattenTree
先序递归,有子则继续遍历
/**
* 转为树节点
* @param lwZbjcpj
* @return
*/
private ZbjcpjTreeNode getTreeNode(LwZbjcpj lwZbjcpj) {
ZbjcpjTreeNode treeNode = new ZbjcpjTreeNode();
try {
BeanUtils.copyProperties(treeNode, lwZbjcpj);
} catch (IllegalAccessException | InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return treeNode;
}
/**
* 根据pjpcdxId查所有子节点
* @param request
* @return
*/
public IPage<ZbjcpjTreeNode> getZbjcpjTreeList(PageDTO<LwZbjcpj, LwZbjcpjQuery> request) {
Page<ZbjcpjTreeNode> page = new Page<>();
page.setCurrent(request.getPageNo());
page.setSize(request.getPageSize());
String pjpcdxId = request.getSimpleCondition().getPjpcdxId();
List<LwZbjcpj> zbjcpjTreeList = this.entityMapper.queryZbjcpjTree(pjpcdxId);
// 存储 子元素
Map<String, ZbjcpjTreeNode> childMap = new LinkedHashMap<>();
for (LwZbjcpj zbjcpj : zbjcpjTreeList) {
ZbjcpjTreeNode zbjcNode = this.getTreeNode(zbjcpj);
childMap.put(zbjcpj.getPjzbId(), zbjcNode);
}
// 树结构
List<ZbjcpjTreeNode> tree = new ArrayList<ZbjcpjTreeNode>();
for (String zbid: childMap.keySet()) {
ZbjcpjTreeNode treeNode = childMap.get(zbid);
if (treeNode.getSjzb() == null || "0".equals(treeNode.getSjzb())) { // 根节点
tree.add(treeNode);
} else { // 子节点
ZbjcpjTreeNode parentNode = childMap.get(treeNode.getSjzb());
if (parentNode.getChildren()==null) {
parentNode.setChildren(new ArrayList<>());
}
parentNode.getChildren().add(treeNode);
}
}
// 将树形结构转换为列表
List<ZbjcpjTreeNode> flattenedList = treeToList(tree);
page.setTotal(flattenedList.size());
page.setRecords(flattenedList);
return page;
}
private List<ZbjcpjTreeNode> treeToList(List<ZbjcpjTreeNode> tree) {
List<ZbjcpjTreeNode> flattenedList = new ArrayList<ZbjcpjTreeNode>();
for (ZbjcpjTreeNode node:tree) {
// 拉平树
flattenTree(node, flattenedList);
}
return flattenedList;
}
// 递归先序遍历转为扁平数据结构
private void flattenTree(ZbjcpjTreeNode node, List<ZbjcpjTreeNode> flattenedList) {
flattenedList.add(node);
if (node.getChildren() != null) {
for(ZbjcpjTreeNode child:node.getChildren()) {
flattenTree(child, flattenedList);
}
}
}
3、mapper层无改变,接口名不修改
4、pjpc.md文件
queryZbjcpjTree
与queryZbjcpjParentId
两个pgsql语句
queryZbjcpjTree
===
*根据批次对象id查所有子节点
WITH RECURSIVE query AS (
SELECT zbjc.id,
zbjc.pjzb_id,
zbjc.pjpcdx_id,
mxzb.zbmc,
mxzb.sjzb,
mxzb.zbcx,
zbjc.pjfs,
zbjc.xzfs,
zbjc.zpfs
FROM lw_zbjcpj zbjc
LEFT JOIN lw_pjmxzb mxzb ON zbjc.pjzb_id = mxzb.id
WHERE zbjc.pjpcdx_id = #{pjpcdxId}
),
tree AS (
SELECT t.*, 1 AS level
FROM query t
WHERE sjzb = '0'
UNION ALL
SELECT q.*, tree.level + 1
FROM query q
JOIN tree ON q.sjzb = tree.pjzb_id
)
SELECT tree.*,
CASE WHEN EXISTS (SELECT 1 FROM query q WHERE q.sjzb = tree.pjzb_id) THEN 0 ELSE 1 END AS isleaf
FROM tree;
queryZbjcpjParentId
===
*根据批次对象id和id查所有父节点id
WITH RECURSIVE query AS (
SELECT zbjc.id,
zbjc.pjzb_id,
zbjc.pjpcdx_id,
mxzb.zbmc,
mxzb.sjzb,
mxzb.zbcx,
zbjc.pjfs,
zbjc.xzfs,
zbjc.zpfs
FROM lw_zbjcpj zbjc
LEFT JOIN lw_pjmxzb mxzb ON zbjc.pjzb_id = mxzb.id
WHERE zbjc.pjpcdx_id = #{pjpcdxId}
),
tree AS (
SELECT id, pjzb_id, sjzb
FROM query
WHERE id = #{id}
UNION ALL
SELECT q.id, q.pjzb_id, q.sjzb
FROM query q
JOIN tree ON q.pjzb_id = tree.sjzb
)
SELECT id FROM tree;
queryZbjcpjTree的结果集为:level为1在一起,level为2气候,依次往后排,无法达到oracle使用start with…connect by prior就能达到排序的效果,因此使用java代码实现扁平数据树形展示
总结:俩要点,第一:结果集转为带children的树结构;第二:将树转为list扁平结构;算法使用到递归与迭代。
树其他链接
前后端构建侧边栏多级动态导航栏—树形结构