所謂複合主鍵就是在一張資料庫表中,主鍵有兩個或者多個,在日常開發中會遇到這樣一種情況,資料庫中的某張表需要多個字段列才能唯一确定一行記錄,這時表需要使用複合主鍵。這是我們以前在hibernate配置中沒有遇到過的情況。面對這樣的情況Hibernate為我們提供了兩種方式來解決複合主鍵問題,下面讓我們來看一下這兩種情況:
1:将複合主鍵對應的屬性與實體其他普通屬性放在一起
2:将主鍵屬性提取到一個主鍵類中,實體類隻需包含主鍵類的一個引用
下面我們就具體來看一下:
方式一:将複合主鍵對應的屬性與實體其他普通屬性放在一起
例如實體類People中"id"和"name"屬性對應複合主鍵:
[java] view plain copy print ?
- 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:
[html] view plain copy print ?
- <?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方法的聲明形式如下:
[java] view plain copy print ?
- 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方法,隻與主鍵屬性有關,普通屬性不要影響這兩個方法進行判斷。這個原因很簡單,主鍵才能決定一條記錄,其他屬性不能決定一條記錄。
儲存測試:
[java] view plain copy print ?
- tx = session.beginTransaction();
- People people = new People();
- people.setId("123456");
- people.setName("zhangsan");
- people.setAge(40);
- session.save(people);
- tx.commit();
看看資料庫:
資料被正确的插入到資料庫中了。
讀取資料測試:
[java] view plain copy print ?
- tx = session.beginTransaction();
- People peoplePrimaryKey = new People();
- peoplePrimaryKey.setId("123456");
- peoplePrimaryKey.setName("zhangsan");
- People people = (People)session.get(People.class, peoplePrimaryKey);
- System.out.println("people age is:"+people.getAge());
- tx.commit();
控制台輸出:people age is:40,以看到資料成功的取出了。
方式二:将主鍵屬性提取到一個主鍵類中,實體類隻需包含主鍵類的一個引用。
主鍵類:
[java] view plain copy print ?
- public class PeoplePrimaryKey implements Serializable
- {
- private String id;
- private String name;
- public PeoplePrimaryKey()
- {}
- @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;
- }
- }
實體類:
[java] view plain copy print ?
- public class People
- {
- private PeoplePrimaryKey peoplePrimaryKey;
- private int age;
- public People()
- {}
- ***************************set/get方法省略
- }
People.hbm.xml檔案稍有一點變動:
[html] view plain copy print ?
- <?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方法,原因與上面一樣。