目录:
- 概述
- 环境
- 代码示例
- 测试结果
[一]、概述
在系统中,经常会用到无限级递归的树形结构,比如菜单、组织机构管理、多级分类等等,一般是在同一个表中定义父子关系实现这种树形结构,本文主要讲述如何运用hibernate全注解的方式实现这个功能。
[二]、环境
- hibernate 4.1.2
- java 1.6
- mysql 5.1
[三]、代码示例
第一步:创建Entity类,并添加注解实现关联关系
ps: 主要是利用@ManyToOne 和 @OneToMany 配置在同一个Entity类中实现树形递归的结构。
TreeNode.java
1 |
package com.micmiu.hibernate.anno.entity; |
3 |
import java.util.LinkedHashSet; |
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; |
23 |
@Table (name = "DEMO_T_TREE_NODE" ) |
24 |
public class TreeNode { |
29 |
public TreeNode(String name) { |
37 |
private TreeNode parent; |
39 |
private Set<TreeNode> children = new LinkedHashSet<TreeNode>(); |
48 |
@Column (name = "NAME" , length = 20 ) |
49 |
public String getName() { |
53 |
@ManyToOne (cascade = CascadeType.ALL, fetch = FetchType.EAGER) |
54 |
@JoinColumn (name = "PARENT_ID" ) |
55 |
public TreeNode getParent() { |
59 |
@OneToMany (cascade = CascadeType.ALL, mappedBy = "parent" , fetch = FetchType.EAGER) |
60 |
public Set<TreeNode> getChildren() { |
64 |
public void setId( int id) { |
68 |
public void setName(String name) { |
72 |
public void setParent(TreeNode parent) { |
76 |
public void setChildren(Set<TreeNode> children) { |
77 |
this .children = children; |
第二步:创建hibernate默认配置文件:
hibernate.cfg.xml
1 |
<? xml version = '1.0' encoding = 'UTF-8' ?> |
2 |
<!DOCTYPE hibernate-configuration PUBLIC |
3 |
"-//Hibernate/Hibernate Configuration DTD 3.0//EN" |
6 |
< hibernate-configuration > |
10 |
< property name = "dialect" >org.hibernate.dialect.MySQLDialect</ property > |
11 |
< property name = "connection.driver_class" >com.mysql.jdbc.Driver</ property > |
13 |
< property name = "connection.username" >root</ property > |
14 |
< property name = "connection.password" ></ property > |
16 |
< property name = "show_sql" >true</ property > |
17 |
< property name = "format_sql" >true</ property > |
19 |
< property name = "current_session_context_class" >thread</ property > |
20 |
< property name = "hbm2ddl.auto" >update</ property > |
22 |
< mapping class = "com.micmiu.hibernate.anno.entity.TreeNode" /> |
25 |
</ hibernate-configuration > |
第三步:创建测试文件:
HibernateAnnoTreeTest.java
1 |
package com.micmiu.hibernate; |
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; |
12 |
import com.micmiu.hibernate.anno.entity.TreeNode; |
19 |
public class HibernateAnnoTreeTest { |
21 |
private static SessionFactory sessionFactory; |
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); |
34 |
public static void afterClass() { |
35 |
sessionFactory.close(); |
39 |
public void testTreeCRUD() { |
53 |
public void testSave() { |
54 |
System.out.println( "========>测试添加 start <========" ); |
55 |
Session session = sessionFactory.openSession(); |
57 |
session.beginTransaction(); |
58 |
TreeNode rootNode = initData(); |
59 |
session.save(rootNode); |
60 |
session.getTransaction().commit(); |
62 |
System.out.println( "========>测试添加 end <========" ); |
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 ); |
74 |
System.out.println( "-----> 输出树形结构如下:" ); |
75 |
printNode(rootNode, 0 ); |
77 |
session.getTransaction().commit(); |
79 |
System.out.println( "========>读取 end <========" ); |
82 |
public void testUpdate() { |
85 |
System.out.println( "========>测试更新 start <========" ); |
86 |
Session session = sessionFactory.openSession(); |
87 |
session.beginTransaction(); |
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)" ); |
95 |
TreeNode node_del = null ; |
96 |
for (TreeNode node : rootNode.getChildren()) { |
97 |
if ( "Hazel" .equals(node.getName())) { |
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); |
108 |
System.out.println( "---> 添加节点(包含子节点)" ); |
109 |
TreeNode node_add = new TreeNode( "企业应用" ); |
110 |
node_add.setParent(rootNode); |
111 |
rootNode.getChildren().add(node_add); |
113 |
TreeNode node_add_0 = new TreeNode( "SNMP" ); |
114 |
node_add_0.setParent(node_add); |
115 |
node_add.getChildren().add(node_add_0); |
117 |
TreeNode node_add_1 = new TreeNode( "SSO" ); |
118 |
node_add_1.setParent(node_add); |
119 |
node_add.getChildren().add(node_add_1); |
121 |
session.update(rootNode); |
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); |
131 |
System.out.println( "---> 节点下删除子节点" ); |
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); |
140 |
session.update(node_update); |
142 |
session.getTransaction().commit(); |
144 |
System.out.println( "========>测试更新 end <========" ); |
149 |
public void testDelete() { |
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()); |
165 |
session.update(node); |
166 |
session.getTransaction().commit(); |
168 |
System.out.println( "========>测试删除 end <========" ); |
177 |
private TreeNode initData() { |
178 |
TreeNode rootNode = new TreeNode( "micmiu.com" ); |
181 |
TreeNode node0 = new TreeNode( "Michael" ); |
182 |
node0.setParent(rootNode); |
183 |
rootNode.getChildren().add(node0); |
186 |
TreeNode node0_0 = new TreeNode( "J2EE" ); |
187 |
node0_0.setParent(node0); |
188 |
node0.getChildren().add(node0_0); |
190 |
TreeNode node0_1 = new TreeNode( "SOA" ); |
191 |
node0_1.setParent(node0); |
192 |
node0.getChildren().add(node0_1); |
194 |
TreeNode node0_2 = new TreeNode( "NoSQL" ); |
195 |
node0_2.setParent(node0); |
196 |
node0.getChildren().add(node0_2); |
199 |
TreeNode node0_3 = new TreeNode( "编程语言" ); |
200 |
node0_3.setParent(node0); |
201 |
node0.getChildren().add(node0_3); |
204 |
TreeNode node0_3_0 = new TreeNode( "Java" ); |
205 |
node0_3_0.setParent(node0_3); |
206 |
node0_3.getChildren().add(node0_3_0); |
208 |
TreeNode node0_3_1 = new TreeNode( "Groovy" ); |
209 |
node0_3_1.setParent(node0_3); |
210 |
node0_3.getChildren().add(node0_3_1); |
212 |
TreeNode node0_3_2 = new TreeNode( "javascript" ); |
213 |
node0_3_2.setParent(node0_3); |
214 |
node0_3.getChildren().add(node0_3_2); |
217 |
TreeNode node1 = new TreeNode( "Hazel" ); |
218 |
node1.setParent(rootNode); |
219 |
rootNode.getChildren().add(node1); |
221 |
TreeNode node1_0 = new TreeNode( "life" ); |
222 |
node1_0.setParent(node1); |
223 |
node1.getChildren().add(node1_0); |
225 |
TreeNode node1_1 = new TreeNode( "美食" ); |
226 |
node1_1.setParent(node1); |
227 |
node1.getChildren().add(node1_1); |
229 |
TreeNode node1_2 = new TreeNode( "旅游" ); |
230 |
node1_2.setParent(node1); |
231 |
node1.getChildren().add(node1_2); |
236 |
private void printNode(TreeNode node, int level) { |
238 |
for ( int i = 0 ; i < level; i++) { |
241 |
System.out.println(preStr + node.getName()); |
242 |
for (TreeNode children : node.getChildren()) { |
243 |
printNode(children, level + 1 ); |
第四步:创建日志输出配置文件:
log4j.properties
1 |
# Output pattern : date [thread] priority category - message |
2 |
log4j.rootLogger=info, 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 |
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 <========
数据库中查询记录如下:
本文介绍到此结束