Hibernate註解實現單表遞迴樹形結構
阿新 • • 發佈:2019-02-08
目錄:
- 概述
- 環境
- 程式碼示例測試結果
[一]、概述
在系統中,經常會用到無限級遞迴的樹形結構,比如選單、組織機構管理、多級分類等等,一般是在同一個表中定義父子關係實現這種樹形結構,本文主要講述如何運用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);
}
}
}