参考于京东和天猫的分类树结构,整理了个人在做电商项目分类树的开发思路及演变过程:
第一种方式:
纯粹的采用Java代码和SQL语句方式,不断的递归查询,其自身是否拥有子节点,这样有不好的缺点就是,访问时数据SQL链接比较多,占用了数据库链接。
实现思路:
- 查询是否拥有子节点的SQL是:
select c.* from category c where c.status = 1 and c.parent_id=?
- 实现组装的TreeNo:
// 分类ID
private Long categoryId;
// 分类名称
private String categoryName;
private List<
TreeNo
> childrens;
不断通过
java 递归方式去获取该节点是否拥有子节点,如果拥有则继续往下递归,直到递归到没有子节点。
方式:比较影响数据库的访问,可以通过MemcachedCache缓存起来,定时的刷新分类树缓存。
第二种方式:
利用
函数
Function遍历一张
数据库数据
表中分类树关系:
如:
CREATE DEFINER=`localhot` FUNCTION `Tree`(treeId INT) RETURNS VARCHAR(4000) CHARSET utf8 BEGIN DECLARE sTemp VARCHAR(4000); DECLARE sTempChd VARCHAR(4000); SET sTemp = ‘$‘; SET sTempChd = CAST(treeId AS CHAR); WHILE sTempChd IS NOT NULL DO SET sTemp = CONCAT(sTemp,‘,‘,sTempChd); SELECT GROUP_CONCAT(id) INTO sTempChd FROM Category WHERE FIND_IN_SET(parent_id,sTempChd)>0; END WHILE; RETURN sTemp; END$$ DELIMITER
返回当前节点的晚辈集合:
再去分析出来,进行数据组装;
第三种方式:
取出表中的全部SQL数据,再去组装分类树的结构数据:
public final class CategoryHelper { private static TreeNode root; public static HashSet<TreeNode> tempNodeList; private static CategoryHelper treeHelper; private final static Lock lock =new ReentrantLock(); //创建一个锁 private CategoryHelper() { /** * TODO 加载分类树 */ } public static CategoryHelper getInstance() { if (treeHelper == null) { lock .lock(); try { if (treeHelper == null) { treeHelper = new CategoryHelper(); } } catch (Exception e) { e.printStackTrace(); }finally { lock.unlock(); } } return treeHelper; } /** * 找到一颗树中某个节点 * @param tree * @param id * @return */ public static TreeNode getTreeNodeById(TreeNode tree, int id) { if (tree == null) return null; TreeNode treeNode = tree.findTreeNodeById(id); return treeNode; } /** * 根据节点ID找到TreeNote */ public static TreeNode getTreeNodeById(int id){ if(id==-1) return null; HashMap<String,TreeNode> hashMap=getNodesIntoMap(); TreeNode node=hashMap.get(String.valueOf(id)); return node; } /** generate a tree from the given treeNode or entity list * 生成树或实体由给定的treeNode名单 * * */ public static void generateTree() { HashMap nodeMap = getNodesIntoMap(); Iterator it = nodeMap.values().iterator(); while (it.hasNext()) { TreeNode treeNode = (TreeNode) it.next(); int parentId = treeNode.getParentId(); String parentKeyId = String.valueOf(parentId); if (nodeMap.containsKey(parentKeyId)) { TreeNode parentNode = (TreeNode) nodeMap.get(parentKeyId); if (parentNode == null) { return; } else { parentNode.addChildNode(treeNode); } } } } /** * set the parent nodes point to the child nodes * 设置子节点集合添加到父节点上。 * @param nodeMap * a hashmap that contains all the treenodes by its id as the key * 一个hashmap,包括所有treenodes由其id作为关键 */ @Deprecated private void putChildIntoParent(HashMap nodeMap) { Iterator it = nodeMap.values().iterator(); while (it.hasNext()) { TreeNode treeNode = (TreeNode) it.next(); int parentId = treeNode.getParentId(); String parentKeyId = String.valueOf(parentId); if (nodeMap.containsKey(parentKeyId)) { TreeNode parentNode = (TreeNode) nodeMap.get(parentKeyId); if (parentNode == null) { return; } else { parentNode.addChildNode(treeNode); } } } } /** * put all the treeNodes into a hash table by its id as the key * 把所有的treeNodes成一张哈希表由其id作为关键 * @return hashmap that contains the treenodes */ protected static HashMap<String,TreeNode> getNodesIntoMap() { int maxId = Integer.MAX_VALUE; HashMap<String,TreeNode> nodeMap = new HashMap<String, TreeNode>(); Iterator<TreeNode> it = tempNodeList.iterator(); while (it.hasNext()) { TreeNode treeNode = (TreeNode) it.next(); int id = treeNode.getSelfId(); if (id < maxId) { maxId = id; root = treeNode; } String keyId = String.valueOf(id); nodeMap.put(keyId, treeNode); } return nodeMap; } /** * add a tree node to the tempNodeList * 添加一个树节点的tempNodeList * * */ public static void addTreeNode(TreeNode treeNode) { lock.lock(); try { boolean insertFlag =root.insertJuniorNode(treeNode); //将已经产生的树节点插入到树 if (insertFlag) { tempNodeList.add(treeNode); } else{ throw new BusinessException(treeNode.getParentId()+"这父节点不存在", ErrorCode.BUSINESS_ERROR); } } catch (Exception e) { e.printStackTrace(); }finally{ lock.unlock(); } } /* 删除节点和它下面的晚辈 */ public static void delTreeNode(TreeNode treeNode){ boolean absent = tempNodeList.contains(treeNode); if(absent){ lock.lock(); try { List<TreeNode> delTempNodeList = new ArrayList<TreeNode>(); List<TreeNode> childList = treeNode.getJuniors(); if (AppUtils.isNotBlank(childList)) { for (int i = 0; i < childList.size(); i++) { delTempNodeList.add(childList.get(i)); } } delTempNodeList.add(treeNode); tempNodeList.removeAll(delTempNodeList); // 更新tempNodeList treeNode.deleteNode(); } catch (Exception e) { e.printStackTrace(); }finally{ lock.unlock(); } } } /* 删除当前节点的某个子节点 */ public static void delTreeNode(TreeNode treeNode,int childId){ boolean absent = tempNodeList.contains(treeNode); if(absent){ lock.lock(); try { List<TreeNode> childList = treeNode.getChildList(); if(AppUtils.isNotBlank(childList)){ int childNumber = childList.size(); for (int i = 0; i < childNumber; i++) { TreeNode child = childList.get(i); if (child.getSelfId() == childId) { tempNodeList.remove(child); treeNode.deleteChildNode(childId); } } } } catch (Exception e) { e.printStackTrace(); }finally{ lock.unlock(); } } } /** * 更新树节点 * @param sourceTreeNode 源对象 * @param targetTreeNode 目标对象 */ public static void updateTreeNode(TreeNode sourceTreeNode,String noteName){ boolean absent = tempNodeList.contains(sourceTreeNode); if(absent){ lock.lock(); try { sourceTreeNode.setNodeName(noteName); Object obj=sourceTreeNode.getObj(); if(obj!=null && obj instanceof OrganizationEntity){ OrganizationEntity entity=(OrganizationEntity)obj; entity.setOrgName(noteName); } tempNodeList.add(sourceTreeNode); } catch (Exception e) { e.printStackTrace(); }finally{ lock.unlock(); } } } /** * 更新树节点 * @param sourceTreeNode 源对象 * @param targetTreeNode 目标对象 */ public static void updateTreeNode(TreeNode sourceTreeNode,TreeNode targetTreeNode){ boolean absent = tempNodeList.contains(sourceTreeNode); if(absent){ lock.lock(); try { sourceTreeNode.setNodeName(targetTreeNode.getNodeName()); OrganizationEntity entity=new OrganizationEntity(targetTreeNode.getParentId(), targetTreeNode.getSelfId(), targetTreeNode.getNodeName()); sourceTreeNode.setObj(entity); tempNodeList.add(sourceTreeNode); } catch (Exception e) { e.printStackTrace(); }finally{ lock.unlock(); } } } /** * adapt the entities to the corresponding treeNode * 适应于相应的treeNode的实体 * @param entityList * list that contains the entities 列表包含的实体 *@return the list containg the corresponding treeNodes of the entities * containg相应的treeNodes名单的实体 */ public static HashSet<TreeNode> changeEnititiesToTreeNodes(Set entityList) { OrganizationEntity orgEntity = new OrganizationEntity(); Set<TreeNode> tempNodeList = new HashSet<TreeNode>(); Map<Integer, TreeNode> map=new HashMap<Integer, TreeNode>(); TreeNode treeNode; Iterator<OrganizationEntity> it = entityList.iterator(); while (it.hasNext()) { orgEntity = (OrganizationEntity) it.next(); treeNode = new TreeNode(); treeNode.setObj(orgEntity); treeNode.setParentId(orgEntity.getParentId()); treeNode.setSelfId(orgEntity.getOrgId()); treeNode.setNodeName(orgEntity.getOrgName()); treeNode.setParentNode(map.get(orgEntity.getParentId())); tempNodeList.add(treeNode); map.put(orgEntity.getOrgId(), treeNode); } return (HashSet<TreeNode>) tempNodeList; } public static TreeNode getRoot() { return root; } public static HashSet<TreeNode> getTempNodeList() { return tempNodeList; } public static void setTempNodeList(HashSet<TreeNode> tempNodeList) { CategoryHelper.tempNodeList = tempNodeList; } }
public class TreeNode implements Serializable { /** * */ private static final long serialVersionUID = 1L; private int parentId; private int selfId; protected String nodeName; protected Object obj; protected TreeNode parentNode; protected List<TreeNode> childList; public TreeNode() { initChildList(); } public TreeNode(TreeNode parentNode) { this.getParentNode(); initChildList(); } public boolean isLeaf() { if (childList == null) { return true; } else { if (childList.isEmpty()) { return true; } else { return false; } } } /* 插入一个child节点到当前节点中 */ public void addChildNode(TreeNode treeNode) { initChildList(); childList.add(treeNode); } public void initChildList() { if (childList == null) childList = new ArrayList<TreeNode>(); } public boolean isValidTree() { return true; } /* 返回当前节点的父辈节点集合 */ public List<TreeNode> getElders() { List<TreeNode> elderList = new ArrayList<TreeNode>(); TreeNode parentNode = this.getParentNode(); if (parentNode == null) { return elderList; } else if(parentNode.getParentId()==-1) { return elderList; }else{ elderList.add(parentNode); elderList.addAll(parentNode.getElders()); return elderList; } } /* 返回当前节点的晚辈集合 */ public List<TreeNode> getJuniors() { List<TreeNode> juniorList = new ArrayList<TreeNode>(); List<TreeNode> childList = this.getChildList(); if (childList == null) { return juniorList; } else { int childNumber = childList.size(); for (int i = 0; i < childNumber; i++) { TreeNode junior = childList.get(i); juniorList.add(junior); juniorList.addAll(junior.getJuniors()); } return juniorList; } } /* 返回当前节点的同级的集合 */ public List<TreeNode> getCompatriots(){ List<TreeNode> compatriotList = new ArrayList<TreeNode>(); TreeNode parentNode = this.getParentNode(); if (parentNode == null) { return compatriotList; }else if(this.getParentId()==0){ //说明是一级节点 compatriotList=getParentNode().getChildList(); } else if(parentNode.getParentId()==-1) { //说明是根节点 compatriotList= this.getChildList(); }else{ compatriotList.add(this.getParentNode()); compatriotList.addAll(parentNode.getChildList()); } return compatriotList; } /* 返回当前节点的孩子集合 */ public List<TreeNode> getChildList() { return childList; } /* 删除节点和它下面的晚辈 */ public void deleteNode() { TreeNode parentNode = this.getParentNode(); int id = this.getSelfId(); if (parentNode != null) { parentNode.deleteChildNode(id); } } /* 删除当前节点的某个子节点 */ public void deleteChildNode(int childId) { List<TreeNode> childList = this.getChildList(); int childNumber = childList.size(); for (int i = 0; i < childNumber; i++) { TreeNode child = childList.get(i); if (child.getSelfId() == childId) { childList.remove(i); return; } } } /* 动态的插入一个新的节点到当前树中 */ public boolean insertJuniorNode(TreeNode treeNode) { int juniorParentId = treeNode.getParentId(); if(juniorParentId==0){ addChildNode(treeNode); return true; } else if (this.parentId == juniorParentId) { List<TreeNode> list=this.getParentNode().getChildList(); if(list==null || list.isEmpty()){ list = new ArrayList<TreeNode>(); list.add(treeNode); }else{ list.add(treeNode); } return true; } else { List<TreeNode> childList = this.getChildList(); int childNumber = childList.size(); boolean insertFlag; for (int i = 0; i < childNumber; i++) { TreeNode childNode = childList.get(i); insertFlag = childNode.insertJuniorNode(treeNode); if (insertFlag == true) return true; } return false; } } /* 找到一颗树中某个节点 */ public TreeNode findTreeNodeById(int id) { if (this.selfId == id) return this; if (childList.isEmpty() || childList == null) { return null; } else { int childNumber = childList.size(); for (int i = 0; i < childNumber; i++) { TreeNode child = childList.get(i); TreeNode resultNode = child.findTreeNodeById(id); if (resultNode != null) { return resultNode; } } return null; } } /* 遍历一棵树,层次遍历 */ public void traverse() { if (selfId < 0) return; print(this.selfId); if (childList == null || childList.isEmpty()) return; int childNumber = childList.size(); for (int i = 0; i < childNumber; i++) { TreeNode child = childList.get(i); child.traverse(); } } public void print(String content) { System.out.println(content); } public void print(int content) { System.out.println(String.valueOf(content)); } public void setChildList(List<TreeNode> childList) { this.childList = childList; } public int getParentId() { return parentId; } public void setParentId(int parentId) { this.parentId = parentId; } public int getSelfId() { return selfId; } public void setSelfId(int selfId) { this.selfId = selfId; } public TreeNode getParentNode() { return parentNode; } public void setParentNode(TreeNode parentNode) { this.parentNode = parentNode; } public String getNodeName() { return nodeName; } public void setNodeName(String nodeName) { this.nodeName = nodeName; } public Object getObj() { return obj; } public void setObj(Object obj) { this.obj = obj; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + selfId; return result; } @Override public boolean equals(Object obj) { if(obj instanceof TreeNode){ TreeNode node=(TreeNode)obj; return this.selfId==node.getSelfId(); } return false; } public TreeNode(int parentId, int selfId, String nodeName, Object obj) { super(); this.parentId = parentId; this.selfId = selfId; this.nodeName = nodeName; this.obj = obj; } }
public class OrganizationEntity { public int parentId; public int orgId; public String orgName; public int getParentId() { return parentId; } public void setParentId(int parentId) { this.parentId = parentId; } public int getOrgId() { return orgId; } public void setOrgId(int orgId) { this.orgId = orgId; } public String getOrgName() { return orgName; } public void setOrgName(String orgName) { this.orgName = orgName; } public OrganizationEntity(){ } public OrganizationEntity(int parentId, int orgId, String orgName) { super(); this.parentId = parentId; this.orgId = orgId; this.orgName = orgName; } }
数据库表结构设计:
方式一:通过一张表来实现分类树:
CREATE TABLE `ls_category` ( `id` bigint(20) NOT NULL DEFAULT ‘0‘ COMMENT ‘产品类目ID‘, `parent_id` bigint(20) DEFAULT NULL COMMENT ‘父节点‘, `name` varchar(50) NOT NULL DEFAULT ‘‘ COMMENT ‘产品类目名称‘, `pic` varchar(300) DEFAULT NULL COMMENT ‘类目的显示图片‘, `seq` int(5) DEFAULT NULL COMMENT ‘排序‘, `enable` int(1) NOT NULL DEFAULT ‘1‘ COMMENT ‘默认是1,表示正常状态,0为下线状态‘ `keyword` varchar(256) DEFAULT NULL COMMENT ‘SEO关键字‘, `cat_desc` varchar(256) DEFAULT NULL COMMENT ‘SEO描述‘, `title` varchar(256) DEFAULT NULL COMMENT ‘SEO标题‘, `rec_date` datetime DEFAULT NULL COMMENT ‘记录时间‘, `level` int(2) DEFAULT NULL COMMENT ‘分类层级‘, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=‘产品类目‘;
方式二:用两种表结构实现产品的分类树,表一:存放商品分类的一级分类 表二:存放商品分类的无限下级分类,及关联一级分类表的ID引用:
CREATE TABLE `ls_first_cat` ( `first_cat_id` int(11) NOT NULL DEFAULT ‘0‘ COMMENT ‘产品分类ID‘, `first_cat_name` varchar(50) NOT NULL DEFAULT ‘‘ COMMENT ‘产品分类名称‘, `first_cat_picture` varchar(250) DEFAULT NULL COMMENT ‘分类的显示图片‘, `seq` int(11) DEFAULT NULL COMMENT ‘排序‘, `status` int(1) NOT NULL DEFAULT ‘1‘ COMMENT ‘默认是1,表示正常状态,0为下线状态‘ PRIMARY KEY (`first_cat_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=‘商品分类‘; CREATE TABLE `ls_cat` ( `cat_id` int(11) NOT NULL DEFAULT ‘0‘ COMMENT ‘产品分类ID‘, `cat_name` varchar(50) NOT NULL DEFAULT ‘‘ COMMENT ‘产品分类名称‘, `first_cat_id` varchar(250) DEFAULT NULL COMMENT ‘对应的一级类型‘, `seq` int(11) DEFAULT NULL COMMENT ‘排序‘, `status` int(1) NOT NULL DEFAULT ‘1‘ COMMENT ‘默认是1,表示正常状态,0为下线状态‘, `parent_cat_id` int(11) COMMENT ‘父节点ID‘ PRIMARY KEY (`cat_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=‘商品子类表‘;
资料参考:
http://my.oschina.net/bootstrap/blog/166805
时间: 2024-11-02 14:18:43