所謂複合主鍵就是在一張數據庫表中,主鍵有兩個或者多個,在日常開發中會遇到這樣一種情況,數據庫中的某張表需要多個字段列才能唯一確定一行記錄,這時表需要使用複合主鍵。這是我們以前在hibernate配置中沒有遇到過的情況。面對這樣的情況Hibernate爲我們提供了兩種方式來解決複合主鍵問題,下面讓我們來看一下這兩種情況:
2:將主鍵屬性提取到一個主鍵類中,實體類只需包含主鍵類的一個引用
下面我們就具體來看一下:
方式一:將複合主鍵對應的屬性與實體其他普通屬性放在一起
例如實體類People中"id"和"name"屬性對應複合主鍵:
- /*實體類,使用複合主鍵必須實現Serializable接口*/
- public class People implements Serializable
- {
- private String id;
- private String name;
- private int age;
- public People()
- { }
- ***************set、get方法
- public int hashCode()
- {
- final int prime = 31;
- int result = 1;
- result = prime * result + ((id == null) ? 0 : id.hashCode());
- result = prime * result + ((name == null) ? 0 : name.hashCode());
- return result;
- }
- @Override
- public boolean equals(Object obj)
- {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (getClass() != obj.getClass())
- return false;
- People other = (People) obj;
- if (id == null)
- {
- if (other.id != null)
- return false;
- }
- else if (!id.equals(other.id))
- return false;
- if (name == null)
- {
- if (other.name != null)
- return false;
- }
- else if (!name.equals(other.name))
- return false;
- return true;
- }
- }
People.hbm.xml:
- <?xml version="1.0" encoding="utf-8"?>
- <!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.suxiaolei.hibernate.pojos.People" table="people">
- <!-- 複合主鍵使用composite-id標籤 -->
- <composite-id>
- <!-- key-property標籤表示哪一些屬性對應複合主鍵 -->
- <key-property name="id" column="id" type="string"></key-property>
- <key-property name="name" column="name" type="string"></key-property>
- </composite-id>
- <property name="age" column="age" type="integer"></property>
- </class>
- </hibernate-mapping>
Hibernate中使用複合主鍵時需要注意一些規則:
1. 使用複合主鍵的實體類必須實現Serializable接口。必須實現Serializable接口的原因很簡單,我們查找數據的時候是根據主鍵查找的。打開Hibernate的幫助文檔我們可以找到get與load方法的聲明形式如下:
- Object load(Class theClass,Serializable id)
- Object get(Class theClass,Serializable id)
當 我們查找複合主鍵類的對象時,需要傳遞主鍵值給get()或load()方法的id參數,而id參數只能接收一個實現了Serializable接口的對 象。而複合主鍵類的主鍵不是一個屬性可以表示的,所以只能先new出複合主鍵類的實例(例如:new People()),然後使用主鍵屬性的set方法將主鍵值賦值給主鍵屬性,然後將整個對象傳遞給get()或load()方法的id參數,實現主鍵值的 傳遞,所以複合主鍵的實體類必須實現Serializable接口。
2. 使 用複合主鍵的實體類必須重寫equals和hashCode方法。必須重寫equals和hashCode方法也很好理解。這兩個方法使用於判斷兩個對象 (兩條記錄)是否相等的。爲什麼要判斷兩個對象是否相等呢?因爲數據庫中的任意兩條記錄中的主鍵值是不能相同的,所以我們在程序中只要確保了兩個對象的主 鍵值不同就可以防止主鍵約束違例的錯誤出現。也許這裏你會奇怪爲什麼不使用複合主鍵的實體類不重寫這兩個方法也沒有主鍵違例的情況出現,這是因爲使用單一 主鍵方式,主鍵值是Hibernate來維護的,它會確保主鍵不會重複,而複合主鍵方式,主鍵值是編程人員自己維護的,所以必須重寫equals和hashCode方法用於判斷兩個對象的主鍵是否相同。
3. 重寫的equals和hashCode方法,只與主鍵屬性有關,普通屬性不要影響這兩個方法進行判斷。這個原因很簡單,主鍵才能決定一條記錄,其他屬性不能決定一條記錄。
保存測試:
- tx = session.beginTransaction();
- People people = new People();
- /*主鍵值由我們自己維護*/
- people.setId("123456");
- people.setName("zhangsan");
- people.setAge(40);
- session.save(people);
- tx.commit();
看看數據庫:
數據被正確的插入到數據庫中了。
讀取數據測試:
- tx = session.beginTransaction();
- /*查詢複合主鍵對象,需要先構建主鍵*/
- People peoplePrimaryKey = new People();
- peoplePrimaryKey.setId("123456");
- peoplePrimaryKey.setName("zhangsan");
- /*然後將構建的主鍵值傳入get方法中獲取對應的People對象*/
- People people = (People)session.get(People.class, peoplePrimaryKey);
- System.out.println("people age is:"+people.getAge());
- tx.commit();
控制檯輸出:people age is:40,以看到數據成功的取出了。
方式二:將主鍵屬性提取到一個主鍵類中,實體類只需包含主鍵類的一個引用。
主鍵類:
- /*必須實現Serializable接口*/
- public class PeoplePrimaryKey implements Serializable
- {
- /*複合主鍵值*/
- private String id;
- private String name;
- public PeoplePrimaryKey()
- {}
- /*複合主鍵值的get和set方法在此省略*/
- @Override
- public int hashCode()
- {
- final int prime = 31;
- int result = 1;
- result = prime * result + ((id == null) ? 0 : id.hashCode());
- result = prime * result + ((name == null) ? 0 : name.hashCode());
- return result;
- }
- @Override
- public boolean equals(Object obj)
- {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (getClass() != obj.getClass())
- return false;
- PeoplePrimaryKey other = (PeoplePrimaryKey) obj;
- if (id == null)
- {
- if (other.id != null)
- return false;
- }
- else if (!id.equals(other.id))
- return false;
- if (name == null)
- {
- if (other.name != null)
- return false;
- }
- else if (!name.equals(other.name))
- return false;
- return true;
- }
- }
實體類:
- public class People
- {
- /*持有主鍵類的一個引用,使用該引用作爲這個類的OID*/
- private PeoplePrimaryKey peoplePrimaryKey;
- private int age;
- public People()
- {}
- ***************************set/get方法省略
- }
People.hbm.xml文件稍有一點變動:
- <?xml version="1.0" encoding="utf-8"?>
- <!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.suxiaolei.hibernate.pojos.People" table="people">
- <!-- 複合主鍵使用composite-id標籤 -->
- <!--
- name - 指定了複合主鍵對應哪一個屬性
- class - 指定了複合主鍵屬性的類型
- -->
- <composite-id name="peoplePrimaryKey" class="com.suxiaolei.hibernate.pojos.PeoplePrimaryKey">
- <!-- key-property指定了複合主鍵由哪些屬性組成 -->
- <key-property name="id" column="id" type="string"></key-property>
- <key-property name="name" column="name" type="string"></key-property>
- </composite-id>
- <property name="age" column="age" type="integer"></property>
- </class>
- </hibernate-mapping>
場景測試與方式一大同小異這裏不再舉例了。主鍵類爲什麼實現Serializable接口和爲什麼重寫equals和hashCode方法上面已經解釋的很清楚了。
3.聯合主鍵的映射規則
1) 類中的每個主鍵屬性都對應到數據表中的每個主鍵列。Hibernate要求具有聯合主鍵的實體類實現Serializable接口,並且重寫hashCode與equals方法,重寫這兩個方法的原因在於Hibernate要根據數據庫的聯合主鍵來判斷某兩行記錄是否是一樣的,如果一樣那麼就認爲是同一個對象,如果不一樣,那麼就認爲是不同的對象。這反映到程序領域中就是根據hashCode與equals方法來判斷某兩個對象是否能夠放到諸如Set這樣的集合當中。聯合主鍵的實體類實現Serializable接口的原因在於使用get或load方法的時候需要先構建出來該實體的對象,並且將查詢依據(聯合主鍵)設置進去,然後作爲get或load方法的第二個參數傳進去即可。
2) 將主鍵所對應屬性提取出一個類(稱之爲主鍵類),並且主鍵類需要實現Serializable接口,重寫equals方法與hashCode方法,原因與上面一樣。