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);
}
}