目录:
- 概述
- 环境
- 代码示例
- 测试结果
[一]、概述
在系统中,经常会用到无限级递归的树形结构,比如菜单、组织机构管理、多级分类等等,一般是在同一个表中定义父子关系实现这种树形结构,本文主要讲述如何运用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
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/
请教下,PARENT_ID是需要在数据库中另加一个字段么
如果Hibernate 配置的是update 模式,它会自己创建相关字段,如果不是update或者create 那么需要自己把相应的表及字段(包括PARENT_ID这个字段)创建好