前言
接上一篇文章中我们说了下怎么去做《通用service》,来简化单表操作下的通用service层的逻辑,今天我们来接着讲解下通用的树表结构操作。
思考
首先我们先思考一下,通用的树结构操作都需要那些功能?
对于树结构首先我们知道该表一定是一个自关联的,也就是需要一个关联自己的父ID,来做上下级关联,然后我们需要一个排序值,因为通常我们都需要对孩子节点有个排序,这样也方便使用,当然这个排序值是可有可无的,要看具体业务,好了我们的需求就是这样,下面我们来看具体的实现思路
实现上述思考
我们首先要确定的是我们的表结构,首先要求表中有个parent_id字段,作为自身的关联,还有一个sort字段(当然这个字段是可有可无的,根据业务需要)
- 接下来我们需要两个接口类:TreePO、SortTreePO一个定义树接口另一个用作排序树,具体的业务实体对象应该实现该接口以取得通用树操作功能。
- 还有个Node树节点类用来封装整颗树。
- 在定义一个TreeCrudService树操作接口,该接口拥有操作树的常用方法定义,它的实现有两个:BaseTreeCurdServiceImpl、BaseSortTreeCrudServiceImpl,看名称大家也能够理解,一个实现了树的基本操作,另一个则再此基础上增加排序功能,如果你的业务不需要有排序字段,则继承第一个就可以了。
好了,举一个业务中的例子来说明下,加深一下对该功能的使用,我们现在以组织架构为例,组织架构中会存储:公司、部门、组别等信息,一个公司有多个部门,一个部门有多个组别,所以我们将其建立到一张表上,它其实是满足树型结构的,最后时我们用该具体的业务例子向你演示一下通用树操作该怎样实现以及使用,下面看下建表SQL语句:
-- 组织架构表
DROP TABLE IF EXISTS `org`;
CREATE TABLE `org` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`name` varchar(64) NOT NULL COMMENT '组织架构名称',
`type` varchar(32) NOT NULL COMMENT '类型',
`sort` int(11) DEFAULT 0 COMMENT '排序值',
`parent_id` int(11) NOT NULL COMMENT '父ID',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_time` datetime NOT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
INDEX index_parent_id(`parent_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='组织架构';
TreePO树实体父接口:
package com.zm.zhuma.commons.model.po;
public interface TreePO<PK> extends PO<PK> {
PK getParentId();
void setParentId(PK parentId);
}
树节点:
package com.zm.zhuma.commons.model.bo;
import com.zm.zhuma.commons.model.po.TreePO;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* @desc 树节点
*
* @author zhuamer
* @since 19/12/2017 9:54 AM
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Node<E extends TreePO> {
private E parent;
private List<Node<E>> children;
}
备注
- PO类(也就是数据库表对应的实体类)的统一接口,所有的PO类都应该实现该接口。
- 我们的实体类目录会分的相对详细些:po(persistant object 持久对象)、qo(query object查询对象)、vo(view object 值对象)、bo(business object 业务对象)。
TreeCrudService接口:
package com.zm.zhuma.commons.service;
import com.zm.zhuma.commons.model.po.TreePO;
/**
* @desc 树结构crud服务
*
* @author zhumaer
* @since 10/18/2017 18:31 PM
*/
public interface TreeCrudService<E extends TreePO, PK> extends
CrudService<E, PK>,
TreeSelectService<E, PK> {
}
package com.zm.zhuma.commons.service;
import com.zm.zhuma.commons.model.bo.Node;
import com.zm.zhuma.commons.model.po.TreePO;
import java.util.List;
/**
* @desc 树结构查看服务
*
* @author zhumaer
* @since 10/18/2017 18:31 PM
*/
public interface TreeSelectService<E extends TreePO, PK> {
/**
* 根据父节点id获取子节点数据
*
* @param parentId 父节点ID
* @return 子节点数据
*/
List<E> selectChildren(PK parentId);
/**
* 获取当前节点下树数据
*
* @param parentId 父节点ID
* @return 树信息
*/
Node<E> selectNodeByParentId(PK parentId);
}
备注
- TreeCrudService该服务,我们继承了CrudService,让其拥有普通表的全部增删改查功能,然后用E extends TreePO用以限制使用该接口服务,必须先有个parentId的功能实现。
- 对于树接口的查询我们定义两个方法一个获取孩子列表的,一个是获取完整树的。
BaseTreeCurdServiceImpl通用接口实现逻辑:
package com.zm.zhuma.commons.service.impl;
import com.google.common.collect.Lists;
import com.zm.zhuma.commons.model.bo.Node;
import com.zm.zhuma.commons.model.po.TreePO;
import com.zm.zhuma.commons.service.TreeCrudService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Assert;
import java.util.ArrayList;
import java.util.