在測試Hibernate的一對多雙向關(guān)聯(lián)映射時,碰到很有趣的問題,跟inverse屬性直接相關(guān)。

1、People.hbm.xml

<hibernate-mapping default-lazy="false"> 
<class name="com.persistent.People" table="people"> 
<id name="id" column="peopleId" unsaved-value="0"> 
<generator class="increment"> 
</generator> 
</id> 
<property name="name" column="name"></property> 
<set name="addresses" cascade="save-update">
<key column="peopleId" not-null="true" />
<o(jì)ne-to-many class="com.persistent.Address"/>
</set> 
</class>
</hibernate-mapping>

2、Address.hbm.xml

<hibernate-mapping>
<class name="com.persistent.Address" table="address"> 
<id name="id" column="addressId" unsaved-value="0">
<generator class="increment">
</generator>
</id> 
<many-to-one name="people" column="peopleId" insert="false" update="false"></many-to-one> 
<property name="addressName" column="addressName"></property> 
<property name="codeNumber" column="codeNumber"></property> 
</class> 
</hibernate-mapping>
3、People.java和Address.java

public class People ...{ 
private long id; 
private String name; 
private Set addresses = new HashSet(); 
...
}

public class Address ...{ 
private long id; 
private People people; 
private String addressName; 
private String codeNumber; 
...
} 

4、數(shù)據(jù)庫結(jié)構(gòu)

people表:{peopleId,name}

address表:{addressId,peopleId,addressName,codeNumber}

5、測試代碼

People people = new People(); 
people.setName("linda"); 
Address address = new Address(); 
address.setAddressName("yunnan"); 
address.setCodeNumber("564123"); 
address.setPeople(people); 
people.getAddresses().add(address); 
Session session = HibernateSessionFactory.getSession(); 
session.beginTransaction(); 
session.save(people); 
session.getTransaction().commit(); 

6、運行結(jié)果

  上面測試代碼運行起來正確:

Hibernate: select max(peopleId) from people
Hibernate: select max(addressId) from address
Hibernate: insert into people (name, peopleId) values (?, ?)
Hibernate: insert into address (addressName, codeNumber, peopleId, addressId) values (?, ?, ?, ?)
Hibernate: update address set peopleId=? where addressId=?

  如果將People.hbm.xml映射改寫一下:

<set name="addresses" cascade="save-update" inverse="true">
<key column="peopleId" not-null="true" />
<o(jì)ne-to-many class="com.persistent.Address"/>
</set>

  不同之處在于添加了inverse="true",結(jié)果:

Hibernate: select max(peopleId) from people
Hibernate: select max(addressId) from address
Hibernate: insert into people (name, peopleId) values (?, ?)
Hibernate: insert into address (addressName, codeNumber, addressId) values (?, ?, ?)

  可以看到,peopleId并沒有寫入到關(guān)聯(lián)的address當(dāng)中,數(shù)據(jù)庫address表中相應(yīng)記錄的peopleId字段為空。

7、分析

  在Hibernate中,術(shù)語inverse是反轉(zhuǎn)的意思,在關(guān)聯(lián)關(guān)系中,inverse="false"為主控方,由主控方負(fù)責(zé)維護(hù)對象的關(guān)聯(lián)關(guān)系。所以上面的映射文件改動之后,address為主控方,people為被控方,但是測試代碼只進(jìn)行了一個保存操作session.save(people),這是針對people的,因此無法正確級聯(lián)保存address。而原來的映射文件中(雖然沒有明確指明,Hibernate默認(rèn)inverse="false"),people為主控方,因此保存people時它會保證關(guān)聯(lián)的address的正確保存。

  也就是說,Hibernate僅僅按照主控方對象的狀態(tài)的變化來同步更新數(shù)據(jù)庫。按照原來的映射文件,people.getAddresses().add(address),即主控方對象的狀態(tài)發(fā)生了改變,因此數(shù)據(jù)庫會跟著對象狀態(tài)的變化來同步更新數(shù)據(jù)庫;而address.setPeople(people),即被控方對象的狀態(tài)發(fā)生了改變,它是不能觸發(fā)對象和數(shù)據(jù)庫的同步更新的。