요르딩딩

계층구조 쿼리 & makeTree 본문

[Web]/[Spring]

계층구조 쿼리 & makeTree

요르딩딩 2021. 11. 23. 11:44
728x90
반응형

이번에는 폴더안의 폴더 또는 데이터를 포함하는 계층적인 트리를 만들어 보게되었습니다.

여기서의 요점!!!

첫번째 테이블에서 계층적인 순서로 조회하기

두번째 조회한 리스트를 depth적으로 표현하기 입니다.

계층 구조적인 데이터를 가진 테이블에서 계층순서로 데이터를 조회하고, 이를 map에서 depth별로 노출될 수 있는 비지니스 로직을 구현해 보았습니다. 

 

아래의 로직은 다시한번 상기시키기 위해 테스트용으로 작성한것입니다.

 

# 테이블 데이터 (계층적 구조를 가진 테이블입니다.)

- 테이블 이름 : tree_table

- no = parent_no에따라 (자식 - 부모)관계를 가집니다.

tree_table

# 1. 최상위 노드들만 순서대로 조회하기 (이는 depth별로 표현할때 반복문의 횟수를 줄이고자 적용했습니다.)

<select id="selectRootTree" parameterType="java.util.HashMap" resultType="java.util.HashMap">
  SELECT 
    pk
    , no
    , parent_no
    , sort
  FROM 
	  tree_table
  WHERE 
	  parent_no = '*'
</select>

- 조회 결과

# 2. 계층구조의 데이터를 가진 테이블에서 계층순서로 데이터를 조회하기. (중요!!! 이때 순서는 위의 최상위 노들들과 순서가 같아야한다.)

   ex, A,a1,a2,B,b1,b2C,c1...순

<select id="selectTree" parameterType="java.util.HashMap" resultType="java.util.HashMap">
	  WITH RECURSIVE inc AS
		(
			SELECT 
				pk
		    	, no
		    	, parent_no
		    	, sort
				, CAST(no AS CHAR) lvl
			FROM 
				tree_table
			WHERE 
				parent_no = '*'
			UNION ALL
			SELECT 
				tt.pk
		    	, tt.no
		    	, tt.parent_no
		    	, tt.sort
				, CONCAT(inc.lvl, "," , tt.no) lvl
			FROM 
				tree_table tt
			INNER JOIN 
				inc
			ON tt.parent_no = inc.no
		)
		SELECT 
			inc.pk
	    	, inc.no
	    	, inc.parent_no
	    	, inc.sort
		FROM 
			inc
		ORDER BY lvl;
	</select>

* 주의사항 : lvl의 길이가 길면, 값이 짤려 순서가 이상하게 조회될 수 있다 이럴때는

, CAST(no AS CHAR) lvl  ---> , CAST(no AS CHAR(100)) lvl 로 적절히 수정해보자

 

- 조회 결과

# 3. 비지니스 로직 

   최상위 노드별로 반복하며, depth를 채우는 방식입니다.

public JhResult tree() {
	JhResult result = new JhResult();
    HashMap<String, Object> resultData = new HashMap<String, Object>();
			
	HashMap<String, Object> newParam = new HashMap<String, Object>();
	List<HashMap<String, Object>> resultList = new ArrayList<HashMap<String, Object>>();

	List<HashMap<String, Object>> rootList = new ArrayList<HashMap<String, Object>>();
	rootList = testMapper.selectRootTree(newParam);
			
	if(!EmptyUtil.isEmpty(rootList)) 
	{
		List<HashMap<String, Object>> treeList = testMapper.selectTree(newParam);
		int size = treeList.size();
				
		int idx = 0;
		for(HashMap<String, Object> root : rootList) { //최상위 노드별 반복
			idx = makeTree(root, treeList, size, idx);
			resultList.add(root);
		}
	 }
     resultData.put("list", resultList);
     result.setResultData(resultData);
     result.setResultCode(200);
     return result;
}

 

# 3-1. 계층쿼리로 조회한 리스트를 depth를 가진 map으로 변환하기

   재귀를 이용하여 자식을 쌓는 방식입니다. 풀 스캔하지 않도록 인덱스를 활용하여, 조회된 인덱스 이후부터 조회를 하도록 하였습니다.

@SuppressWarnings("unchecked")
private int makeTree(HashMap<String, Object> root, List<HashMap<String, Object>> treeList, int size, int idx) {
	List<HashMap<String, Object>> child = null;
	if(EmptyUtil.isEmpty(root.get("children"))) 
	{
		child = new ArrayList<HashMap<String, Object>>();
		root.put("children", child);
	}
	else{
		child = (List<HashMap<String, Object>>)root.get("children");
	}

	String rootTreeNo = StringUtil.fixNull(root.get("no"),"0");
	while(idx < size) {
		HashMap<String, Object> tree = (HashMap<String, Object>) treeList.get(idx);
		String treeNo =  StringUtil.fixNull(tree.get("no"),"0");
		String pTreeNo = StringUtil.fixNull(tree.get("parent_no"),"0");

		if(rootTreeNo.equals(treeNo)) {
			idx++;
			continue;
		}
		else if(rootTreeNo.equals(pTreeNo)) {
			child.add(tree);
			idx = makeTree(tree, treeList, size, idx);
		} 
		else { //종료조건
			break;
		}
	 }
	return idx;
}

# 결과

{
    "resultCode": 200,
    "resultMsg": "",
    "resultData": {
        "list": [
            {
                "parent_no": "*",
                "no": "A",
                "children": [
                    {
                        "parent_no": "A",
                        "no": "a1",
                        "children": [],
                        "pk": 2,
                        "sort": "1"
                    },
                    {
                        "parent_no": "A",
                        "no": "a2",
                        "children": [],
                        "pk": 3,
                        "sort": "1"
                    }
                ],
                "pk": 1,
                "sort": "1"
            },
            {
                "parent_no": "*",
                "no": "B",
                "children": [
                    {
                        "parent_no": "B",
                        "no": "b1",
                        "children": [],
                        "pk": 5,
                        "sort": "1"
                    },
                    {
                        "parent_no": "B",
                        "no": "b2",
                        "children": [],
                        "pk": 6,
                        "sort": "1"
                    }
                ],
                "pk": 4,
                "sort": "1"
            },
            {
                "parent_no": "*",
                "no": "C",
                "children": [
                    {
                        "parent_no": "C",
                        "no": "c1",
                        "children": [],
                        "pk": 8,
                        "sort": "2"
                    },
                    {
                        "parent_no": "C",
                        "no": "c2",
                        "children": [],
                        "pk": 11,
                        "sort": "2"
                    },
                    {
                        "parent_no": "C",
                        "no": "c3",
                        "children": [],
                        "pk": 12,
                        "sort": "2"
                    }
                ],
                "pk": 7,
                "sort": "2"
            },
            {
                "parent_no": "*",
                "no": "D",
                "children": [
                    {
                        "parent_no": "D",
                        "no": "d1",
                        "children": [],
                        "pk": 10,
                        "sort": "1"
                    },
                    {
                        "parent_no": "D",
                        "no": "d2",
                        "children": [],
                        "pk": 13,
                        "sort": "1"
                    }
                ],
                "pk": 9,
                "sort": "1"
            }
        ]
    }
}

728x90
반응형
Comments