其實和多對一本質上是一樣的,也是在many的一方加一個字段作為外鍵,指向one的一方的主鍵,只是加載的時候不一樣。多對一的時候我們加載many的一方可以將one的一方加載出來,但是一對多的時候是加載one的一方可以將many的一方加載出來。
一、單向一對多(工程hibernate_one2many_1)
這里我們給出一個班級對應多個學生的例子。
相關映射:

注意:這里在實體類中是在one的一方維護了一個集合,但是在數(shù)據(jù)庫中是在many的一方維護了一個指向one主鍵的外鍵。
Student.hbm.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="cn.itcast.hibernate.Student" table="_student">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
</class>
</hibernate-mapping>
Classes.hbm.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 package="cn.itcast.hibernate">
<class name="Classes" table="_classes">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<set name="students">
<key column="classesid"></key>
<one-to-many class="Student"/>
</set>
</class>
</hibernate-mapping>
測試:
One2ManyTest.java
package cn.itcast.hibernate;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.hibernate.Session;
import org.junit.Test;
public class One2ManyTest {
@Test
public void testSave1(){
Session session = null;
try{
session = HibernateUtils.getSession();
session.beginTransaction();
Student student1 = new Student();
student1.setName("張三");
session.save(student1);
Student student2 = new Student();
student2.setName("李四");
session.save(student2);
Set students = new HashSet();
students.add(student1);
students.add(student2);
Classes classes = new Classes();
classes.setName("高一班");
classes.setStudents(students);
session.save(classes);
session.getTransaction().commit();
}catch(Exception e){
e.printStackTrace();
session.getTransaction().rollback();
}finally{
HibernateUtils.closeSession(session);
}
}
@Test
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 iterator = students.iterator(); iterator.hasNext();){
Student student = (Student) iterator.next();
System.out.println("student.name = " + student.getName());
}
session.getTransaction().commit();
}catch(Exception e){
e.printStackTrace();
session.getTransaction().rollback();
}finally{
HibernateUtils.closeSession(session);
}
}
}
說明:
- 對于save方法我們可以看到發(fā)出sql語句的順序是:
Hibernate: insert into _student (name) values (?)
Hibernate: insert into _student (name) values (?)
Hibernate: insert into _classes (name) values (?)
Hibernate: update _student set classesid=? where id=?
Hibernate: update _student set classesid=? where id=?
可以發(fā)現(xiàn)在存儲many一方的時候沒有設置其classid,在之后保存one一方之后再對many一方進行更新。
- 對于load方法我們看到其發(fā)出sql語句的順序是:
Hibernate: select classes0_.id as id1_0_, classes0_.name as name1_0_ from _classes classes0_ where classes0_.id=?
Hibernate: select students0_.classesid as classesid1_, students0_.id as id1_, students0_.id as id0_0_, students0_.name as name0_0_ from _student students0_ where students0_.classesid=?
也就是說我們可以通過one的一方查出many的一方信息,但是不能反過來。
雙向一對多(工程hibernate_one2many_2)
相關映射:

說明:雙向映射只需要在Student類中加上一個private Classes classes字段,存儲的時候還是和以前一樣。
Student.hbm.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="cn.itcast.hibernate.Student" table="_student">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<many-to-one name="classes" column="classesid"></many-to-one>
</class>
</hibernate-mapping>
說明:對于one的一方相關實體類和配置文件不變,但是對于many一方我們需要在其實體類中加入一個屬性,而在其配置文件中我們也需要對這一屬性進行配置。由于在數(shù)據(jù)庫中one對應的表中的外鍵字段只有classid,所以在兩個配置文件中這一字段必須名字一樣。
測試:
One2ManyTest.java
package cn.itcast.hibernate;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.hibernate.Session;
import org.junit.Test;
public class One2ManyTest {
@Test
public void testSave1(){
Session session = null;
try{
session = HibernateUtils.getSession();
session.beginTransaction();
Student student1 = new Student();
student1.setName("張三");
session.save(student1);
Student student2 = new Student();
student2.setName("李四");
session.save(student2);
Set students = new HashSet();
students.add(student1);//這表示在one的一方維護關系
students.add(student2);
Classes classes = new Classes();
classes.setName("高一班");
classes.setStudents(students);
session.save(classes);
session.getTransaction().commit();
}catch(Exception e){
e.printStackTrace();
session.getTransaction().rollback();
}finally{
HibernateUtils.closeSession(session);
}
}
@Test
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();
student1.setName("張三");
student1.setClasses(classes);//這表示在many的一方維護關系
session.save(student1);
Student student2 = new Student();
student2.setName("李四");
student2.setClasses(classes);
session.save(student2);
session.getTransaction().commit();
}catch(Exception e){
e.printStackTrace();
session.getTransaction().rollback();
}finally{
HibernateUtils.closeSession(session);
}
}
}
說明:
對于testSave1方法和之前的例子一樣,但是這種方式有個缺點,當classid不能為null的時候會出現(xiàn)異常,所以我們一般不采用這種方式。因為這種方式是在one的一方維護映射關系。
我們也可以先存儲one的一方,如方法testSave2方法所示。發(fā)出sql語句的順序是:
Hibernate: insert into _classes (name) values (?)
Hibernate: insert into _student (name, classesid) values (?, ?)
Hibernate: insert into _student (name, classesid) values (?, ?)
這比第一種方式要簡便。因為在存儲many一方的時候就已經(jīng)知道其映射關系了,即在many一方維護關系。
- 由于我們一般不推薦在one的一方維護關系,所以我們一般讓one這邊維護關系的功能失效,此時
Classes.hbm.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 package="cn.itcast.hibernate">
<class name="Classes" table="_classes">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<set name="students" inverse="true" cascade="all">
<key column="classesid"></key>
<one-to-many class="Student"/>
</set>
</class>
</hibernate-mapping>
此時我們再使用方法testSave1進行存儲同樣是可以進行存儲的,但是卻不會存儲映射關系,即classid為null,這是因為one的一方已經(jīng)不具備維護關系的功能了。
- inverse主要用在一對多和多對多雙向關聯(lián)上,inverse可以被設置到集合標簽<set>上,默認inverse為false,所以我們可以從”one“一端和”many“一端維護關聯(lián)關系,如果設置成inverse為true,則我們只能從many一端來維護關聯(lián)關系。注意:inverse屬性,只影響數(shù)據(jù)的存儲,也就是持久化
當然此時我們也可以對方法testSave1進行改進:
@Test
public void testSave3(){
Session session = null;
try{
session = HibernateUtils.getSession();
session.beginTransaction();
Classes classes = new Classes();
classes.setName("高一班");
Student student1 = new Student();
student1.setName("張三");
student1.setClasses(classes);
Student student2 = new Student();
student2.setName("李四");
student2.setClasses(classes);
Set students = new HashSet();
students.add(student1);
students.add(student2);
classes.setStudents(students);
session.getTransaction().commit();
}catch(Exception e){
e.printStackTrace();
session.getTransaction().rollback();
}finally{
HibernateUtils.closeSession(session);
}
}
說明:可以看到我們在保存many之前就已經(jīng)將其classid設置好了,這樣就可以存儲了,雖然one的一方同樣沒有維護關系。
對于加載方法:
@Test
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 iterator = students.iterator(); iterator.hasNext();){
Student student = (Student) iterator.next();
System.out.println("student.name = " + student.getName());
}
session.getTransaction().commit();
}catch(Exception e){
e.printStackTrace();
session.getTransaction().rollback();
}finally{
HibernateUtils.closeSession(session);
}
}
@Test
public void testLoad2(){
Session session = null;
try{
session = HibernateUtils.getSession();
session.beginTransaction();
Student student = (Student) session.load(Student.class, 1);
System.out.println("student.name = " + student.getName());
System.out.println("student.classes.name = " + student.getClasses().getName());
session.getTransaction().commit();
}catch(Exception e){
e.printStackTrace();
session.getTransaction().rollback();
}finally{
HibernateUtils.closeSession(session);
}
}
說明:可以看到對于雙向關聯(lián)顯然可以雙向查詢。
最后:我們一般在many一方維護關系,因為維護關系的字段就在many這邊,所以盡量在many一方維護關系,同時將one這邊維護關系的功能取消掉。