浏览 5209 次
精华帖 (1) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2008-05-05
Rails生成Ext Tree,结合以前的Java项目经验,提出了我的一些想法。
最近在自学RoR,看到了rainlife的帖子
以下的文字和rainlife的帖子Rails生成Ext Tree中我的回复大致相同,只是以SSH架构的Java代码重新实现了原帖中的内容。 我的疑问主要是在数据库的设计上,对于其中lft和rgt字段的设计感觉不好。当然我刚刚开始学习RoR,其中也许有很完备的解决方案我不知道,仅在此提出我的看法而以,如有异议,欢迎批评指正。 维护一个树型结构的类型列表,如下所示: Root |- Child 1 | |- Child 1.1 | |- Child 1.2 |- Child 2 |- Child 2.1 |- Child 2.2 原帖中表示树状节点的Category类中有lft和rgt这两个字段看上去是用来定义一个类型边界的。如果需要查找某个特定类型及其子类的话,则先查找该类型的lft和rgt,然后再 (lft > ? and rgt < ?) 获得其子类。 这种设计在数据结构完备的情况下能准确的统计出所有的子类。但是我要动态的增加Child子类,或者改变Child的隶属关系的时候,就需要对数据库表中所有数据的lft和rgt做出调整。比如我要在Child 1中增加一个Child 1.3。相应的 Root、Child 1、Child 2、Child 2.1、Child 2.2 的lft和rgt都要做相应的变化。 也许acts_as_nested_set可以通过先 delete from category; 后 insert into values(?,?,?,?); 的方式进行全类的维护,但是如果别的类有对Category的引用(即外键)。这样的隶属关系不是会产生混乱了吗?况且如果数据库真的建立了外键的话,也不允许 delete from category; 操作的。 所以我的考虑是用一个level字段代替lft和rgt字段,level字段维护着Category实例的层级关系。 例如,将root的level定义为1(这个在数据库或程序中可配),那么Child 1的level就为1|${id}(其中'|'为Level分层标记,${id}表示当前数据Id或其它可唯一标识的字段值),假定为1|2,同理Child 1.1的level就是1|2|3。 类结构如下所示:使用的是annotation的hibernate @Entity public class Category implements Serializable { /** Level分层标记 */ public static final String LEVEL_SPLIT = "|"; @Id @GeneratedValue private Integer id; /** 名称 */ private String name; /** level */ private String level; /** 删除标记 */ private Boolean delFlag; /** 下级的类别 */ @OneToMany(fetch = FetchType.LAZY, mappedBy="parent") @OrderBy("id") private List<Category> children = new ArrayList<Category>(); /** 上级的类别 */ @ManyToOne @JoinColumn(name = "category_id") private Category parent; // 省略所有 getter/setter 方法... } 维护后数据库中的数据如下: 查找某个特定类型及其子类,只需要获得当前类型的level值,然后查找 (level like 'xxxx%')即可。如下所示: Rails中需要重写Category中类似find的方法。 public List<Category> getCategoryList(Integer startId) { Category root = crudDao.get(Category.class, startId); // crudDao继承HibernateDaoSupport,并封装了HibernateTemplate的操作,下同 String hql = "from Category where level like ? order by id desc"; return crudDao.query(hql, root.getLevel + Category.LEVEL_SPLIT + "%"); } 新增或修改Category的方法。则需要先查找当前父类,再重新维护当前类的level属性值即可。如下所示: Rails中需要重写Category中的add_child方法 public void saveCategory(Category category) { Category parent = category.getParent(); // 判断Category的父类有没有设置 if (parent == null || parent.getId() == null || parent.getId() == 0) { throw new ParentNotFoundException(Category.class); } // 重新设置父类关系 parent = crudDao.get(Category.class, parent.getId()); // crudDao继承HibernateDaoSupport,并封装了HibernateTemplate的操作,下同 category.setParent(parent); if (category.getId() == null || category.getId() == 0) { crudDao.save(category); category.setLevel(parent.getLevel() + Category.LEVEL_SPLIT + category.getId()); crudDao.update(category); } else { Category oldCategory = crudDao.get(Category.class, category.getId()); BeanUtils.copyProperties(category, oldCategory, new String[]{"id", "children"}); oldCategory.setLevel(parent.getLevel() + Category.LEVEL_SPLIT + oldCategory.getId()); crudDao.update(oldCategory); } } 使用Rails的ActiveRecord,以上代码可以写的更简练 页面显示,我使用的是纯javascript的dtree。代码如下所示: <body class="dtree"> <p><a href="javascript:categoryTree.openAll();">open all</a> | <a href="javascript:categoryTree.closeAll();">close all</a></p> <script type="text/javascript"> var dtreeImgPath = "${ctx}/script/dtree/img/"; categoryTree = new dTree('categoryTree'); <c:forEach var="category" items="${list}"> <c:choose> <c:when test="${category.parent == null or category.parent == null or category.parent.id == 0}"> categoryTree.add(${category.id},-1,'${category.name }',"javascript:doSelect('${category.id}','${category.name }')"); </c:when> <c:otherwise> categoryTree.add(${category.id},${category.parent.id},'${category.name }',"javascript:doSelect('${category.id}','${category.name }')"); </c:otherwise> </c:choose> </c:forEach> document.write(categoryTree); </script> </body> 原帖中是用Ext来实现视图的现实。我只需要在Struts2中使用JSONUtils实现一个Result,使得Action返回一个JSON对象,就可以在JSP中用Ext来显示树型结构的列表 显示的效果如下: 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2008-05-05
恩 不错 用struts-menu 也不错
|
|
返回顶楼 | |
发表时间:2008-05-15
能不能把所有的源代码发上来呢,不知道你页面上<c:forEach var="category" items="${list}">中的list是怎么来的,是下面的代码中查出来的吗:
String hql = "from Category where level like ? order by id desc"; return crudDao.query(hql, root.getLevel + Category.LEVEL_SPLIT + "%"); 但我觉得不对啊,这会把子树的子树也查出来的啊,能否解释一下呢,再好能发个完整的源代码上来。谢谢。 |
|
返回顶楼 | |
发表时间:2008-05-15
Very Good.
|
|
返回顶楼 | |
发表时间:2008-05-21
ManyToMany定义自关联间接子节点和间接父结点也能办到
|
|
返回顶楼 | |
发表时间:2008-05-21
rennuoting:不知道你页面上<c:forEach var="category" items="${list}">中的list是怎么来的:
crudDao.query(hql, root.getLevel + Category.LEVEL_SPLIT + "%"); |
|
返回顶楼 | |
发表时间:2008-05-21
rennuoting:不知道你页面上<c:forEach var="category" items="${list}">中的list是怎么来的:
weijiang8410 2 小时前:crudDao.query(hql, root.getLevel + Category.LEVEL_SPLIT + "%"); 但我觉得不对啊,这会把子树的子树也查出来的啊,能否解释一下呢 |
|
返回顶楼 | |