1. 程式人生 > >hibernate——多對一和一對多對映淺析

hibernate——多對一和一對多對映淺析

首先應該清楚多對一和一對多隻是站在不同的角度看待問題,其本質是一樣的。在思考這個問題的時候,不要把這兩個概念混在一起,這樣不容易理解,而要分開,站在不同的角度去解決同一個問題。

就拿員工和部門的例子來說,我們站在不同的角度,可能會遇到如下的幾種情況:

站在員工的角度看,是多對一的關係:

 1、來了新員工,但是還不知道該分配到哪個部門,只有先把員工儲存到員工表中,部門那一列設為空,待以後再更新其部門資訊

 2、來了新員工,並且確定了該員工分配到哪個部門,則將員工的完整資訊儲存到員工表中

站在部門的角度看,是一對多的關係:

 1、成立了新部門,要向其中新增一些新員工

 2、要撤銷一個部門,這時,這個部門中的員工的部門資訊,全部置為空

此外應注意以下幾個問題:

 1、若是儲存員工,說明表中還沒有這個員工的id,在儲存的時候,設定其id屬性的時候,就不要設定,用預設的就可以,這樣系統會自動給這個員工分配一個id的

 2、若是更新某個員工,說明員工表中已經有了這個員工的id,在更新的時候,一定要指明其id,才可以找到這個員工

 3、無論是站在部門還是站在員工的角度,即無論是一對多還是多對一的關係,對映到資料庫中的表的關係都是一樣的,就相當於是兩個表的關係的兩種實現方式,但最終的結果是一樣的。

下面,我們來看看如何具體的去實現上面提到的幾種情況:

首先建立兩個類:Department和Employee

package com.suo.domain;

import java.util.Set;

public class Department {
	private int id;
	private String name;
	private Set<Employee> emps;//用集合來儲存員工
	
        ……//set/get方法	
}
package com.suo.domain;

public class Employee {
	private int id;
	private String name;
	private Department depart;//注意這裡是以部門的物件來作為員工的屬性的,這個思想很關鍵,是建立起部門和員工關聯的關鍵
	
        ……//set/get方法
}
這兩個類的對映檔案,注意看其中的註釋:
Employee的對映檔案:

<hibernate-mapping package="com.suo.domain">
	
	<class name="Employee">
		<id name="id">
			<generator class="native"/>
		</id>
		<property name="name"/>
		<many-to-one name="depart" column="depart_id"></many-to-one>
		<!-- many-to-one指明瞭外來鍵 ,會根據反射機制,找到要和Employee建立多對一關係的類,該列預設的是可以為空的-->
	</class>
	
</hibernate-mapping>
Department的對映檔案:

<hibernate-mapping package="com.suo.domain">
	
	<class name="Department">
		<id name="id">
			<generator class="native"/>
		</id>
		<property name="name"/>
		
		<set name="emps">
			<key column="depart_id"/><!-- key指明瞭員工表中的外來鍵-->
			<one-to-many class="Employee"/><!-- one-to-many指明瞭和哪個類進行一對多的對映 -->
		</set>
		<!-- 
			用set標籤表示Department中的員工集合的屬性,這個屬性並沒有對映到資料庫中的部門表中,
			即部門表中,並沒有emps這樣的一個列。
		 -->
	</class>
	
</hibernate-mapping>

配置檔案就不貼出來了……

然後再建立一個dao層的類:Many_VS_One,方便呼叫:

package com.suo.hibernate.impl;

import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;

import com.suo.domain.Employee;
import com.suo.hibernate.util.HibernateUtil;

public class Many_VS_One {
	
	public void saveObject(Object obj) {
		Session session=null;
		Transaction transaction=null;
		try{
			session=HibernateUtil.getSession();//獲得一個連線
			transaction=session.beginTransaction();//開啟一個事務
			session.save(obj);
			transaction.commit();
		}catch(HibernateException e){
			if(transaction!=null){
				transaction.rollback();
			}
			e.printStackTrace();
		}finally{
			if(session!=null){
				session.close();
			}
		}
	}
	public void updateObject(Object obj) {
		Session session=null;
		Transaction transaction=null;
		try{
			session=HibernateUtil.getSession();//獲得一個連線
			transaction=session.beginTransaction();//開啟一個事務
			session.update(obj);
			transaction.commit();
		}catch(HibernateException e){
			if(transaction!=null){
				transaction.rollback();
			}
			e.printStackTrace();
		}finally{
			if(session!=null){
				session.close();
			}
		}
	}
	public void deleteObject(Object obj) {
		Session session=null;
		Transaction transaction=null;
		try{
			session=HibernateUtil.getSession();//獲得一個連線
			transaction=session.beginTransaction();//開啟一個事務
			session.delete(obj);
			transaction.commit();
		}catch(HibernateException e){
			if(transaction!=null){
				transaction.rollback();
			}
			e.printStackTrace();
		}finally{
			if(session!=null){
				session.close();
			}
		}
	}
	
	public Employee queryEmp(int empId){
		Session session=null;
		try{
			session=HibernateUtil.getSession();
			Employee emp=(Employee)session.get(Employee.class, empId);//這裡會執行一次查詢employee的語句
			System.out.println(emp.getDepart().getName());//這裡會執行一次查詢department的語句
			Hibernate.initialize(emp.getDepart());//初始化代理,即初始化一個持久態的物件,加上這一句,才能在外部查詢
			return emp;
		}catch(HibernateException e){
			e.printStackTrace();
			return null;
		}finally{
			if(session!=null){
				session.close();
			}
		}
	}
}

接下來,加在Test中解決問題了:

站在員工的角度看,是多對一的關係:

1、來了新員工,但是還不知道該分配到哪個部門,只有先把員工儲存到員工表中,部門那一列設為空,待以後再更新其部門資訊

這個很簡單,只要在設定新增的員工屬性的時候,不要設定他的部門屬性,就可以了,注意,也不要設定他的id屬性,全都用預設值,程式碼如下:

public static void main(String[] args) {
		Many_VS_One mvso=new Many_VS_One();
		
		Employee e1=new Employee();
		e1.setName("suo");
		
		Employee e2=new Employee();
		e2.setName("piao");

		mvso.saveObject(e1);
		mvso.saveObject(e2);
		
	}
這樣就在員工表中插入了這兩個員工的資訊,但是所屬部門那一列資訊為空。

現在,確定了這兩個員工的部門資訊,那麼就要更新這兩個員工的部門了,這裡我們假設將這兩個員工都分配到了depart1,實現程式碼如下:

public static void main(String[] args) {
		Many_VS_One mvso=new Many_VS_One();
		
		Department depart=new Department();
		depart.setId(1);
		
		Employee e1=new Employee();
		e1.setId(1);
		e1.setName("suo");
		e1.setDepart(depart);//這句看似簡單,但是很關鍵,它建立起了兩個物件之間的關聯
		
		Employee e2=new Employee();
		e2.setId(2);
		e2.setName("piao");
		e2.setDepart(depart);
		
		mvso.updateObject(e1);
		mvso.updateObject(e2);
		
	}
注意,執行這一步的前提是資料庫中已經有了id為1的部門,還有了id為1、2的兩個員工,只是要將員工的部門資訊設定為id為1的部門。因為資料庫中已經有了這幾條記錄,所以我們在new出來物件之後,一定要指定這個物件在資料庫中的id,以告訴hibernate,這個物件不是新的,它已經存到資料庫中去了。因為hibernate是根據id來判斷一個新建的物件是否在資料庫中。所以指明id很重要。其次,就是在設定員工屬性的時候,要把所有屬性重新設定一下,因為hibernate更新的時候,是更新的全部資訊,從它執行的sql語句就可以看出來(update Employee set name=?, depart_id=? where id=?)。

 2、來了新員工,並且確定了該員工分配到哪個部門,則將員工的完整資訊儲存到員工表中

如果理解了1中的情況,那麼這種情況,就很簡單了,只要在儲存員工的時候,設定上部門屬性就可以了,但是不要忘了在new出來部門物件之後,要指定它在資料庫中的id,否則,hibernate會認為這個部門物件是個瞬時狀態的物件,沒有在資料庫中。而在new出來員工物件後,不要設定他的id屬性啊,因為資料庫中,還沒有員工的資訊,此時物件還處於瞬時狀態。實現程式碼如下:

public static void main(String[] args) {
		Many_VS_One mvso=new Many_VS_One();
		
		Department depart=new Department();
		depart.setId(1);
		
		Employee e1=new Employee();
		e1.setName("suo");
		e1.setDepart(depart);//這句看似簡單,但是很關鍵,它建立起了兩個物件之間的關聯
		
		Employee e2=new Employee();
		e2.setName("piao");
		e2.setDepart(depart);

		mvso.saveObject(e1);
		mvso.saveObject(e2);
		
	}

站在部門的角度看,是一對多的關係:

 1、成立了新部門,要向其中新增一些新員工

public static void main(String[] args) {
		Many_VS_One mvso=new Many_VS_One();
		
		Employee e1=new Employee();
		e1.setName("suo");
		
		Employee e2=new Employee();
		e2.setName("piao");
		
		Department depart=new Department();
		Set<Employee> emps=new HashSet<Employee>();
		emps.add(e1);
		emps.add(e2);
		depart.setName("depart1");
		depart.setEmps(emps);
	
		mvso.saveObject(e1);
		mvso.saveObject(e2);
		mvso.saveObject(depart);
			
	}
這裡很不好理解的一步是depart.setEmps(emps);設定這個員工集合到depart中,這個對映到資料庫中,就是在員工表中加上部門資訊,即給外來鍵新增資訊。這和站在員工的角度給員工的屬性設定部門資訊,作用的效果是一樣的。

 2、 要撤銷一個部門,這時,這個部門中的員工的部門資訊,全部置為空
這一步其實應該看資料庫是怎麼設定的,主表是否能在從表存在引用的條件下刪除,本例中,預設的設定是允許的。刪除的時候也很簡單,只要指定部門的id即可,同理,刪除員工也是如此。如下:

public static void main(String[] args) {
		Many_VS_One mvso=new Many_VS_One();
		
		Department depart=new Department();
		depart.setId(1);

		mvso.deleteObject(depart);
		
	}
這種情況下的刪除,其實在資料庫中分兩步操作,首先將員工表的外來鍵置空,然後在刪除部門表,執行的sql語句如下:

Hibernate: update Employee set depart_id=null where depart_id=?
Hibernate: delete from Department where id=?