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=?