最近的需求中,需要在构建的树结构中增加每一级的层级level,之前使用的hutools TreeUtil.build 中需要数据库中有字段记录,才能赋值过去,但是增加字段后新增、编辑等逻辑需要改动。
# 对新增的level字段,赋值
UPDATE sys_management AS t1
JOIN (
SELECT id, parent_id, @level := IF(parent_id = 0, 1, @level + 1) AS level
FROM sys_management
ORDER BY id
) AS t2 ON t1.id = t2.id
SET t1.level = t2.level;
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
所以编写了简单的构建方法并抽取成公共方法。
/**
*
* @param dataList
* @param parentId
* @param level
* @return OrganizationTreeResult 为接收实体
*/
private static List<OrganizationTreeResult> buildTreeLevel(List<OrganizationTreeResult> dataList, int parentId, int level) {
List<OrganizationTreeResult> treeList = new ArrayList<>();
for (OrganizationTreeResult data : dataList) {
if (data.getParentId() == parentId) {
data.setLevel(level);
data.setChildren(buildTreeLevel(dataList, data.getId(), level + 1));
treeList.add(data);
}
}
return treeList;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
将上面方法中修改为公共方法,OrganizationTreeResult 修改为泛型,使用了泛型
在泛型方法中,无法直接调用对象的方法或访问对象的属性,因为泛型类型是未知的。为了解决这个问题,可以使用一个接口TreeData来定义对象的通用方法,其中包括getParentId()、getId()、setLevel()和setChildren(),然后将对象实现该接口。修改后的代码如下所示:
private static <T extends TreeData> List<T> buildTreeLevel(List<T> dataList, int parentId, int level) {
List<T> treeList = new ArrayList<>();
for (T data : dataList) {
if (data.getParentId() == parentId) {
data.setLevel(level);
List<T> children = buildTreeLevel(dataList, data.getId(), level + 1);
data.setChildren(children);
treeList.add(data);
}
}
return treeList;
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
需要将OrganizationTreeResult类实现TreeData接口,并在接口中定义这些方法。这样,就可以在泛型方法中调用这些通用方法了。
进一步改进
使用了反射来设置泛型对象的属性,以适应不同类型对象的属性命名。
private static <T> List<T> buildTreeLevel(List<T> dataList, int parentId, int level, Class<T> dataType) {
List<T> treeList = new ArrayList<>();
for (T data : dataList) {
try {
Method getParentIdMethod = dataType.getMethod("getParentId");
int dataParentId = (int) getParentIdMethod.invoke(data);
if (dataParentId == parentId) {
Method setLevelMethod = dataType.getMethod("setLevel", Integer.class);
setLevelMethod.invoke(data, level);
Method getIdMethod = dataType.getMethod("getId");
int dataId = (int) getIdMethod.invoke(data);
List<T> children = buildTreeLevel(dataList, dataId, level + 1, dataType);
Method setChildrenMethod = dataType.getMethod("setChildren", Object.class);
setChildrenMethod.invoke(data, children);
treeList.add(data);
}
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
return treeList;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
在这个修改后的方法中,添加了一个额外的参数dataType,用于传递TreeData的类型。
通过使用反射,我们可以在运行时获取并调用对象的方法。
这样,就可以在调用方法时指定TreeData的具体类型,使方法适用于不同的数据类型。
/**
* 组装树结构(带层级)
*
* @param dataList
* @param parentId
* @param level
* @param dataType
* @return
*/
private static <T> List<T> buildTreeLevel(List<T> dataList, int parentId, int level, Class<T> dataType) {
List<T> treeList = new ArrayList<>();
for (T data : dataList) {
try {
Method getIdMethod = dataType.getMethod("getId");
int dataId = (int) getIdMethod.invoke(data);
if (hasChildren(dataList, dataId, dataType)) {
Method setLevelMethod = dataType.getMethod("setLevel", Integer.class);
setLevelMethod.invoke(data, level);
List<T> children = buildTreeLevel(dataList, dataId, level + 1, dataType);
Method setChildrenMethod = dataType.getMethod("setChildren", Object.class);
setChildrenMethod.invoke(data, children);
treeList.add(data);
} else {
Method setLevelMethod = dataType.getMethod("setLevel", Integer.class);
setLevelMethod.invoke(data, level);
Method setChildrenMethod = dataType.getMethod("setChildren", Object.class);
List<T> emptyTreeList = new ArrayList<>();
setChildrenMethod.invoke(data, emptyTreeList);
treeList.add(data);
}
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
return treeList;
}
/**
* 是否存在下级
*
* @param dataList
* @param parentId
* @param dataType
* @param <T>
* @return
*/
private static <T> boolean hasChildren(List<T> dataList, int parentId, Class<T> dataType) {
for (T data : dataList) {
try {
Method getParentIdMethod = dataType.getMethod("getParentId");
int dataParentId = (int) getParentIdMethod.invoke(data);
if (dataParentId == parentId) {
return true;
}
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
return false;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63