hibernate之树状映射

提到树状映射,许多人肯定会头疼死了,因为看"树状"这俩字就肯定想到会跟数据结构打交道,而数据结构是本科阶段最重要也是最难学的一门专业课。说实话摩罗我《数据结构》这门课学得也不咋滴,花了点时间终于完成了hibernate之树状映射这个小程序。

首先我先定义了个公司组织类(Org),一个组织下面可以有多个子组织,但每个组织都只有一个上级组织,也就是传说中的一对多的关系,但这个关系是双向的。提到双向关系,我们第一件事就是在双向关系的两边都要设双向关联,另外为了防止产生冗余,我们要在其中的一方设mappedBy,至于设在哪方,等下在下面的程序中解释。

既然确定了这个组织类(Org)是双向关系的,那么该如何定义这个类呢?首先从数据库表设计的角度来考虑一下,每个组织下面都可能有多个子组织,而每个子组织对应的只有一个上级组织,这是典型的一对多关系,一个组织拥有它自己的id以及组织名字(name),这俩已经可以描述这个组织了,但怎么描述各个组织之间的关系呢?也就是说在表里我们还必须定义字段去提现关系。因为是一对多的关系,一对多映射关系有一个规律很重要,那就是必须是在“多”的这方设置“一”的这方的外键,在这里就是在下级组织中设置上级组织的外键,这样通过这个外键我们就可以找到这个组织对应的上级组织是谁。有的同学可能会疑惑了:为什么非要在“多”的这方设置设置“一”的这方的外键呢?而不是反过来呢?来看看下面这个例子就会明白为什么要这样设置了。

注:以下是数据库表里的错误设计方法:


GroupId


GroupName


UserId


1


Group_1


1


1


Group_1


2


UserId


UserName


1


moluo


2


xingzhe

一个组(Group)里可以有多个用户(User),这是典型的一对多关系,如果在Group(也就是在“一”的这方)设置User(“多“的这方)的外键,那么表中红色字体部分的信息是不是产生了冗余?这只产生了两条记录的冗余,还可以接受。但如果Group表有很多个字段,并且有很多条记录呢?这样设计产生的冗余就大得夸张了,所以必须在“多”的这方设置“一”的这方的外键。

解释完数据库里表的设计方法,再来讲讲程序里Org这个类该如何定义。

首先Org这个类是一个组织,从面向对象的角度来考虑,它既有上级组织,也有下级组织,当然id和name这俩属性是必不可少的。另外更重要的是它的上级组织以及下级组织实际上也是一个组织,于是我就定义出了下面这个Org类:

package com.hibernate.model;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.CascadeType;
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;
@Entity
@Table(name="_Organization")
public class Org {
private int id;
private String name;
private Set<Org> children = new HashSet<Org>();
private Org parent;
@Id
@GeneratedValue
public int getId() {
	return id;
}
public void setId(int id) {
	this.id = id;
}
public String getName() {
	return name;
}
public void setName(String name) {
	this.name = name;
}
@OneToMany(mappedBy="parent",cascade=CascadeType.ALL,fetch=FetchType.EAGER)
public Set<Org> getChildren() {
	return children;
}
public void setChildren(Set<Org> children) {
	this.children = children;
}
@ManyToOne
@JoinColumn(name="parent_id")
public Org getParent() {
	return parent;
}
public void setParent(Org parent) {
	this.parent = parent;
}

}

简要解释一下,首先id自增就不解释了,解释一下mappedBy,mappedBy是用来定义类与类之间的关系的,如果类与类是单向关系,那么可以不用定义;如果是双向关系,那么就必须定义mappedBy,mappedBy表示外键在”对方“已经设置过了,这里不用再设置了,如果没有设置mappedBy,就很可能会产生数据的不一致性。另外mappedBy的值应该设成"多"方对应的class里”一“方的变量名,实际上更确切的讲是应该设置成"一"方变量名对应的getXXX方法里那个XXX,因为极少数情况下getXXX()和对应的变量名不一致(当然这是一种极不提倡的做法)。

再来解释一下cascade,该属性定义的是类与类之间的级联关系,定义的级联关系将被容器视为将当前类对象以及与之关联的类对象采取相同的操作,而且这种级联关系是递归的。cascade属性值有:ALL:表示所有情况都进行相同操作,即save、update、delete;PERSIST:这个表示在保存时采取相同的操作,MERGE是JPA的官方叫法,实际上就跟sava()一样;REMOVE:这个表示级联删除,实际上跟delete()方法一样;REFRESH:级联刷新。

接着解释一下fetch:fetch是读操作,它有俩属性,LAZY:表示只读取当前对象,关联对象不读;EAGER:当前对象被读取的时候,关联对象也会被读取,而且这种级联关系是递归的。

@OneToMany:表示当前这个类对象(实体)对应getXXX()方法表示的属性XXX对应的对象(实体)是一对多的关系。

@ManyToOne:表示当前这个类对象(实体)对应getXXX()方法表示的属性XXX对应的对象(实体)是多对一的关系。

@JoinColumn:用来注释表中的字段名字。

最后再来看看JUnit Test Case:

package com.hibernate.model;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

public class TreeTest {
	public static SessionFactory sf = null;
	@BeforeClass
	public static void beforeClass(){
		try{
			sf = new AnnotationConfiguration().configure().buildSessionFactory();
		}
		catch(Exception e) {
			e.printStackTrace();
		}
		finally{
		}
	}

	@Test
	public void testSave() {
		Org o = new Org();
		o.setName("总公司");
		Org o1 = new Org();
		o1.setName("分公司1");
		Org o2 = new Org();
		o2.setName("分公司2");
		Org o11 = new Org();
		o11.setName("分公司1下的部门1");
		Org o12 = new Org();
		o12.setName("分公司1下的部门2");
		Org o21 = new Org();
		o21.setName("分公司2下的部门1");
		o.getChildren().add(o1);
		o.getChildren().add(o2);
		o1.getChildren().add(o11);
		o1.getChildren().add(o12);
		o12.getChildren().add(o21);
		o1.setParent(o);
		o2.setParent(o);
		o11.setParent(o1);
		o12.setParent(o1);
		o21.setParent(o2);
		Session session =  sf.getCurrentSession();
		session.beginTransaction();
		session.save(o);
		session.getTransaction().commit();
	}

	@Test
	public void testLoad() {
		testSave();
		Session session =  sf.getCurrentSession();
		session.beginTransaction();
		Org o = (Org)session.load(Org.class, 1);
		print(o,0);
		session.getTransaction().commit();
	}

	private void print(Org o,int level) {
		String preStr = "";
		for (int i=0; i<level; i++) {
			preStr += "_";
		}
		System.out.println(preStr + o.getName());
		for (Org child:o.getChildren()) {
			print(child,level+1);
		}
	}

	@Test
	public void testSchemaExport(){
		new SchemaExport(new AnnotationConfiguration().configure()).create(false, true);
	}

	@AfterClass
	public static void afterClass(){
		sf.close();
	}

}

里面定义了个print()方法,里面那个level是为了使打印结果呈现树状结构

打印结果:

总公司
_分公司2
__分公司2下的部门1
_分公司1
__分公司1下的部门2
__分公司1下的部门1

后记:写这篇文章花的时间比我写这个程序花的时间要多许多,希望各位看客如果要转载的时候尊重一下摩罗我的劳动成果,注明原文链接并且不要删除我的二维码,谢谢!

欢迎关注行者摩罗微信公众号(xingzhemoluo),共同交流编程经验,扫描下方二维码即可;

时间: 2024-10-13 03:04:18

hibernate之树状映射的相关文章

Hibernate 树状映射

示例: 类Org表示组织机构,是一个典型的树状结构数据,其属性包括: id,name,children,parent 要将Org映射到数据库中,对parent作多对一的映射,对children作一对多的映射. 我们可以通过三张表来解释其关系 代码实现: 1.建Org实体类 @Entity public class Org { private int id; private String name; private Set<Org> childen = new HashSet<Org>

Hibernate——树状映射

树状映射 假设: 一张表的结构如下: id parent_id name 公司组织架构即树状映射 范例: package com.zgy.hibernate.model; import java.util.HashSet; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.FetchType; import ja

树状映射

Node类: package com.bjsxt.hibernate.树状映射; import java.util.Set; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import java

Hibernate学习笔记_关系映射_树状结构练习

1         树状结构的设计(至关重要) a) 在同—个类中使用One2Many和Many20ne @Entity public class Org { private int id; private String name; private Set<Org> children=new HashSet<Org>(); private Org parent; @Id @GeneratedValue public int getId() { return id; } public

《Hibernate学习笔记十一》:树状结构设计

<Hibernate学习笔记十一>:树状结构设计 这是马士兵老师讲解Hibernate的一个作业题,树状结构设计,这是一个比较典型的例子,因此有必要写篇博文记录下. 树状结构的设计,它是在同一个类中使用了多对一(ManyToOne)和一对多(OneToMany). 在完成这个题目我们应该按照如下的步骤进行: 1.先思考数据库的模型应该是什么样的?? 数据库中的模型应该如下:即存在id p_id 2.思考面向对象的模型,及如何来进行映射??? 根据数据库中表的特点,对象应该有id name;由于

系统管理模块_部门管理_设计(映射)本模块中的所有实体并总结设计实体的技巧_懒加载异常问题_树状结构

系统管理模块_部门管理_设计本模块中的所有实体并总结设计实体的技巧 设计实体流程 1,有几个实体? 一般是一组增删改查对应一个实体. 2,实体之间有什么关系? 一般是页面引用了其他的实体时,就表示与这个实体有关联关系. 3,每个实体中都有什么属性? 1,主键.推荐使用代理主键 2,关联关系属性.在类图中,关联关系是一条线,有两端,每一端对应一个表达此关联关系的属性.有几个端指向本类,本类中就有几个关联关系属性. 3,一般属性.分析所有有关的页面,找出表单中要填写的或是在显示页面中要显示的信息等.

作业-树状结构设计

作业:树状结构的设计在同一个类中使用OneToMany和ManyToOne 首先想数据库的模型,再想面向对象的模型,然后再来想怎么去做映射,做完映射在想怎么去做CRUD. 1.首先设计数据库的模型应该是Id.parent_Id.单一父亲,好多孩子这就是一棵树. 数据库首先存在的属性有:Id<int> parent_Id<int> name<String> 例子:ID   Parent_ID   公司1       null     分公司12       1      

OA项目11:部门列表树状显示功能及其他代码优化

首注:本学习教程为传智播客汤阳光讲师所公布的免费OA项目视频我的文字版实践笔记,本人用此来加强巩固自己开发知识,如有网友转载,请注明.谢谢. 一 使用递归使部门列表树状显示: 1.写一个工具类,实现通过顶级部门查询所有,具体如下: 1 package cn.clear.oa.util; 2 3 import java.util.ArrayList; 4 import java.util.Collection; 5 import java.util.List; 6 7 import cn.clea

浅析树状数组(二叉索引树)及一些模板

树状数组 动态连续和查询问题.给定一个n个元素的数组a1.a2.……,an,设计一个数据结构,支持以下两种操作:1.add(x,d):让ax增加d;2.query(l,r):计算al+al+1+…+ar 如何让query和add都能快速完成呢?方法有很多,这里介绍的便是树状数组.为此我们先介绍lowbit. 对于正整数x,我们定义lowbit(x)为x的二进制表达式中最右边的1所对应的值(而不是这个比特的序号).比如,38288的二进制1001010110010000,所以lowbit(3828