單向N--->1關聯,比如在一個辦公室裡工作的多個人對應一個地址。為了讓兩個持久化類支援這種關聯映射,程式應該在N的一端的持久化類中增加一個屬性,該屬性引用1的一端的關聯實體。
我們先將test庫裡的表刪除:
然後建立一個web工程,並編寫代碼:
Person.java :
public class Person {private int id;private String name;private int age;private Address address;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public Address getAddress() {return address;}public void setAddress(Address address) {this.address = address;}}
Address.java :
public class Address {private int addressId;private String addressDetail;public int getAddressId() {return addressId;}public void setAddressId(int addressId) {this.addressId = addressId;}public String getAddressDetail() {return addressDetail;}public void setAddressDetail(String addressDetail) {this.addressDetail = addressDetail;}public Address(String addressDetail) {super();this.addressDetail = addressDetail;}}
Person.hbm.xml :
<?xml version="1.0"?><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="db.domain"> <class name="Person" table="persons"> <id name="id" type="integer"> <generator class="identity"></generator> </id> <property name="name" type="string"> <column name="name"></column> </property> <property name="age" type="integer"> <column name="age"></column> </property> <many-to-one name="address" class="Address" column="address_id" cascade="all"/> </class></hibernate-mapping>
Person端增加了Address屬性,該屬性不是一個普通的組件屬性,而是引用另一個持久化類的類,Hibernate使用<many-to-one.../>元素映射N--->1的關聯實體,直接採用<many-to-one.../>元素來映射關聯實體將會在N的一端的資料表中增加一個外鍵列,用於參照主表記錄。直接使用<many-to-one.../>元素來映射N--->1關聯時,Hibernate將無需使用串連表,直接使用外部索引鍵關聯策略來處理這種關聯映射。
Address.hbm.xml :
<?xml version="1.0"?><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="db.domain"> <class name="Address" table="address"> <id name="addressId" column="address_id"> <generator class="identity"></generator> </id> <property name="addressDetail" type="string"> <column name="addressDetail"></column> </property> </class></hibernate-mapping>
Test.java :
public class Test {public static void main(String[] args) {Session session=HibernateSessionFactory.getSession();Transaction txt=session.beginTransaction();Person p=new Person();p.setName("tom");p.setAge(20);Address a1=new Address("廣州天河");//①p.setAddress(a1);session.persist(p);//③Address a2=new Address("上海虹口");//②p.setAddress(a2);txt.commit();HibernateSessionFactory.closeSession();}}
上面程式建立了三個持久化實體,即一個Person對象,兩個Address對象,程式只在③處儲存了一次Person對象,從來不曾儲存過Address對象。
程式在①處建立了一個瞬態的Address對象,當程式執行到③處時,系統準備儲存Person對象,系統將要向persons表中插入一條記錄------但該記錄參照的主表記錄還不曾儲存(被參照的Address實體還處於瞬態),這時可能有如下兩種情況發生:
① 系統拋出TransientObjectException異常:因為主表記錄不曾插入,所以參照該記錄的從表記錄無法插入。
② 系統先自動級聯插入主表記錄,再插入從表記錄。
因為上面的對應檔中指定了cascade="all",這意味著系統將先自動級聯插入主表記錄,也就是先持久化Address對象,再持久化Person對象。也就是說,Hibernate在③處將先執行一條insert into address...語句,再執行一條insert into persons...語句。換一個方向來說,如果上面的對應檔缺少cascade="all",則程式運行到③處將拋出TransientObjectException異常。
必須牢記:要麼總是先持久化主表記錄對應的實體,要麼設定級聯操作;否則當hibernate試圖插入從表記錄時,如果發現該從表記錄參照的主表記錄不存在,就會拋出異常。
運行Test.java,查看資料庫: