这种视图结构可能被用在菜单,或者某种体现分类的信息上面等等。今天就让我们来探讨下怎么从后端去实现这样的一个内容!如果是Java使用者那就比较熟悉这其中的语法了。
首先看下前端浏览器的效果:
从其中不难发现,这是个分类信息展示,而且具有多级效果,业务上是需要无限极的特点的,上级和下级具有关联关系的。那么这种结构需要什么样的数据库表结构呢?
数据库内需要存在以上4个信息以上才能很方便地展示出刚才的效果,至少目前来看是拥有足够的信息了。那么接下来咱们的业务层如何去处理?
下面我定义了一个transform方法,用于取出数据库内所有的分类数据,并且转换为相对应的TreeNode对象的集合。这个TreeNode集合是一个彼此有关系的对象集合,TreeNode至少拥有如此的属性:id、text、children,其中children又是一个TreeNode对象的集合,代表子分类对象的集合。
public List<TreeNode> transform() {
// Gets all categories
List<Category> categories = categoryDao.selectWhole();
// Do sorting this categories,by level attribute value.The level 0 is
// root category.
categories.sort(new Comparator<Category>() {
@Override
public int compare(Category o1, Category o2) {
Integer level1 = o1.getLevel();
Integer level2 = o2.getLevel();
// The min level will be sorted to the top
return level1.compareTo(level2);
}
});
// Initial list of tree nodes
List<TreeNode> treeNodes = new ArrayList<>();
for (Category category : categories) {
// Transform the category to a new treenode
TreeNode newTreeNode = new TreeNode(category.getName());
newTreeNode.setId(category.getId());
newTreeNode.setSuperId(category.getSuperId());
// If the category‘s super id is null,that means it is a root
// category.
if (category.getSuperId() == null) {
treeNodes.add(newTreeNode);
} else {// Else call this method for next step
treePlant(treeNodes, newTreeNode);
}
}
return treeNodes;
}
transform方法内,首先调用数据访问层的方法将分类数据全部取出来,并返回一个领域对象的集合,这个领域对象跟数据库的结构是属于对应关系的,肯定是不能直接拿来用在显示上面的,这里并没有使用像Hibernate那种的ORM框架,可以理解为使用的就是原生的JDBC技术。接下来调用了该集合的sort方法用于排序集合内的对象,且从里面的内容可以看出,排序依据是领域对象的level属性,也就是级别。在此处,说下级别是什么意思,最顶级的分类可以将其级别置为0,第二级别置为1,以此类推。添加级别属性的话,是对处理层次关系是有很大的帮助的,此处就用上了。经过这样排序的操作,那么最终这个集合内是一个级别最小排最前面的集合。为什么进行排序?后面就会知道。接下来定义一个TreeNode新的空的集合,用于方法返回。循环遍历领域对象的集合,并将当前循环到的对象转换为一个新的TreeNode对象,分别将编号和父编号赋值到TreeNode。然后对领域对象进行一个判断,判断父编号是否为空,如果为空的话,就直接将其放入返回对象集合内,即TreeNode集合。反之调用treePlant方法:
private void treePlant(List<TreeNode> treeNodes, TreeNode newTreeNode) {
// Else it is not a root category,then find the super category and plant
// the category in it.
for (TreeNode treeNode : treeNodes) {
if (treeNode.getId().equals(newTreeNode.getSuperId())) {
treeNode.getChildren().add(newTreeNode);
return;
}
treePlant(treeNode.getChildren(), newTreeNode);
}
}
treePlant方法,用于将新的TreeNode对象正确放入到TreeNode对象集合内。如果找到编号为父编号的对象,那么就将其放入到父TreeNode的children对象集合内,以表明这种关系。其实要想这个方法运行正常的话,得考虑几个问题,首先是传入的treeNodes对象也就是TreeNode对象集合必须存在顶级分类(在数据库存在数据的情况下),这也就是为什么在transform方法内要首先将父编号为空的放入treeNodes对象。其次就是层次关系的问题,假设有这样一种情况,衣服是顶级分类,其下有上衣、裤子等,这些是二级分类,再往下有夹克,这个属于三级分类,属于上衣分类。在这里只是举个例子,如果不用设计一个层次属性的话,会出现什么情况呢?在调用treePlant方法的时候,有可能会出现丢失分类数据的情况!衣服分类在传入之前就已经放入参数treeNodes对象中,这个不用怀疑,在执行到内部循环的时候,此时如果循环到的分类是夹克,它属于上衣这个分类,你此时不能保证上衣这个分类已经被放入treeNodes对象中,也就是说,夹克这个三级分类有可能丢失。这就是为什么要设计一个level属性即层次级别。经过transform方法内排序处理,层次数越小的排在最前面,也就不会出现丢失分类的情况。
那么此时,已经得到一个TreeNode对象的集合,这个集合正是前端需要的数据,里面的结构也正好符合相关框架的要求,在这里,我用的是ExtJS。
这个解决方案是本人亲自摸索出来并应用而没有问题。如有更好的解决方案,请联系我告知,感恩不尽!