举个例子,一个班级和班里学生的关系可以用如下这幅关系图表示:
从Classes的角度来看,1个班级里有多个学生,关联关系为1:n,从对象到表结构的映射关系则为:
注意:在student表中有一个classid字段,原理:先向学生表中插入学生基本信息,执行insert操作,classid字段为空,然后向class表中插入数据,同时update student这张表,当然只要进行update操作的one-to-many操作,绝对是单向的一对多关联映射,下面介绍单向一对多映射:
单向one-to-many映射:
一、代码搭建映射
表结构原理图:
实质上,同多对一映射的原理,将外键加入到多的一端,仅仅看问题的角度不同。 实体类: (1)Student
public class Student {
private int id;
private String name;
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;
}
}
(2)Classes
public class Classes {
private int id;
private String name;
//一对多在one端,加入set集合
private Set students;
public Set getStudents() {
return students;
}
public void setStudents(Set students) {
this.students = students;
}
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;
}
}
可见,在对应的实体类当中,classes类中加入set集合,切记不是HashSet,因为Set支持延迟加载。
xml配置映射文件
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.bjpowernode.hibernate.Classes" table="t_classes">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<set name="students">
<key column="classesid"/> <!-- 关系维护在one的这端 -->
<one-to-many class="com.bjpowernode.hibernate.Sutdent"/>
</set>
</class>
</hibernate-mapping>
如上,仅仅是对one端的classes类进行映射配置,除去"id"和"name"之外,观察<set>标签
:
<set name="students">
<key column="classesid"/> <!-- 关系维护在one的这端 -->
<one-to-many class="com.bjpowernode.hibernate.Sutdent"/>
</set>
name属性说明了要关联的表结构为stud
ents,其中子标签<key>作为维护关系的”要害”,即将名为“classesid”的字段存到了students表结构下,成为其外键!!!<one-to-many>标签,则关联了实体中的Student类,这都是套路,找到并总结出规律记住即可。 至此,单向"one-to-many"映射的结构已经实现。
二、实现one-to-many存储与加载
public class One2ManyTest extends TestCase{
public void testSave1(){
Session session = null;
try{
session = HibernateUtils.getSession();
session.beginTransaction();
Student student1 = new Student();
Student student2 = new Student();
student1.setName("张三");
student2.setName("李四");
//只有先分别save了studnet1和2,再save class的时候,不会报错
session.save(student1);
session.save(student2);
Classes classes = new Classes();
classes.setName("提高班");
Set students = new HashSet();
students.add(student1);
students.add(student2);
classes.setStudents(students);
//可以成功的保存数据,但是会发出多余的update语句,来维持关系
session.save(classes);
session.getTransaction().commit();
}catch(Exception e){
e.printStackTrace();
session.getTransaction().rollback();
}finally{
HibernateUtils.closeSession(session);
}
}
}
如上,是对该one-to-many结构实现存储的方法,因为从one端,即classes的角度进行维护,需要先将两个student进行session.save()保存,
否则TransientObjectException提示就会报出,执行代码,成功向Students表和Classes表中插入了对应数据。
public void testLoad1(){
Session session = null;
try{
session = HibernateUtils.getSession();
session.beginTransaction();
Classes classes = (Classes)session.load(Classes.class, 1);
System.out.println("Classes.name=" + classes.getName());
Set students = classes.getStudents();
for(Iterator iter = students.iterator(); iter.hasNext();){
Student student = (Student)iter.next();
System.out.println("student.name=" + student.getName());
}
session.getTransaction().commit();
}catch(Exception e){
e.printStackTrace();
session.getTransaction().rollback();
}finally{
HibernateUtils.closeSession(session);
}
}
如上,从Class的角度取出id为1的数据,通过classes.getStudents()获取对应的Student信息,并通过迭代器将其进行打印。 缺陷: 1、因为Students端不知道Classes端的存在,在保存Student的时候关系字段classesid是为null的,如果将该关系字段设置为非空,则将无法保存数据。 2、因为Student不维护关系,而Classes维护关系,Classes就会发出多余的update语句,保证Classes和Student有关系,这样加载Classes的时候才可以把该Classes对应的学生加载上来。
双向one-to-many映射:
为了避免单向one-to-many关联映射的缺陷,使用双向关联可以解决该问题,如图:
即从classes能够拿到students,从students可以反向拿到classes
(1)修改studnet的配置文件
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.bjpowernode.hibernate.Classes" table="t_classes">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<!-- 通过别名,使得实体中映射的classes与one对many的表字典classesid一致 -->
<many-to-one name="classes" column="classesid"/>
</class>
</hibernate-mapping>
不要奇怪,这里出现了一个<many-to-one>标签,因为是从反向进行观察,想要双向打通,就要这样做,而且column别名的设置,要和在one端设置的别名一样,此处设置为“classesid”。 另外,对于one端可以进行翻转设置,如下:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.bjpowernode.hibernate.Classes" table="t_classes">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<!-- 添加inverse=“true”之后,one端将不再维护关系 -->
<set name="students" inverse="true">
<key column="classesid"/> <!-- 关系维护在one的这 -->
<one-to-many class="com.bjpowernode.hibernate.Sutdent"/>
</set>
</class>
</hibernate-mapping>
set标签中"inverse"属性设置为"true"之后,one端将不再维护关系,此时对于存储方法就要这样子写:
public void testSave2(){
Session session = null;
try{
session = HibernateUtils.getSession();
session.beginTransaction();
Classes classes = new Classes();
classes.setName("提高班");
session.save(classes);
Student student1 = new Student();
Student student2 = new Student();
student1.setName("张三");
student2.setName("李四");
//从students端的角度来维护
session.save(student1);
session.save(student2);
session.getTransaction().commit();
}catch(Exception e){
e.printStackTrace();
session.getTransaction().rollback();
}finally{
HibernateUtils.closeSession(session);
}
}
此时,维护关系的一端即为many端,session.save(classes)的时候,不会向Students表中添加classesid记录了,即从one端执行save方法,student表中,classid字段此时为null值。
对比one-to-many与many-to-one
从维护关系上来看:
多对一维护关系:多指向一的关系,如果维护了多指向一的关系,那么加载多的时候会把一加载上来。
一对多维护关系:一指向多的关系,如果维护了一指向多的关系,那么加载一的时候会把多加载上来。
That's all.
转载请注明原文地址: https://ju.6miu.com/read-650110.html