1. 程式人生 > >樹結構表,列表轉換成樹狀關係的工具類

樹結構表,列表轉換成樹狀關係的工具類

controller

@RequestMapping(value = "deviceinfo")
@JSON(type = InfoTree.class, filter = "createDate,updateDate,pageNo,pageSize,parentIds")
public List treeData2(@RequestParam(required=false) String extId, HttpServletResponse response) {

	List<InfoTree> list = testTreeService.findLists(new InfoTree());

	return list;
}

service

public List<InfoTree> findLists(InfoTree testTree) {
	List<InfoTree> list = super.findList(testTree);
	List<InfoTree> servicePlaces = TreeSupportEntity.list2tree(list, InfoTree::setChildList,
			(Predicate<InfoTree>) servicePlace ->
					// parentId為空或者為-1的選單則認為是根選單
					StringUtils.isEmpty(servicePlace.getParentId()) || servicePlace.getParentId().equals("-1")
							|| servicePlace.getParentId().equals("0"));

	return servicePlaces;
}

xml

<select id="findList" resultType="com.hollysmart.admin.modules.info.entity.InfoTree">
	SELECT 
		<include refid="testTreeColumns"/>
	FROM test_tree_data a
	<include refid="testTreeJoins"/>
	<where>
		a.del_flag = #{DEL_FLAG_NORMAL}
		<if test="parent != null and parent.id != null and parent.id != ''">
			AND a.parent_id = #{parent.id}
		</if>
		<if test="parentIds != null and parentIds != ''">
			AND a.parent_ids LIKE 
				<if test="dbName == 'oracle'">'%'||#{parentIds}||'%'</if>
				<if test="dbName == 'mssql'">'%'+#{parentIds}+'%'</if>
				<if test="dbName == 'mysql'">concat('%',#{parentIds},'%')</if>
		</if>
		<if test="name != null and name != ''">
			AND a.name LIKE 
				<if test="dbName == 'oracle'">'%'||#{name}||'%'</if>
				<if test="dbName == 'mssql'">'%'+#{name}+'%'</if>
				<if test="dbName == 'mysql'">concat('%',#{name},'%')</if>
		</if>
	</where>
	ORDER BY a.sort ASC
</select>

實體類

public class InfoTree extends TreeEntity<InfoTree> implements TreeSupportEntity {
	
	private static final long serialVersionUID = 1L;
	private InfoTree parent;		// 父級編號
	private String parentIds;		// 所有父級編號
	private String name;		// 名稱
	private Integer sort;		// 排序
	private List<InfoTree> childList = Lists.newArrayList();

	//getter/setter
}

樹狀工具實體類

import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 樹形資料構造
 *
 * @param <PK> 實體主鍵型別
 */
public interface TreeSupportEntity<PK> {

    PK getId();

    PK getParentId();

    /**
     * 集合轉為樹形結構,返回根節點集合
     *
     * @param dataList      需要轉換的集合
     * @param childConsumer 設定子節點回調
     * @param <N>           樹節點型別
     * @param <PK>          主鍵型別
     * @return 樹形結構集合
     */
    static <N extends TreeSupportEntity<PK>, PK> List<N> list2tree(Collection<N> dataList, BiConsumer<N, List<N>> childConsumer) {
        return list2tree(dataList, childConsumer, (Function<TreeHelper<N, PK>, Predicate<N>>) predicate -> node -> node == null || predicate.getNode(node.getParentId()) == null);
    }

    /**
     * 列表結構轉為樹結構,並返回根節點集合
     *
     * @param dataList          資料集合
     * @param childConsumer     子節點消費介面,用於設定子節點
     * @param rootNodePredicate 判斷是否為跟節點的函式
     * @param <N>               元素型別
     * @param <PK>              主鍵型別
     * @return 根節點集合
     */
    static <N extends TreeSupportEntity<PK>, PK> List<N> list2tree(Collection<N> dataList,
                                                                   BiConsumer<N, List<N>> childConsumer,
                                                                   Predicate<N> rootNodePredicate) {
        return list2tree(dataList, childConsumer, (Function<TreeHelper<N, PK>, Predicate<N>>) predicate -> rootNodePredicate);
    }

    /**
     * 列表結構轉為樹結構,並返回根節點集合
     *
     * @param dataList          資料集合
     * @param childConsumer     子節點消費介面,用於設定子節點
     * @param predicateFunction 根節點判斷函式,傳入helper,獲取一個判斷是否為跟節點的函式
     * @param <N>               元素型別
     * @param <PK>              主鍵型別
     * @return 根節點集合
     */
    static <N extends TreeSupportEntity<PK>, PK> List<N> list2tree(final Collection<N> dataList,
                                                                   final BiConsumer<N, List<N>> childConsumer,
                                                                   final Function<TreeHelper<N, PK>, Predicate<N>> predicateFunction) {
        Objects.requireNonNull(dataList, "source list can not be null");
        Objects.requireNonNull(childConsumer, "child consumer can not be null");
        Objects.requireNonNull(predicateFunction, "root predicate function can not be null");

        Supplier<Stream<N>> streamSupplier = () -> dataList.size() < 1000 ? dataList.stream() : dataList.parallelStream();
        // id,node
        Map<PK, N> cache = new HashMap<>();
        // parentId,children
        Map<PK, List<N>> treeCache = streamSupplier.get()
                .peek(node -> cache.put(node.getId(), node))
                .collect(Collectors.groupingBy(TreeSupportEntity::getParentId));

        Predicate<N> rootNodePredicate = predicateFunction.apply(new TreeHelper<N, PK>() {
            @Override
            public List<N> getChildren(PK parentId) {
                return treeCache.get(parentId);
            }

            @Override
            public N getNode(PK id) {
                return cache.get(id);
            }
        });

        return streamSupplier.get()
                //設定每個節點的子節點
                .peek(node -> childConsumer.accept(node, treeCache.get(node.getId())))
                //獲取根節點
                .filter(rootNodePredicate)
                .collect(Collectors.toList());
    }

    /**
     * 樹結構Helper
     *
     * @param <T>  節點型別
     * @param <PK> 主鍵型別
     */
    interface TreeHelper<T, PK> {
        /**
         * 根據主鍵獲取子節點
         *
         * @param parentId 節點ID
         * @return 子節點集合
         */
        List<T> getChildren(PK parentId);

        /**
         * 根據id獲取節點
         *
         * @param id 節點ID
         * @return 節點
         */
        T getNode(PK id);
    }
}