Hibernate注解实现单表递归树形结构
目录:
- 概述
- 环境
- 代码示例
- 测试结果
[一]、概述
在系统中,经常会用到无限级递归的树形结构,比如菜单、组织机构管理、多级分类等等,一般是在同一个表中定义父子关系实现这种树形结构,本文主要讲述如何运用hibernate全注解的方式实现这个功能。
[二]、环境
- hibernate 4.1.2
- java 1.6
- mysql 5.1
[三]、代码示例
第一步:创建Entity类,并添加注解实现关联关系
ps: 主要是利用@ManyToOne 和 @OneToMany 配置在同一个Entity类中实现树形递归的结构。
TreeNode.java
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
|
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
XHTML
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
<?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
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
|
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
|
1
2
3
4
5
6
7
8
9
10
|
# 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
|
[四]、测试结果
测试添加方法,输出日志如下:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
========>测试添加 start <========
========>测试添加 end <========
========>读取 start <========
-----> get root node:
-----> 输出树形结构如下:
micmiu.com
|----Michael
|----|----J2EE
|----|----SOA
|----|----NoSQL
|----|----编程语言
|----|----|----Java
|----|----|----Groovy
|----|----|----javascript
|----Hazel
|----|----life
|----|----美食
|----|----旅游
========>读取 end <========
|
数据库中查询记录如下:
再运行测试程序中的更新方法,输出日志如下:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
========>读取 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 <========
|
数据库中查询记录如下:
本文介绍到此结束@Michael Sun.
原创文章,转载请注明: 转载自micmiu – 软件开发+生活点滴[ http://www.micmiu.com/ ]
本文链接地址: http://www.micmiu.com/j2ee/hibernate/hibernate-anno-tree/


浙公网安备 33010602011771号