Hibernate註解實現單表遞歸樹形結構

目錄:javascript

  1. 概述
  2. 環境
  3. 代碼示例
  4. 測試結果

[一]、概述java

在系統中,常常會用到無限級遞歸的樹形結構,好比菜單、組織機構管理、多級分類等等,通常是在同一個表中定義父子關係實現這種樹形結構,本文主要講述如何運用hibernate全註解的方式實現這個功能。node

[二]、環境mysql

  • hibernate 4.1.2
  • java 1.6
  • mysql 5.1

[三]、代碼示例sql

第一步:建立Entity類,並添加註解實現關聯關係數據庫

ps: 主要是利用@ManyToOne 和 @OneToMany 配置在同一個Entity類中實現樹形遞歸的結構apache

TreeNode.java編程

1 package com.micmiu.hibernate.anno.entity;
2  
3 import java.util.LinkedHashSet;
4 import java.util.Set;
5  
6 import javax.persistence.CascadeType;
7 import javax.persistence.Column;
8 import javax.persistence.Entity;
9 import javax.persistence.FetchType;
10 import javax.persistence.GeneratedValue;
11 import javax.persistence.Id;
12 import javax.persistence.JoinColumn;
13 import javax.persistence.ManyToOne;
14 import javax.persistence.OneToMany;
15 import javax.persistence.Table;
16  
17 /**
18  * 樹形結構示例
19  *
20  * @author <a href="http://www.micmiu.com">Michael Sun</a>
21  */
22 @Entity
23 @Table(name = "DEMO_T_TREE_NODE")
24 public class TreeNode {
25  
26     public TreeNode() {
27     }
28  
29     public TreeNode(String name) {
30         this.name = name;
31     }
32  
33     private int id;
34  
35     private String name;
36     // 父節點
37     private TreeNode parent;
38     // 子節點
39     private Set<TreeNode> children = new LinkedHashSet<TreeNode>();
40  
41     @Id
42     @Column(name = "ID")
43     @GeneratedValue
44     public int getId() {
45         return id;
46     }
47  
48     @Column(name = "NAME", length = 20)
49     public String getName() {
50         return name;
51     }
52  
53     @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
54     @JoinColumn(name = "PARENT_ID")
55     public TreeNode getParent() {
56         return parent;
57     }
58  
59     @OneToMany(cascade = CascadeType.ALL, mappedBy = "parent", fetch = FetchType.EAGER)
60     public Set<TreeNode> getChildren() {
61         return children;
62     }
63  
64     public void setId(int id) {
65         this.id = id;
66     }
67  
68     public void setName(String name) {
69         this.name = name;
70     }
71  
72     public void setParent(TreeNode parent) {
73         this.parent = parent;
74     }
75  
76     public void setChildren(Set<TreeNode> children) {
77         this.children = children;
78     }
79 }

第二步:建立hibernate默認配置文件:session

hibernate.cfg.xmlapp

1 <?xml version='1.0' encoding='UTF-8'?>
2 <!DOCTYPE hibernate-configuration PUBLIC
3           "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
5  
6 <hibernate-configuration>
7  
8     <session-factory>
9  
10         <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
11         <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
12         <property name="connection.url">jdbc:mysql://localhost:3306/michaeldemo</property>
13         <property name="connection.username">root</property>
14         <property name="connection.password"></property>
15  
16         <property name="show_sql">true</property>
17         <property name="format_sql">true</property>
18  
19         <property name="current_session_context_class">thread</property>
20         <property name="hbm2ddl.auto">update</property>
21  
22         <mapping class="com.micmiu.hibernate.anno.entity.TreeNode" />
23     </session-factory>
24  
25 </hibernate-configuration>

第三步:建立測試文件:

HibernateAnnoTreeTest.java

1 package com.micmiu.hibernate;
2  
3 import org.hibernate.Session;
4 import org.hibernate.SessionFactory;
5 import org.hibernate.cfg.Configuration;
6 import org.hibernate.service.ServiceRegistry;
7 import org.hibernate.service.ServiceRegistryBuilder;
8 import org.junit.AfterClass;
9 import org.junit.BeforeClass;
10 import org.junit.Test;
11  
12 import com.micmiu.hibernate.anno.entity.TreeNode;
13  
14 /**
15  * 測試
16  *
17  * @author <a href="http://www.micmiu.com">Michael Sun</a>
18  */
19 public class HibernateAnnoTreeTest {
20  
21     private static SessionFactory sessionFactory;
22  
23     @BeforeClass
24     public static void beforeClass() {
25         Configuration configuration = new Configuration().configure();
26         ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
27                 .applySettings(configuration.getProperties())
28                 .buildServiceRegistry();
29         sessionFactory = configuration.buildSessionFactory(serviceRegistry);
30  
31     }
32  
33     @AfterClass
34     public static void afterClass() {
35         sessionFactory.close();
36     }
37  
38     @Test
39     public void testTreeCRUD() {
40         // 先運行添加測試
41         // testSave();
42  
43         // 讀取測試
44         // testRead();
45  
46         // 更新測試
47         testUpdate();
48  
49         // 刪除測試
50         // testDelete();
51     }
52  
53     public void testSave() {
54         System.out.println("========>測試添加 start <========");
55         Session session = sessionFactory.openSession();
56  
57         session.beginTransaction();
58         TreeNode rootNode = initData();
59         session.save(rootNode);
60         session.getTransaction().commit();
61         session.close();
62         System.out.println("========>測試添加 end <========");
63         // 讀取添加的數據
64         testRead();
65     }
66  
67     public void testRead() {
68         System.out.println("========>讀取 start <========");
69         Session session = sessionFactory.openSession();
70         session.beginTransaction();
71         System.out.println("-----> get root node:");
72         TreeNode rootNode = (TreeNode) session.get(TreeNode.class, 1);
73  
74         System.out.println("-----> 輸出樹形結構以下:");
75         printNode(rootNode, 0);
76  
77         session.getTransaction().commit();
78         session.close();
79         System.out.println("========>讀取 end <========");
80     }
81  
82     public void testUpdate() {
83         // 更新前讀取信息
84         testRead();
85         System.out.println("========>測試更新 start <========");
86         Session session = sessionFactory.openSession();
87         session.beginTransaction();
88  
89         System.out.println("---> 更新節點屬性");
90         TreeNode rootNode = (TreeNode) session.get(TreeNode.class, 1);
91         System.out.println("get root node:" + rootNode.getName()
92                 + " child size:" + rootNode.getChildren().size());
93         rootNode.setName(rootNode.getName() + "(個人blog)");
94  
95         TreeNode node_del = null;
96         for (TreeNode node : rootNode.getChildren()) {
97             if ("Hazel".equals(node.getName())) {
98                 node_del = node;
99             }
100         }
101         System.out.println("---> 刪除節點(包含子節點)");
102         System.out.println("delete node:" + node_del.getName() + " child size:"
103                 + node_del.getChildren().size());
104         node_del.setParent(null);
105         rootNode.getChildren().remove(node_del);
106         session.delete(node_del);
107  
108         System.out.println("---> 添加節點(包含子節點)");
109         TreeNode node_add = new TreeNode("企業應用");
110         node_add.setParent(rootNode);
111         rootNode.getChildren().add(node_add);
112  
113         TreeNode node_add_0 = new TreeNode("SNMP");
114         node_add_0.setParent(node_add);
115         node_add.getChildren().add(node_add_0);
116  
117         TreeNode node_add_1 = new TreeNode("SSO");
118         node_add_1.setParent(node_add);
119         node_add.getChildren().add(node_add_1);
120  
121         session.update(rootNode);
122  
123         System.out.println("---> 節點下添加子節點");
124         TreeNode node_update = (TreeNode) session.get(TreeNode.class, 6);
125         TreeNode node_child_add = new TreeNode("go(新增)");
126         System.out.println("append child node:" + node_child_add.getName()
127                 + " to parent node: " + node_update.getName());
128         node_child_add.setParent(node_update);
129         node_update.getChildren().add(node_child_add);
130  
131         System.out.println("---> 節點下刪除子節點");
132  
133         TreeNode node_child_del = node_update.getChildren().iterator().next();
134         System.out.println("delete node child :" + node_child_del.getName()
135                 + " from parent node: " + node_update.getName());
136         node_update.getChildren().remove(node_child_del);
137         node_child_del.setParent(null);
138         session.delete(node_child_del);
139  
140         session.update(node_update);
141  
142         session.getTransaction().commit();
143         session.close();
144         System.out.println("========>測試更新 end <========");
145         // 更新後讀取信息
146         testRead();
147     }
148  
149     public void testDelete() {
150         // 刪除前讀取信息
151         testRead();
152         System.out.println("========>測試刪除 start <========");
153         Session session = sessionFactory.openSession();
154         session.beginTransaction();
155         TreeNode node = (TreeNode) session.get(TreeNode.class, 6);
156         System.out.println("node:" + node.getName() + " child size:"
157                 + node.getChildren().size());
158         TreeNode childNode = node.getChildren().iterator().next();
159         childNode.setParent(null);
160         node.getChildren().remove(childNode);
161         session.delete(childNode);
162         System.out.println("delete node:" + childNode.getName()
163                 + " from parent:" + node.getName());
164  
165         session.update(node);
166         session.getTransaction().commit();
167         session.close();
168         System.out.println("========>測試刪除 end <========");
169         // 刪除後讀取信息
170         testRead();
171  
172     }
173  
174     /**
175      * 模擬測試數據
176      */
177     private TreeNode initData() {
178         TreeNode rootNode = new TreeNode("micmiu.com");
179  
180         // 一級
181         TreeNode node0 = new TreeNode("Michael");
182         node0.setParent(rootNode);
183         rootNode.getChildren().add(node0);
184  
185         // 二級
186         TreeNode node0_0 = new TreeNode("J2EE");
187         node0_0.setParent(node0);
188         node0.getChildren().add(node0_0);
189         // 二級
190         TreeNode node0_1 = new TreeNode("SOA");
191         node0_1.setParent(node0);
192         node0.getChildren().add(node0_1);
193         // 二級
194         TreeNode node0_2 = new TreeNode("NoSQL");
195         node0_2.setParent(node0);
196         node0.getChildren().add(node0_2);
197  
198         // 二級
199         TreeNode node0_3 = new TreeNode("編程語言");
200         node0_3.setParent(node0);
201         node0.getChildren().add(node0_3);
202  
203         // 三級
204         TreeNode node0_3_0 = new TreeNode("Java");
205         node0_3_0.setParent(node0_3);
206         node0_3.getChildren().add(node0_3_0);
207  
208         TreeNode node0_3_1 = new TreeNode("Groovy");
209         node0_3_1.setParent(node0_3);
210         node0_3.getChildren().add(node0_3_1);
211  
212         TreeNode node0_3_2 = new TreeNode("javascript");
213         node0_3_2.setParent(node0_3);
214         node0_3.getChildren().add(node0_3_2);
215  
216         // 一級
217         TreeNode node1 = new TreeNode("Hazel");
218         node1.setParent(rootNode);
219         rootNode.getChildren().add(node1);
220         // 二級
221         TreeNode node1_0 = new TreeNode("life");
222         node1_0.setParent(node1);
223         node1.getChildren().add(node1_0);
224         // 二級
225         TreeNode node1_1 = new TreeNode("美食");
226         node1_1.setParent(node1);
227         node1.getChildren().add(node1_1);
228         // 二級
229         TreeNode node1_2 = new TreeNode("旅遊");
230         node1_2.setParent(node1);
231         node1.getChildren().add(node1_2);
232  
233         return rootNode;
234     }
235  
236     private void printNode(TreeNode node, int level) {
237         String preStr = "";
238         for (int i = 0; i < level; i++) {
239             preStr += "|----";
240         }
241         System.out.println(preStr + node.getName());
242         for (TreeNode children : node.getChildren()) {
243             printNode(children, level + 1);
244         }
245     }
246  
247 }

第四步:建立日誌輸出配置文件:

log4j.properties

1 # Output pattern : date [thread] priority category - message
2 log4j.rootLogger=info, Console
3  
4 #Console
5 log4j.appender.Console=org.apache.log4j.ConsoleAppender
6 log4j.appender.Console.layout=org.apache.log4j.PatternLayout
7 log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n
8  
9 log4j.logger.org.hibernate.tool.hbm2ddl=debug
10 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 <========
-----> get root node:
-----> 輸出樹形結構以下:
micmiu.com
|----Michael
|----|----J2EE
|----|----SOA
|----|----NoSQL
|----|----編程語言
|----|----|----Java
|----|----|----Groovy
|----|----|----javascript
|----Hazel
|----|----life
|----|----美食
|----|----旅遊
========>讀取 end <========
========>測試更新 start <========
---> 更新節點屬性
get root node:micmiu.com child size:2
---> 刪除節點(包含子節點)
delete node:Hazel child size:3
---> 添加節點(包含子節點)
---> 節點下添加子節點
append child node:go(新增) to parent node: 編程語言
---> 節點下刪除子節點
delete node child :Java from parent node: 編程語言
========>測試更新 end <========
========>讀取 start <========
-----> get root node:
-----> 輸出樹形結構以下:
micmiu.com(個人blog)
|----Michael
|----|----J2EE
|----|----SOA
|----|----NoSQL
|----|----編程語言
|----|----|----Groovy
|----|----|----javascript
|----|----|----go(新增)
|----企業應用
|----|----SNMP
|----|----SSO
========>讀取 end <========

數據庫中查詢記錄以下:

本文介紹到此結束

相關文章
相關標籤/搜索