一、雙向主鍵關聯
雙向的主鍵關聯其實是單向一對一主鍵關聯的一種特殊情況,只不過要在關聯對象的兩端的映射文件中都要進行<one-to-one>的配置,另外還要在主映射的主鍵一端採用foreign外鍵關聯屬性。
這裡同樣使用Person和IdCard來討論,一個人對應著一個唯一的身份證,而且一個身份證也唯一映射著一個人,所以這就產生了雙向的關聯關係,Person的主鍵同樣也是IdCard的主鍵,分別是主鍵的同時也是外鍵,這種關聯關係成為雙向一對一映射,表現到關係模型中可如下圖:
圖中的兩個表採用了主鍵關聯,person的主鍵是idCard的主鍵,所以它們之間構成了朱外鍵的約束關係,並且保證唯一性,映射到對像模型中,轉變為person類和idCard類的一對一關係,如下圖:
這種一對一的關係上篇文章中也有講到用的是<one-to-one>標籤,另外這種一對一映射又是雙向的,所以要在兩個對象之間同時配置<one-to-one>,首先來看idCard對應的類代碼和映射文件代碼。
1、IdCard對應的信息
IdCard.java類,IdCard類和Person類之間有一對一的關聯關係所以要在IdCard類中添加對應的Person屬性,這是為了能在映射文件中的外鍵中添加對應的屬性,設置對應的外鍵關聯類。
package com.src.hibernate; public class IdCard { //id屬性private int id; public int getId() { return id; } public void setId(int id) { this.id = id; } //卡號屬性private String cardNo; public String getCardNo() { return cardNo; } public void setCardNo(String cardNo) { this.cardNo = cardNo; } //卡號對應的人private Person person; public Person getPerson(){ return person; } public void setPerson(Person person){ this.person=person; } }IdCard.hbm.xml映射文件,在映射文件中添加外鍵屬性person,並添加對應的<one-to-one>標籤,目的是強制約束person類來實現一對一的映射關係,最後在映射中將constrained屬性設為true,保證強制約束關係。
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Generated 2014-5-15 23:47:00 by Hibernate Tools 3.4.0.CR1 --> <hibernate-mapping> <class name="com.src.hibernate.IdCard" table="IDCARD"> <id name="id" type="int" column="personId"> <generator> <param name="property">person</param> </generator> </id> <property name="cardNo" type="string" column="cardno"></property> <one-to-one name="person" constrained="true"></one-to-one> </class> </hibernate-mapping>
2、Person對應的信息
Person.java類,在該類中除了添加基本的屬性外還要添加對應的IdCard類作為屬性,因為它們之間是一對一的雙向關聯關係,所以在Person類中同樣要添加IdCard類,相同的道理IdCard類中同樣添加了Person類屬性。
package com.src.hibernate; public class Person { //id號private int id; public int getId() { return id; } public void setId(int id) { this.id = id; } //姓名private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } //idCard private IdCard idcard; public IdCard getIdcard() { return idcard; } public void setIdcard(IdCard idcard) { this.idcard = idcard; } }Person.hbm.xml映射文件,該文件中主鍵生成策略沒有特殊的要求,因為它和IdCard類相互制約的關係,它的主鍵和外鍵都是IdCard的主鍵,另外因為是一對一關係所以要在映射文件中添加<one-to-one>標籤來標示。
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Generated 2014-5-15 23:47:00 by Hibernate Tools 3.4.0.CR1 --> <hibernate-mapping> <class name="com.src.hibernate.Person" table="PERSON"> <id name="id" type="int" column="personId"> <generator></generator> </id> <property name="name" type="string" column="personName"></property> <!-- one-to-one標籤指示Hibernate如何加載其關聯對象,默認根據主鍵加載,也就是拿到關係字段值,根據對端的主鍵來加載關聯對象--> <one-to-one name="idcard"></one-to-one> </class> </hibernate-mapping>
3、Hibernate映射文件
上面的類和映射文件配置好後接下來要在Hibernate.cfg.xml中配置與數據庫映射的信息,需要將兩個配置文件添加到Hibernate配置文件中,這樣在生成對應的數據庫時才能找到對應的生成項。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernate_one2one_pk1</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">1234</property> <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> <mapping resource="com/src/hibernate/Person.hbm.xml"/> <mapping resource="com/src/hibernate/IdCard.hbm.xml" ></mapping> </session-factory> </hibernate-configuration>
4、生成結果
配置完成後就可以將上面的內容生成對應的數據庫了,在數據庫中它會按照配置的內容生成相應的表結構,在表中有相應的外鍵和主鍵字段。生成表結構時Hibernate會在控制台輸出相應的SQL語句,如下:
alter table IDCARD drop foreign key FK806F76ABAC038CD8 drop table if exists IDCARD drop table if exists PERSON create table IDCARD (personId integer not null, cardno varchar(255), primary key (personId)) create table PERSON (personId integer not null auto_increment, personName varchar(255), primary key (personId)) alter table IDCARD add index FK806F76ABAC038CD8 (personId), add constraint FK806F76ABAC038CD8 foreign key (personId) references PERSON (personId)
生成的表結構如下圖:
在兩張表中同時生成了personId主鍵,並且也是相應的外鍵,它同時限制約束了兩張表的主鍵相同且唯一。
5、寫入加載測試
生成表後測試下對錶的寫入和從表中讀取數據,編寫相應的測試類,測試採用的是單元測試,編寫對應的測試方法。
5.1 寫入測試
在寫入到數據庫時一定要注意寫入的兩個對像都要轉化到對應的Trainent狀態,否則會出現狀態轉化的錯誤,測試代碼如下:
public void testSave1(){ Session session=null; try{ //創建一個會話對象session=HibernateUtils.getSession(); //開啟會話事務session.beginTransaction(); //創建person對象,並保存Person person=new Person(); person.setName("zhangsan"); session.save(person); //創建idCard對象,並保存IdCard idcard=new IdCard(); idcard.setCardNo("1111111111111"); idcard.setPerson(person); session.save(idcard); //提交事務,修改數據庫session.getTransaction().commit(); }catch(Exception e){ //打印錯誤信息e.printStackTrace(); //業務回滾session.getTransaction().rollback(); }finally{ //關閉會話HibernateUtils.closeSession(session); } }插入的數據如下圖:
5.2 加載測試
編寫加載方法,因為關聯關係是雙向的,所以相應的加載操作應該是通過一端加載另一端,也就是獲取對應的Person類,並通過Person類來獲取對應的IdCard信息,相反的也要成立,代碼如下:
public void testLoad1(){ Session session=null; try{ //創建一個會話對象session=HibernateUtils.getSession(); //開啟會話事務session.beginTransaction(); //獲取person對象,並保存Person person=(Person)session.load(Person.class,5); System.out.println("IdCard.Id: "+person.getIdcard().getId()); System.out.println("IdCard.cardno: "+person.getIdcard().getCardNo()); //創建idCard對象,並保存IdCard idcard=(IdCard)session.load(IdCard.class, 5); System.out.println("Person.Id: "+idcard.getPerson().getId()); System.out.println("Person.name: "+idcard.getPerson().getName()); //提交事務,修改數據庫session.getTransaction().commit(); }catch(Exception e){ //打印錯誤信息e.printStackTrace(); //業務回滾session.getTransaction().rollback(); }finally{ //關閉會話HibernateUtils.closeSession(session); } }運行上面的測試方法,在控制台打印的相關內容如下:
二、雙向外鍵關聯
雙向的外鍵關聯可以理解為外鍵關聯的一種特殊情況,這種特殊主要是由於它是一種雙向的對應關係,在前篇文章中提到如果想要在一張表中添加一個外鍵字段的話可以使用<many-to-one>標籤,它會關係模型中生成對應的外鍵列。這裡想要實現雙向的外鍵關聯就必須使用該標籤。
1、對像模型
先來看對像模型,人和身份證屬於一對一的關係,一個人對應著一個身份,所以它們之間的多重性是一對一的,並且這種對應關係是雙向的。所以它的對像模型同雙向主鍵一對一是相同的,如下圖:
2、關係模型
對應的關係模型會發生很大的變化,一對一的外鍵關聯關係會在一張表中生成對應的外鍵,拿到人和身份證上來說也就是人的關係模型中會有一個身份證號的主鍵列,它們之間形成了雙向的一對一的情況,如下圖:
它們之間的對應關係就是上圖中看到的,person表中有idCard表的主鍵,形成了一對一的外鍵關聯關係,而且是雙向的,也就是說通過person能夠獲取到idCard,另外通過idCard也能獲取到person。
Person對象和IdCard對象內的代碼同上篇文章中的對象代碼一致,不在做代碼羅列,唯一不同的是映射文件中的配置問題。
3、映射文件
idCard.hbm.xml映射文件,idCard表不是映射的主表,所以在做一對一的映射時需要使用的是<one-to-one>標籤來配置,並且需要製定person關係模型中的外鍵屬性,具體代碼如下:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Generated 2014-5-18 22:27:43 by Hibernate Tools 3.4.0.CR1 --> <hibernate-mapping> <class name="com.src.hibernate.IdCard" table="IDCARD"> <id name="id" type="int"> <generator /> </id> <property name="cardNo" type="java.lang.String"> <column name="CARDNO" /> </property> <one-to-one name="person" property-ref="idCard"></one-to-one> </class> </hibernate-mapping>
Person.hbm.xml映射文件,person表是映射的主表,需要在該表中添加一個外鍵屬性列來標示idCard表,所以這裡需要使用<many-to-one>標籤,在person對像中生成相應的外鍵,並且還要使用unique標明屬性唯一。
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Generated 2014-5-18 22:27:43 by Hibernate Tools 3.4.0.CR1 --> <hibernate-mapping> <class name="com.src.hibernate.Person" table="PERSON"> <id name="id" type="int" column="personId"> <generator /> </id> <property name="name" type="java.lang.String"> <column name="NAME" /> </property> <many-to-one name="idCard" column="idCardNo" unique="true" not-null="true"></many-to-one> </class> </hibernate-mapping>
對象的映射文件配置完成,接下來生成關係模型,SQL語句如下:
alter table PERSON drop foreign key FK8C768F55794A52CA drop table if exists IDCARD drop table if exists PERSON create table IDCARD (id integer not null auto_increment, CARDNO varchar(255), primary key (id)) create table PERSON (personId integer not null auto_increment, NAME varchar(255), idCardNo integer not null unique, primary key (personId)) alter table PERSON add index FK8C768F55794A52CA (idCardNo), add constraint FK8C768F55794A52CA foreign key (idCardNo) references IDCARD (id)
生成的SQL語句首先是創建的表,在建表時指定了主鍵列,創建完成後修改了兩個表指定外鍵屬性,形成一對一的關係。
編寫測試方法,採用單元測試,加載兩個類的對象,並分別從對象的一端獲取另一個對象
//加載對象,使用IdCard對象裝載person對象public void testLoad1(){ Session session=null; try{ session=HibernateUtils.getSession(); session.beginTransaction(); //獲取IdCard對象,在IdCard中獲取與該對象唯一關聯的person對象IdCard idcard=(IdCard)session.load(IdCard.class,1); System.out.println("person.Id= "+idcard.getPerson().getId()); System.out.println("idCard.person.name= "+idcard.getPerson().getName()); //獲取Person對象,在Person對像中獲取與它唯一關聯的IdCard對象Person person=(Person)session.load(Person.class,1); System.out.println("idCard.id: "+person.getIdCard().getId()); System.out.println("idCard.cardNo: "+person.getIdCard().getCardNo()); //提交事務session.getTransaction().commit(); }catch(Exception e){ e.printStackTrace(); session.getTransaction().rollback(); }finally{ HibernateUtils.closeSession(session); } }生成的內容:
對比兩種映射關係,主鍵和外鍵兩種映射,都是雙向的映射關係,需要在對象的兩端同時配置映射關係,不同的是主鍵只需要使用<one-to-one>因為它不需要生成屬性列,但是必須對錶的主鍵採用foreign的主鍵生成策略,並標示外鍵對象;外鍵的生成策略則需要採用<many-to-one>標籤來生成新的外鍵列。
結語
雙向關聯中的一對一映射至此已經討論完成,兩篇文章主要討論了雙向關聯中的兩種用法,其實還是很簡單的,記住一句話想要生成外鍵就使用<many-to-one>標籤,如果唯一那就添加unique屬性,<one-to-one>標籤只是指明了一對一的關係它只是指明一個對像如何加載另一個對象並不在關係模型中添加新列。下篇文章將會對一對多關聯展開討論。