Hibernate一对多双向关联及inverse的作用
在测试Hibernate的一对多双向关联映射时,碰到很有趣的问题,跟inverse属性直接相关。
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"
/>
<
one-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、数据库结构
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、运行结果
上面测试代码运行起来正确:
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"
/>
<
one-to-many
class
="com.persistent.Address"
/>
</
set
>
不同之处在于添加了inverse ="true",结果: 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并没有写入到关联的address当中,数据库address表中相应记录的peopleId字段为空。
7、分析
在Hibernate中,术语inverse是反转的意思,在关联关系中,inverse="false"为主控方,由主控方负责维护对象的关联关系。所以上面的映射文件改动之后,address为主控方,people为被控方,但是测试代码只进行了一个保存操作session.save(people),这是针对people的,因此无法正确级联保存address。而原来的映射文件中(虽然没有明确指明,Hibernate默认inverse="false"),people为主控方,因此保存people时它会保证关联的address的正确保存。
也就是说,Hibernate仅仅按照主控方对象的状态的变化来同步更新数据库。按照原来的映射文件,people.getAddresses().add(address),即主控方对象的状态发生了改变,因此数据库会跟着对象状态的变化来同步更新数据库;而address.setPeople(people),即被控方对象的状态发生了改变,它是不能触发对象和数据库的同步更新的。