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.