Hibernate注解实现单表递归树形结构

目录:

  1. 概述
  2. 环境
  3. 代码示例测试结果

[一]、概述

在系统中,经常会用到无限级递归的树形结构,比如菜单、组织机构管理、多级分类等等,一般是在同一个表中定义父子关系实现这种树形结构,本文主要讲述如何运用hibernate全注解的方式实现这个功能。

[二]、环境

  • hibernate 4.1.2
  • java 1.6
  • mysql 5.1

[三]、代码示例

第一步:创建Entity类,并添加注解实现关联关系

ps: 主要是利用@ManyToOne 和 @OneToMany 配置在同一个Entity类中实现树形递归的结构

TreeNode.java

package com.micmiu.hibernate.anno.entity;

import java.util.LinkedHashSet;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;

/**
 * 树形结构示例
 *
 * @author <a href="http://www.micmiu.com">Michael Sun</a>
 */
@Entity
@Table(name = "DEMO_T_TREE_NODE")
public class TreeNode {

	public TreeNode() {
	}

	public TreeNode(String name) {
		this.name = name;
	}

	private int id;

	private String name;
	// 父节点
	private TreeNode parent;
	// 子节点
	private Set<TreeNode> children = new LinkedHashSet<TreeNode>();

	@Id
	@Column(name = "ID")
	@GeneratedValue
	public int getId() {
		return id;
	}

	@Column(name = "NAME", length = 20)
	public String getName() {
		return name;
	}

	@ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
	@JoinColumn(name = "PARENT_ID")
	public TreeNode getParent() {
		return parent;
	}

	@OneToMany(cascade = CascadeType.ALL, mappedBy = "parent", fetch = FetchType.EAGER)
	public Set<TreeNode> getChildren() {
		return children;
	}

	public void setId(int id) {
		this.id = id;
	}

	public void setName(String name) {
		this.name = name;
	}

	public void setParent(TreeNode parent) {
		this.parent = parent;
	}

	public void setChildren(Set<TreeNode> children) {
		this.children = children;
	}
}

第二步:创建hibernate默认配置文件:

hibernate.cfg.xml

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
          "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
          "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

	<session-factory>

		<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
		<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
		<property name="connection.url">jdbc:mysql://localhost:3306/michaeldemo</property>
		<property name="connection.username">root</property>
		<property name="connection.password"></property>

		<property name="show_sql">true</property>
		<property name="format_sql">true</property>

		<property name="current_session_context_class">thread</property>
		<property name="hbm2ddl.auto">update</property>

		<mapping class="com.micmiu.hibernate.anno.entity.TreeNode" />
	</session-factory>

</hibernate-configuration>

第三步:创建测试文件:

HibernateAnnoTreeTest.java

package com.micmiu.hibernate;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

import com.micmiu.hibernate.anno.entity.TreeNode;

/**
 * 测试
 *
 * @author <a href="http://www.micmiu.com">Michael Sun</a>
 */
public class HibernateAnnoTreeTest {

	private static SessionFactory sessionFactory;

	@BeforeClass
	public static void beforeClass() {
		Configuration configuration = new Configuration().configure();
		ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
				.applySettings(configuration.getProperties())
				.buildServiceRegistry();
		sessionFactory = configuration.buildSessionFactory(serviceRegistry);

	}

	@AfterClass
	public static void afterClass() {
		sessionFactory.close();
	}

	@Test
	public void testTreeCRUD() {
		// 先运行添加测试
		// testSave();

		// 读取测试
		// testRead();

		// 更新测试
		testUpdate();

		// 删除测试
		// testDelete();
	}

	public void testSave() {
		System.out.println("========>测试添加 start <========");
		Session session = sessionFactory.openSession();

		session.beginTransaction();
		TreeNode rootNode = initData();
		session.save(rootNode);
		session.getTransaction().commit();
		session.close();
		System.out.println("========>测试添加 end <========");
		// 读取添加的数据
		testRead();
	}

	public void testRead() {
		System.out.println("========>读取 start <========");
		Session session = sessionFactory.openSession();
		session.beginTransaction();
		System.out.println("-----> get root node:");
		TreeNode rootNode = (TreeNode) session.get(TreeNode.class, 1);

		System.out.println("-----> 输出树形结构如下:");
		printNode(rootNode, 0);

		session.getTransaction().commit();
		session.close();
		System.out.println("========>读取 end <========");
	}

	public void testUpdate() {
		// 更新前读取信息
		testRead();
		System.out.println("========>测试更新 start <========");
		Session session = sessionFactory.openSession();
		session.beginTransaction();

		System.out.println("---> 更新节点属性");
		TreeNode rootNode = (TreeNode) session.get(TreeNode.class, 1);
		System.out.println("get root node:" + rootNode.getName()
				+ " child size:" + rootNode.getChildren().size());
		rootNode.setName(rootNode.getName() + "(我的blog)");

		TreeNode node_del = null;
		for (TreeNode node : rootNode.getChildren()) {
			if ("Hazel".equals(node.getName())) {
				node_del = node;
			}
		}
		System.out.println("---> 删除节点(包含子节点)");
		System.out.println("delete node:" + node_del.getName() + " child size:"
				+ node_del.getChildren().size());
		node_del.setParent(null);
		rootNode.getChildren().remove(node_del);
		session.delete(node_del);

		System.out.println("---> 添加节点(包含子节点)");
		TreeNode node_add = new TreeNode("企业应用");
		node_add.setParent(rootNode);
		rootNode.getChildren().add(node_add);

		TreeNode node_add_0 = new TreeNode("SNMP");
		node_add_0.setParent(node_add);
		node_add.getChildren().add(node_add_0);

		TreeNode node_add_1 = new TreeNode("SSO");
		node_add_1.setParent(node_add);
		node_add.getChildren().add(node_add_1);

		session.update(rootNode);

		System.out.println("---> 节点下添加子节点");
		TreeNode node_update = (TreeNode) session.get(TreeNode.class, 6);
		TreeNode node_child_add = new TreeNode("go(新增)");
		System.out.println("append child node:" + node_child_add.getName()
				+ " to parent node: " + node_update.getName());
		node_child_add.setParent(node_update);
		node_update.getChildren().add(node_child_add);

		System.out.println("---> 节点下删除子节点");

		TreeNode node_child_del = node_update.getChildren().iterator().next();
		System.out.println("delete node child :" + node_child_del.getName()
				+ " from parent node: " + node_update.getName());
		node_update.getChildren().remove(node_child_del);
		node_child_del.setParent(null);
		session.delete(node_child_del);

		session.update(node_update);

		session.getTransaction().commit();
		session.close();
		System.out.println("========>测试更新 end <========");
		// 更新后读取信息
		testRead();
	}

	public void testDelete() {
		// 删除前读取信息
		testRead();
		System.out.println("========>测试删除 start <========");
		Session session = sessionFactory.openSession();
		session.beginTransaction();
		TreeNode node = (TreeNode) session.get(TreeNode.class, 6);
		System.out.println("node:" + node.getName() + " child size:"
				+ node.getChildren().size());
		TreeNode childNode = node.getChildren().iterator().next();
		childNode.setParent(null);
		node.getChildren().remove(childNode);
		session.delete(childNode);
		System.out.println("delete node:" + childNode.getName()
				+ " from parent:" + node.getName());

		session.update(node);
		session.getTransaction().commit();
		session.close();
		System.out.println("========>测试删除 end <========");
		// 删除后读取信息
		testRead();

	}

	/**
	 * 模拟测试数据
	 */
	private TreeNode initData() {
		TreeNode rootNode = new TreeNode("micmiu.com");

		// 一级
		TreeNode node0 = new TreeNode("Michael");
		node0.setParent(rootNode);
		rootNode.getChildren().add(node0);

		// 二级
		TreeNode node0_0 = new TreeNode("J2EE");
		node0_0.setParent(node0);
		node0.getChildren().add(node0_0);
		// 二级
		TreeNode node0_1 = new TreeNode("SOA");
		node0_1.setParent(node0);
		node0.getChildren().add(node0_1);
		// 二级
		TreeNode node0_2 = new TreeNode("NoSQL");
		node0_2.setParent(node0);
		node0.getChildren().add(node0_2);

		// 二级
		TreeNode node0_3 = new TreeNode("编程语言");
		node0_3.setParent(node0);
		node0.getChildren().add(node0_3);

		// 三级
		TreeNode node0_3_0 = new TreeNode("Java");
		node0_3_0.setParent(node0_3);
		node0_3.getChildren().add(node0_3_0);

		TreeNode node0_3_1 = new TreeNode("Groovy");
		node0_3_1.setParent(node0_3);
		node0_3.getChildren().add(node0_3_1);

		TreeNode node0_3_2 = new TreeNode("javascript");
		node0_3_2.setParent(node0_3);
		node0_3.getChildren().add(node0_3_2);

		// 一级
		TreeNode node1 = new TreeNode("Hazel");
		node1.setParent(rootNode);
		rootNode.getChildren().add(node1);
		// 二级
		TreeNode node1_0 = new TreeNode("life");
		node1_0.setParent(node1);
		node1.getChildren().add(node1_0);
		// 二级
		TreeNode node1_1 = new TreeNode("美食");
		node1_1.setParent(node1);
		node1.getChildren().add(node1_1);
		// 二级
		TreeNode node1_2 = new TreeNode("旅游");
		node1_2.setParent(node1);
		node1.getChildren().add(node1_2);

		return rootNode;
	}

	private void printNode(TreeNode node, int level) {
		String preStr = "";
		for (int i = 0; i < level; i++) {
			preStr += "|----";
		}
		System.out.println(preStr + node.getName());
		for (TreeNode children : node.getChildren()) {
			printNode(children, level + 1);
		}
	}

}

第四步:创建日志输出配置文件:

log4j.properties

# Output pattern : date [thread] priority category - message
log4j.rootLogger=info, Console

#Console
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n

log4j.logger.org.hibernate.tool.hbm2ddl=debug
log4j.logger.org.hibernate.test=info

[四]、测试结果

测试添加方法,输出日志如下:

========>测试添加 start <========
========>测试添加 end <========
========>读取 start <========
-----> get root node:
-----> 输出树形结构如下:
micmiu.com
|----Michael
|----|----J2EE
|----|----SOA
|----|----NoSQL
|----|----编程语言
|----|----|----Java
|----|----|----Groovy
|----|----|----javascript
|----Hazel
|----|----life
|----|----美食
|----|----旅游
========>读取 end <========

 

[四]、测试结果

测试添加方法,输出日志如下:

========>测试添加 start <========
========>测试添加 end <========
========>读取 start <========
-----> get root node:
-----> 输出树形结构如下:
micmiu.com
|----Michael
|----|----J2EE
|----|----SOA
|----|----NoSQL
|----|----编程语言
|----|----|----Java
|----|----|----Groovy
|----|----|----javascript
|----Hazel
|----|----life
|----|----美食
|----|----旅游
========>读取 end <========

 --------------------------------------------------------------

public class NodeManage {
	
	private static NodeManage nodeManage= new NodeManage();
	
	private NodeManage(){}//因为要使用单例,所以将其构造方法私有化
	
	//向外提供一个接口
	public static NodeManage getInstanse(){
		return nodeManage;
	}

	/**
	 * 创建树
	 * @param filePath 需要创建树目录的根目录
	 */
	public void createNode(String dir) {
		Session session = null;
		
		try {
			session = HibernateUtils.getSession();
			session.beginTransaction();
			
			File root = new File(dir);
			//因为第一个节点无父节点,因为是null
			this.saveNode(root, session, null, 0);
			
			session.getTransaction().commit();
		} catch (HibernateException e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		} finally {
			HibernateUtils.closeSession(session);
		}
	}

	/**
	 * 保存节点对象至数据库
	 * @param file 节点所对应的文件
	 * @param session session
	 * @param parent 父节点
	 * @param level 级别
	 */
	public void saveNode(File file, Session session, Node parent, int level) {
		
		if (file == null || !file.exists()){
			return;
		}
		
		//如果是文件则返回true,则表示是叶子节点,否则为目录,非叶子节点
		boolean isLeaf = file.isFile();
		
		Node node = new Node();
		node.setName(file.getName());
		node.setLeaf(isLeaf);
		node.setLevel(level);
		node.setParent(parent);
		
		session.save(node);
		
		//进行循环迭代子目录
		File[] subFiles = file.listFiles();
		if (subFiles != null && subFiles.length > 0){
			for (int i = 0; i < subFiles.length ; i++){
				this.saveNode(subFiles[i], session, node, level + 1);
			}
		}
	}

	/**
	 * 输出树结构
	 * @param id
	 */
	public void printNodeById(int id) {

		Session session = null;
		
		try {
			session = HibernateUtils.getSession();
			session.beginTransaction();
			
			Node node = (Node)session.get(Node.class, 1);
			
			printNode(node);
			
			session.getTransaction().commit();
		} catch (HibernateException e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		} finally {
			HibernateUtils.closeSession(session);
		}		
	}

	
	private void printNode(Node node) {
		
		if (node == null){
			return;
		}
		
		int level = node.getLevel();
		
		if (level > 0){
			for (int i = 0; i < level; i++){
				System.out.print("  |");
			}
			System.out.print("--");				
		}
		System.out.println(node.getName() + (node.isLeaf() ? "" : "[" + node.getChildren().size() + "]"));
		
		Set children = node.getChildren();
		for (Iterator iter = children.iterator(); iter.hasNext(); ){
			Node child = (Node)iter.next();
			printNode(child);
		}
	}
}


 

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值