`
bocar
  • 浏览: 5284 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

使用Spring和Hibernate实现树型结构列表

阅读更多
最近在自学RoR,看到了rainlife的帖子Rails生成Ext Tree,结合以前的Java项目经验,提出了我的一些想法。
以下的文字和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来显示树型结构的列表

显示的效果如下:
  • 描述: Category 数据图
  • 大小: 14 KB
  • 描述: Category 显示效果图
  • 大小: 9.1 KB
分享到:
评论
7 楼 liujiew 2008-06-12  
不错哦!
6 楼 rennuoting 2008-05-21  
rennuoting:不知道你页面上<c:forEach var="category" items="${list}">中的list是怎么来的:
weijiang8410  2 小时前:crudDao.query(hql, root.getLevel + Category.LEVEL_SPLIT + "%");
但我觉得不对啊,这会把子树的子树也查出来的啊,能否解释一下呢
5 楼 weijiang8410 2008-05-21  
rennuoting:不知道你页面上<c:forEach var="category" items="${list}">中的list是怎么来的:
crudDao.query(hql, root.getLevel + Category.LEVEL_SPLIT + "%");
4 楼 maming2000 2008-05-21  
ManyToMany定义自关联间接子节点和间接父结点也能办到
3 楼 rainlife 2008-05-15  
Very Good.
2 楼 rennuoting 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 + "%");

但我觉得不对啊,这会把子树的子树也查出来的啊,能否解释一下呢,再好能发个完整的源代码上来。谢谢。
1 楼 isky 2008-05-05  
恩 不错  用struts-menu 也不错

相关推荐

    ext+struts2+spring+hibernate 树型菜单

    "ext+Struts2+Spring+Hibernate 树型菜单"是一个典型的Java Web开发框架组合,用于创建具有树状结构的用户界面,通常用于数据的层级展示,比如部门结构、文件目录等。下面将详细解释这些技术和如何协同工作。 **...

    ext+spring+hibernat+struts 人力资源管理系统

    在系统设计时,通常会使用Hibernate的配置文件来定义数据表结构,使得数据库操作与业务逻辑分离,提高代码的可读性和可扩展性。 综合以上技术,这个人力资源管理系统具有以下特点: 1. 前端采用EXT,提供美观的用户...

    JSP与无限级分类树型菜单

    在IT领域,尤其是在Web开发中,构建一个无限级分类树型菜单是一项常见的需求。这主要应用于网站导航、后台管理系统以及大型...在实际项目中,还可以结合Spring MVC、Hibernate等框架,进一步提升开发效率和代码质量。

    zk的增删改查的例子

    在这个例子中,可能使用Spring管理Zookeeper和Hibernate的配置,实现数据层的统一管理。 【标签】"zk"表明主要内容与Zookeeper相关。Zookeeper是由Apache软件基金会开发的一个开源项目,它是一个分布式的、开放源码...

    J_HI 平台 及 开发文档

    - **关键字、保留字列表**:列出开发中应避免使用的关键字和保留字。 9. **相关资料附录** - **C3P0连接池配置描述**:详细说明了C3P0连接池的配置方法。 - **Acgei与配置文件相关的类说明**:解释了与配置文件...

    E3tree开发文档

    在实际开发中,你需要根据项目需求选择合适的树型结构和配置相应的属性,同时注意版本兼容性问题,确保所有依赖库(如E3-Tree.jar、E3-TemplateEngine.jar等)的版本与E3.Tree相匹配。通过阅读E3.Tree的参考手册,...

    xalan.jar,serializer.jar,jdom.jar

    在压缩包列表中提到的"DWR+SSH报错所用包"可能意味着这些库是为了解决在使用DWR框架时遇到的问题,特别是与SSH(Spring Security和Struts Hibernate)集成时可能出现的错误。SSH是企业级Java开发中的常用组合,...

    DOM4J 解析XML 添加 删除 修改

    此外,DOM4J还与其他Java工具库(如Spring、Hibernate等)有很好的集成,便于在实际项目中使用。 博客中可能还会介绍DOM4J的具体示例代码,如`dom4jCode.txt`文件所示,它可能包含创建XML文档、解析XML、添加元素、...

    ExtJs使用文档

    - **整体架构:** 使用 Spring.NET + NHibernate + ASP.NET MVC + ExtJs 构建。 - **架构特点:** - **松耦合性:** 各个层之间通过接口进行通信。 - **可扩展性:** 易于维护和升级。 - **面向对象设计:** 通过...

    javase相关知识点

    3. 树型表结构:通常需要id(主键)、parent_id(父节点id)字段,通过递归查询构建树形结构。 4. SQL查询:SELECT Sname, SUM(Ccredit) FROM Student JOIN SC ON Student.Sno = SC.Sno JOIN Course ON SC.Cno = ...

    dom4j-1.6.1jar包

    DOM是一种树型结构,可以将整个XML文档加载到内存中,方便进行查询和修改。 2. **简单可扩展标记语言 (SAX)**:对于大型XML文档,DOM解析可能会消耗大量内存。因此,dom4j也支持SAX解析,它通过事件回调机制处理XML...

    JavaEye论坛热点月报 总第7期

    2. **AJAX可视化编辑器** - 报告中提到了一个由用户分享的AJAX可视化编辑器项目,该编辑器拥有40多个图形界面控件,如标签、对话框、树型表格和时间线等,展示了AJAX在构建复杂交互式界面方面的潜力。 3. **Shoes**...

Global site tag (gtag.js) - Google Analytics