目录:
- 概述
- 环境
- 代码示例测试结果
[一]、概述
在系统中,经常会用到无限级递归的树形结构,比如菜单、组织机构管理、多级分类等等,一般是在同一个表中定义父子关系实现这种树形结构,本文主要讲述如何运用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);
- }
- }
- }