“一對多”,顧名思義,是由“一”的一端加載“多”的一端,關(guān)系交由“一”來維護。反映在Java代碼中就是在“一”的一端中持有“多”一端的集合,而hibernate把這種關(guān)系反映到數(shù)據(jù)庫的策略是在“多”一端的表上加上一個外鍵指向“一”一端表。顯現(xiàn),其實這采用的還是“多対一”的映射原理。
但是,在“一”一端維護關(guān)系是我們不提倡的,因為它有不可避免的缺點,即級聯(lián)插入數(shù)據(jù)的時候要先插“多”一端,這樣造成了兩方面的不妥:1.如果我們把“多”一端的外鍵必須添加非空約束,將導(dǎo)致數(shù)據(jù)不能插入;2.即使外鍵不設(shè)置為非空,在插入“多”一端數(shù)據(jù)時外鍵將暫時為空( 因為此時它所引用的“一”記錄還沒有插入),而只有等到它所引用的“一”記錄插入后,再發(fā)出update語句修改外鍵,這樣的效率必然降低。
不管怎樣,還是來看看我的Classes和Student怎么做的吧。
1.實體模型:
2.關(guān)系模型:
3.實體類:
Student.java
public class Student {
private Integer id;
private String name;
//一系列的setter.getter方法
@Override
public String toString() {
return "name of student: " + name;
}
}
Classes.java
public class Classes {
private Integer id;
private String name;
private Set<Student> students;
//一系列的setter.getter方法
@Override
public String toString() {
return "name of class: " + name;
}
}
4.映射文件:
Student.hbm.xml
<class name="com.sxt.hibernate.one2many.entity.Student" table="sxt_hibernate_student">
<id name="id" length="4">
<generator class="native"></generator>
</id>
<property name="name" length="10"></property>
</class>
Classes.hbm.xml
<class name="com.sxt.hibernate.one2many.entity.Classes" table="sxt_hibernate_class">
<id name="id" length="4">
<generator class="native"></generator>
</id>
<property name="name" length="10"></property>
<!-- 配置集合屬性 -->
<set name="students" cascade="save-update">
<!-- key的含義,指在另一端增加的外鍵指向本主鍵.
如果設(shè)置上屬性not-null="true",表示該外鍵非空,則在由"一"的一端維護關(guān)系時,
可能導(dǎo)致插入數(shù)據(jù)異常PropertyValueException.
-->
<key column="class_id"></key>
<!--one-to-many含義,指出set集合中的元素類型,以供加載時使用 -->
<one-to-many class="com.sxt.hibernate.one2many.entity.Student"/>
</set>
</class>
5.hibernate配置文件:
參考前面的。
6.測試方法:
public static void main(String[] args) {
Session session = HibernateUtils.getSession();
Transaction t = session.beginTransaction();
try {
/**
* 測試插入數(shù)據(jù)
*/
/*
* Student student1=new Student();
* student1.setName("奇隆");
*
* Student student2=new Student();
* student2.setName("有朋");
*
* Set<Student> students=new HashSet<Student>();
* students.add(student1);
* students.add(student2);
*
* Classes classes=new Classes();
* classes.setName("不一班");
* classes.setStudents(students);
* //存儲不成功.報錯:org.hibernate.TransientObjectException
* //因為此時student對象還沒有持久化,classes引用了瞬時對象student1,student2
* session.save(classes);
*/
Student student1=new Student(); student1.setName("奇隆");
//session.save(student1);//先把student對象持久化
Student student2=new Student();
student2.setName("有朋");
//session.save(student2);
Set<Student> students=new HashSet<Student>();
students.add(student1);
students.add(student2);
Classes classes=new Classes();
classes.setName("不一班");
classes.setStudents(students);
//存儲成功.sql語句如下:
/*
Hibernate: insert into sxt_hibernate_class (name, id) values (?, ?)
Hibernate: insert into sxt_hibernate_student (name, id) values (?, ?)
Hibernate: insert into sxt_hibernate_student (name, id) values (?, ?)
Hibernate: update sxt_hibernate_student set class_id=? where id=?
Hibernate: update sxt_hibernate_student set class_id=? where id=? */
//可見在存儲class之后,發(fā)出兩個update語句,把它的兩個student對象的class_id更新了.
//這是因為關(guān)系由"一"的一端維護(即class維護關(guān)系).這顯然會降低效率.
//所以對于一對多,我們一般把關(guān)系交給"多"的一端維護.
session.save(classes);
/**
* 測試加載數(shù)據(jù)
*/
/* Classes classes = (Classes) session.load(Classes.class, 3);
System.out.println(classes);
Set<Student> students = classes.getStudents();
for (Iterator<Student> stus = students.iterator(); stus.hasNext();) {
System.out.println(stus.next());
}*/
t.commit();
} catch (HibernateException e) {
e.printStackTrace();
t.rollback();
} finally {
HibernateUtils.closeSession(session);
}
}
}