在測試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ù)庫的同步更新的。