【Hibernate步步爲營】--(一對多映射)之單向關聯

一、單向一對多


        前篇文章中曾經對多對一的關係展開了討論,其中主要使用的是<many-to-one>的關係,在多的一端維護一的一端,那和今天要討論的一對多的關係它們之間是否有關聯呢?在關係和對象模型中存在了一對多關係所以理所當然就會有多對一的關係,Hibernate同樣提供了一對多關係的標籤<one-to-many>。
        一對多關係的對象模型在日常生活中也經常看到,就拿學生和班級來說,一個班級裏有多個學生,所以班級和學生的關係是一對多的關係,映射到對象模型中,如下圖:

       對象模型說明了這種一對多的關係是由一的一端來維護的,那麼映射成關係模型就是一個班級字段下面會有多個學生,這樣就形成了一對多的關係,通過班級能夠查詢獲得學生信息,對應的關係模型如下圖:


  1、基本配置


       有了對象模型接下來就讓它們映射爲對應的關係代碼,在進行關係映射時需要在一的一端添加<one-to-many>標籤,另外還需要在一的一端添加Set屬性,它支持延遲加載,然後在映射文件添加set標籤,並指明一對多的關係,這樣就能夠在一的一端查詢獲取多的一端。

    Classes類及映射文件
      它是模型中最重要的一端,在該端需要添加對應的set屬性,並在配置文件中添加set標籤,在set標籤中配置相應的<one-to-many>對象,具體Classes.java對象代碼如下:
  1. <pre name="code" class="java">package com.src.hibernate;  
  2.   
  3. import java.util.Set;  
  4.   
  5. public class Classes {  
  6.     private int id;  
  7.     public int getId() {  
  8.         return id;  
  9.     }  
  10.     public void setId(int id) {  
  11.         this.id = id;  
  12.     }  
  13.     public String getName() {  
  14.         return name;  
  15.     }  
  16.     public void setName(String name) {  
  17.         this.name = name;  
  18.     }  
  19.     private String name;  
  20.       
  21.     //Set支持延遲加載  
  22.     private Set students;  
  23.     public Set getStudents() {  
  24.         return students;  
  25.     }  
  26.     public void setStudents(Set students) {  
  27.         this.students = students;  
  28.     }  
  29. }  
<pre name="code" class="java">package com.src.hibernate;

import java.util.Set;

public class Classes {
	private int id;
	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;
	}
	private String name;
	
	//Set支持延遲加載
	private Set students;
	public Set getStudents() {
		return students;
	}
	public void setStudents(Set students) {
		this.students = students;
	}
}

      Classes對象中使用了set屬性,但是隻是說明了延遲加載的屬性,並沒有爲屬性配置對應的對象,屬性的對象是要在映射文件中來配置的,需要添加set標籤,並在set標籤中添加<one-to-many>標籤,具體如下代碼:
  1. <?xml version="1.0"?>  
  2. <!DOCTYPE hibernate-mapping PUBLIC   
  3.     "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  
  4.     "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">  
  5. <hibernate-mapping>  
  6.     <class name="com.hibernate.Classes" table="t_classes">  
  7.         <id name="id">  
  8.             <generator class="native"/>  
  9.         </id>  
  10.         <property name="name"/>  
  11.         <set name="students">  
  12.             <key column="classesid"></key>  
  13.             <one-to-many class="com.hibernate.Student"></one-to-many>  
  14.         </set>  
  15.     </class>  
  16. </hibernate-mapping>  
<?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.hibernate.Classes" table="t_classes">
		<id name="id">
			<generator class="native"/>
		</id>
		<property name="name"/>
		<set name="students">
			<key column="classesid"></key>
			<one-to-many class="com.hibernate.Student"></one-to-many>
		</set>
	</class>
</hibernate-mapping>

        對應的Student對象中的代碼和映射文件不需要什麼特殊的配置,只需要按照通常的寫法編寫即可,具體的配置方法不再詳述,很簡單。配置好後需要生成對應的SQL語句,將對象模型轉化爲關係模型時Hibernate生成相應的語句如下:
  1. alter table t_student drop foreign key FK4B9075705E0AFEFE  
  2. drop table if exists t_classes  
  3. drop table if exists t_student  
  4. create table t_classes (id integer not null auto_increment, name varchar(255), primary key (id))  
  5. create table t_student (id integer not null auto_increment, name varchar(255), classesid integerprimary key (id))  
  6. alter table t_student add index FK4B9075705E0AFEFE (classesid), add constraint FK4B9075705E0AFEFE foreign key (classesid) references t_classes (id)  
alter table t_student drop foreign key FK4B9075705E0AFEFE
drop table if exists t_classes
drop table if exists t_student
create table t_classes (id integer not null auto_increment, name varchar(255), primary key (id))
create table t_student (id integer not null auto_increment, name varchar(255), classesid integer, primary key (id))
alter table t_student add index FK4B9075705E0AFEFE (classesid), add constraint FK4B9075705E0AFEFE foreign key (classesid) references t_classes (id)

      生成的對應的關係模型如下圖:

 對比SQL語句和關係模型,相應的表之間的關聯是通過外鍵來維護的,首先是創建兩張表,並指定表的主鍵,最後添加一對多的外鍵關聯關係。

  2、基本操作

       在對數據庫的操作無非是讀和寫兩種,修改也屬於寫的一種,接下來看看是如何向數據庫中寫入和讀取操作的。

       寫入數據
       寫入數據需要注意的是一對多的關係,所以在添加的時候需要添加多個學生類,另外由於在classes中添加了對應的set屬性,所以在添加Student對象時應該使用HashSet來添加,這樣既可實現一對多的關係,具體如下代碼:
  1. public void testSave2(){  
  2.     Session session=null;  
  3.     try{  
  4.         session=HibernateUtils.getSession();  
  5.         session.beginTransaction();  
  6.           
  7.         Student student1=new Student();  
  8.         student1.setName("zhangsan");  
  9.         session.save(student1);  
  10.           
  11.         Student student2=new Student();  
  12.         student2.setName("lisi");  
  13.         session.save(student2);  
  14.           
  15.         Classes classes=new Classes();  
  16.         classes.setName("ClassOne");  
  17.           
  18.         Set students=new HashSet();  
  19.         students.add(student1);  
  20.         students.add(student2);  
  21.           
  22.         classes.setStudents(students);  
  23.         //可以成功保存數據  
  24.         //但是會發出多餘的update語句來維持關係,因爲是一對多的原因  
  25.         session.save(classes);  
  26.         session.getTransaction().commit();  
  27.     }catch(Exception e){  
  28.         e.printStackTrace();  
  29.         session.getTransaction().rollback();  
  30.     }finally{  
  31.         HibernateUtils.closeSession(session);  
  32.     }  
  33. }  
public void testSave2(){
	Session session=null;
	try{
		session=HibernateUtils.getSession();
		session.beginTransaction();
		
		Student student1=new Student();
		student1.setName("zhangsan");
		session.save(student1);
		
		Student student2=new Student();
		student2.setName("lisi");
		session.save(student2);
		
		Classes classes=new Classes();
		classes.setName("ClassOne");
		
		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);
	}
}

        那麼運行上面的測試用例生成的對應的數據寫入到數據庫中後如下圖:


       讀取數據
       寫入操作相對簡單,只需要把所有加載的對象都添加到Transient狀態下,運行相應的方法就可以插入內容,但是對應的讀取操作就會稍微複雜點,因爲需要迭代獲取所有的學生對象,所以這種一對多的關係效率並不很高,具體代碼如下:
  1. package com.test.hibernate;  
  2.   
  3. import java.util.Iterator;  
  4. import java.util.Set;  
  5. import com.src.hibernate.*;  
  6. import junit.framework.TestCase;  
  7. import org.hibernate.Session;  
  8.   
  9. public class One2ManyTest extends TestCase {  
  10.     public void testLoad1(){  
  11.         Session session=null;  
  12.         try{  
  13.             session=HibernateUtils.getSession();  
  14.             session.beginTransaction();  
  15.               
  16.             //獲取主鍵爲5的班級信息  
  17.             Classes classes=(Classes)session.load(Classes.class,5);  
  18.             //打印班級信息  
  19.             System.out.println("classes.name="+classes.getName());  
  20.             //設置學生集合,通過班級加載學生集合  
  21.             Set students=classes.getStudents();  
  22.             //迭代集合,打印集合中學生的信息  
  23.             for(Iterator iter=students.iterator();iter.hasNext();){  
  24.                 Student student=(Student)iter.next();  
  25.                   
  26.                 System.out.println("student.name="+student.getName());  
  27.             }  
  28.               
  29.             session.getTransaction().commit();  
  30.         }catch(Exception e){  
  31.             e.printStackTrace();  
  32.             session.getTransaction().rollback();  
  33.         }finally{  
  34.             HibernateUtils.closeSession(session);  
  35.         }  
  36.     }  
  37. }  
package com.test.hibernate;

import java.util.Iterator;
import java.util.Set;
import com.src.hibernate.*;
import junit.framework.TestCase;
import org.hibernate.Session;

public class One2ManyTest extends TestCase {
	public void testLoad1(){
		Session session=null;
		try{
			session=HibernateUtils.getSession();
			session.beginTransaction();
			
			//獲取主鍵爲5的班級信息
			Classes classes=(Classes)session.load(Classes.class,5);
			//打印班級信息
			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);
		}
	}
}

      生成的相應的語句及信息如下語句:
  1. Hibernate: select classes0_.id as id1_0_, classes0_.name as name1_0_ from t_classes classes0_ where classes0_.id=?  
  2. classes.name=ClassOne  
  3. Hibernate: select students0_.classesid as classesid1_, students0_.id as id1_, students0_.id as id0_0_, students0_.name as name0_0_ from t_student students0_ where students0_.classesid=?  
  4. student.name=lisi  
  5. student.name=zhangsan  
Hibernate: select classes0_.id as id1_0_, classes0_.name as name1_0_ from t_classes classes0_ where classes0_.id=?
classes.name=ClassOne
Hibernate: select students0_.classesid as classesid1_, students0_.id as id1_, students0_.id as id0_0_, students0_.name as name0_0_ from t_student students0_ where students0_.classesid=?
student.name=lisi
student.name=zhangsan

結語


        一對多的關係也是經常用到的,但是這種關係在加載時效率會不高,因爲需要維護的數據較多,所以不建議使用一對多的關係,可以考慮多對一的關係,這樣在加載時其實是一種一對一的關係,加載的效率較高,關係和對象模型得到了優化。這裏只是討論了單向的一對多,下篇文章討論雙向的關聯關係。

更多

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章