TreeUtil
Introduction
Considering the ubiquity of requirements such as menus, a user has submitted an extremely extensible tree structure implementation. This tree structure can flexibly define relationships between nodes based on configuration files and is well-suited for data in relational databases. It achieves the following:
Relational database data <-> Tree <-> JSON
The biggest issue with tree structures is the relationship problem. In a database, each piece of data is associated with its parent node through a certain field, and the name of this field varies in each business. How can this problem be solved?
The provider of the PR offers a solution: custom field names. Nodes are no longer beans, but maps, allowing for flexible field name definition.
Usage
Defining the Structure
Let’s assume we want to build a menu that includes system management and shop management. The menu would look like this:
System Management
|- User Management
|- Add User
Shop Management
|- Product Management
|- Add Product
How would this structure be saved in a database? Typically, it would look like this:
id | parentId | name | weight |
---|---|---|---|
1 | 0 | System Management | 5 |
11 | 1 | User Management | 10 |
111 | 11 | Add User | 11 |
2 | 0 | Shop Management | 5 |
21 | 2 | Product Management | 10 |
221 | 21 | Add Product | 11 |
We can see that each piece of data is related to each other through the parentId
and represents a hierarchical relationship. parentId
is also known as a foreign key here.
Building the Tree
// Build the list of nodes
List<TreeNode<String>> nodeList = CollUtil.newArrayList();
nodeList.add(new TreeNode<>("1", "0", "System Management", 5));
nodeList.add(new TreeNode<>("11", "1", "User Management", 222222));
nodeList.add(new TreeNode<>("111", "11", "Add User", 0));
nodeList.add(new TreeNode<>("2", "0", "Shop Management", 1));
nodeList.add(new TreeNode<>("21", "2", "Product Management", 44));
nodeList.add(new TreeNode<>("221", "21", "Product Management 2", 2));
TreeNode represents an abstract node and also represents a row of data in the database. If there is other data, you can call
setExtra
to add extension fields.
// 0 indicates that the top-level ID is 0
List<Tree<String>> treeList = TreeUtil.build(nodeList, "0");
Because the two Trees are at the same level and have no parent node, it is a List.
Custom Field Names
// Configuration
TreeNodeConfig treeNodeConfig = new TreeNodeConfig();
// Custom attribute names with default values
treeNodeConfig.setWeightKey("order");
treeNodeConfig.setIdKey("rid");
// Maximum recursive depth
treeNodeConfig.setDeep(3);
// Converter (meaning: find all child nodes whose parent node is the string "0", and recursively find their corresponding child nodes, with a maximum depth of 3)
List<Tree<String>> treeNodes = TreeUtil.<TreeNode, String>build(nodeList, "0", treeNodeConfig,
(treeNode, tree) -> {
tree.setId(treeNode.getId());
tree.setParentId(treeNode.getParentId());
tree.setWeight(treeNode.getWeight());
tree.setName(treeNode.getName());
// Extended attributes ...
tree.putExtra("extraField", 666);
tree.putExtra("other", new Object());
});
Through TreeNodeConfig, we can customize the name of the node and the name of the relationship node ID, so that it can correspond to different databases.