通用处理数据库中父子结构数据转树形数结构问题。

王大爷 2023年04月17日 540次浏览

mysql递归查询

在mysql中,可以使用 MySQL 的递归查询(Common Table Expressions,CTE)来解决这个问题。递归查询能够在查询时直接构建层次结构数据,从而减少在 Java 代码中处理的复杂度。

下面是一个使用 CTE 进行递归查询的例子。假设我们有一个名为 my_table 的表,包含 id、name 和 parent_id 三个字段。我们可以通过以下 SQL 语句构建层次结构数据:


WITH RECURSIVE cte (id, name, parent_id, level) AS (
    -- 基本情况:获取所有根节点(parent_id 为 NULL 或 0 的记录)
    SELECT id, name, parent_id, 1 as level
    FROM my_table
    WHERE parent_id IS NULL OR parent_id = 0

    UNION ALL

    -- 递归情况:获取子节点,并将层级加 1
    SELECT t.id, t.name, t.parent_id, cte.level + 1 as level
    FROM my_table t
    JOIN cte ON t.parent_id = cte.id
)

-- 查询结果
SELECT * FROM cte
ORDER BY level, id;

这个查询将返回一个包含 id、name、parent_id 和 level 四个字段的结果集。level 字段表示每条记录在层次结构中的层级,可以帮助我们在 Java 代码中更轻松地处理数据。

在实际使用中,你需要根据具体的表结构和需求调整查询语句。通过使用递归查询,可以避免多次查询数据库和在 Java 代码中处理复杂的层次结构逻辑。


通用java类

下面是一个 Java 实现类的例子,它可以将从数据库查询到的扁平化结构转换为树形结构。这个实现类使用了泛型和反射来实现通用性。

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class TreeBuilder<T> {

    private final List<T> items;
    private final String idFieldName;
    private final String parentIdFieldName;
    private final String childrenFieldName;

    public TreeBuilder(List<T> items, String idFieldName, String parentIdFieldName, String childrenFieldName) {
        this.items = items;
        this.idFieldName = idFieldName;
        this.parentIdFieldName = parentIdFieldName;
        this.childrenFieldName = childrenFieldName;
    }

    public List<T> buildTree() throws IllegalAccessException, NoSuchFieldException {
        Map<Object, T> itemMap = new HashMap<>();
        List<T> roots = new ArrayList<>();

        for (T item : items) {
            Field idField = item.getClass().getDeclaredField(idFieldName);
            idField.setAccessible(true);
            Object idValue = idField.get(item);
            itemMap.put(idValue, item);
        }

        for (T item : items) {
            Field parentIdField = item.getClass().getDeclaredField(parentIdFieldName);
            parentIdField.setAccessible(true);
            Object parentIdValue = parentIdField.get(item);

            if (parentIdValue == null || !itemMap.containsKey(parentIdValue)) {
                roots.add(item);
            } else {
                T parent = itemMap.get(parentIdValue);
                Field childrenField = parent.getClass().getDeclaredField(childrenFieldName);
                childrenField.setAccessible(true);

                List<T> children = (List<T>) childrenField.get(parent);
                if (children == null) {
                    children = new ArrayList<>();
                    childrenField.set(parent, children);
                }
                children.add(item);
            }
        }

        return roots;
    }
}

这个 TreeBuilder 类接受一个泛型参数 T,它表示树形结构中的节点类型。在构造函数中,需要传入一个节点列表 items,以及表示节点 ID、父节点 ID 和子节点列表的字段名。buildTree 方法会将节点列表转换为树形结构。

在使用这个类时,需要将从数据库查询到的数据转换为节点对象列表,然后调用 buildTree 方法构建树形结构。例如:

List<MyNode> items = ... // 从数据库查询到的数据
TreeBuilder<MyNode> treeBuilder = new TreeBuilder<>(items, "id", "parentId", "children");
List<MyNode> tree = treeBuilder.buildTree();

注意:这个实现假设节点类型 T 中的子节点列表字段是一个 List 类型。如果实际情况不符合这个假设,需要根据实际需求修改 TreeBuilder 类中的相关代码。