从使用结果来看,使用单,双向关联效果是一样的。
效率方面哪个会更好些呢?以例测试:
先建立实体 Room 和 UserInfo的一对多关联。其中Room表示住房信息,UserInfo表示住客信息,一个房间可以住多个客户。
实体房间(Room)与实体人(UserInfo)的代码
Room.java
public class Room {
//房间实体的主键对应
private long id;
//房间号
private String roomnumber;
//房间名称
private String name;
// 房间中的人(这是一个Set类型的集合)
private Set Users;
//get/set方法
...........
}
UserInfo.java
public class UserInfo {
//实体人的主键
private long id;
// ...
private String name;
// ...
private String sex;
//实体房间的主键
private long roomid;
//get/set方法
...
}
实体房间和实体人的映射文件
Room.hbm.xml
<?xml version='1.0' encoding='UTF-8‘?>
<!DOCTYPE ...>
<hibernate-mapping package="testhibernate">
<class name="Room" table="Room">
<id name="id" column="id" type="java.lang.Long">
<generator class="increment" />
</id>
<property name="name" column="name" type="java.lang.String">
<property name="roomnumber" column="roomnumber" type="java.lang.String" />
<!-- 一对多映射 -->
<set name="users" cascade="all">
<key column="roomid"></key>
<one-to-many class="UserInfo"></one-to-manay>
</set>
</class>
</hibernate-mapping>
UserInfo.hbm.xml
<?xml version='1.0' encoding='UTF-8‘?>
<!DOCTYPE ...>
<hibernate-mapping package="testhibernate">
<class name="UserInfo" table="userinfo">
<id name="id" column="id" type="java.lang.Long">
<generator class="increment" />
</id>
<property name="name" column="name" type="java.lang.String">
<property name="sex" column="sex" type="java.lang.String" />
<property name="roomid" column="roomid" type="java.lang.Long" />
</class>
</hibernate-mapping>
由以上配置,由于在Hibernate映射文件总配置了级联(cascade="all"), 因此只需对Room实体进行持久化操作,会关联持久化UserInfo实体。经测试以上设置打印出来的SQL文为:
Hibernate:
select
max(id)
from
room
Hibernate:
select
max(id)
from
UserInfo
Hibernate:
insert into room(name, roomnumber, id) values(?,?,?)
Hibernate:
insert into userinfo(name,sex,roomid,id) values(?,?,?,?)
Hibernate:
update userinfo set roomid=? where id=?
可以看出,Hibernate在处理一对多单向关联时,是通过三句SQL来完成的,首先是插入主表(room表) 然后是插入子表(userinfo表),最后更新子表的关联字段(userinfo表的roomid字段)为主表的主键(room表的id字段)。
注意:当room表与userinfo表设置外键关联时,那么这段代码就会抛出"未找到父项关键字"的异常。为了解决这个问题,可以修改映射文件UserInfo.hbm.xml文件,使得其在插入userinfo表时不对roomid进行插入。修改后UserInfo.hbm.xml如下
UserInfo.hbm.xml
<?xml version='1.0' encoding='UTF-8‘?>
<!DOCTYPE ...>
<hibernate-mapping package="testhibernate">
<class name="UserInfo" table="userinfo">
<id name="id" column="id" type="java.lang.Long">
<generator class="increment" />
</id>
<property name="name" column="name" type="java.lang.String">
<property name="sex" column="sex" type="java.lang.String" />
<property name="roomid" column="roomid" type="java.lang.Long" insert="false" update="false"/>
</class>
</hibernate-mapping>
通过设置insert和update属性默认状态为true,设置为false表示该字段是该外源性的字段
单向关联满足了一定的业务要求,但是当抓取UserInfo实体而又要同时抓取Room实体的业务时就无法被满足。此时就需要进行双向关联的设置。
Hibernate的双向关联
Hibernate的双向关联除了在主表(如上的room表)的映射文件中设置一对多外,还需要在从表(如以上userinfo表)设置多对一,首先要在UserInfo.java实体类中增加一个Room实体类型的属性其代码实现见例
UserInfo.java
public class UserInfo {
private long id;
private String sex;
private String name;
private long roomid;
private Room room;
// set/get方法
... ...
}
UserInfo.hbm.xml变化如下:
UserInfo.hbm.xml
<?xml version='1.0' encoding='UTF-8‘?>
<!DOCTYPE ...>
<hibernate-mapping package="testhibernate">
<class name="UserInfo" table="userinfo">
<id name="id" column="id" type="java.lang.Long">
<generator class="increment" />
</id>
<property name="name" column="name" type="java.lang.String">
<property name="sex" column="sex" type="java.lang.String" />
<many-to-one name="room" column="roomid" class="Room" />
</class>
</hibernate-mapping>
执行操作,其显示的SQL语句如下:
Hibernate:
insert into room(name, roomnumber,id) values(?,?,?)
Hibernate:
insert into userinfo(name,sex, roomid, id) values(?,?,?,?)
Hibernate:
update userinfo set roomid=? where id=?
可以看到通过Room实体获取UserInfo实体,再反向获取Room实体完成。
这样的实现并没达到最好的效果。因为SQL执行插入时总是执行三句SQL。还有更好的方法那就是在配置文件中加入inverse属性。
修改Room.hbm.xml
Room.hbm.xml
<?xml version='1.0' encoding='UTF-8‘?>
<!DOCTYPE ...>
<hibernate-mapping package="testhibernate">
<class name="Room" table="Room">
<id name="id" column="id" type="java.lang.Long">
<generator class="increment" />
</id>
<property name="name" column="name" type="java.lang.String">
<property name="roomnumber" column="roomnumber" type="java.lang.String" />
<!-- 一对多映射 -->
<set name="users" cascade="all" inverse="true">
<key column="roomid"></key>
<one-to-many class="UserInfo"></one-to-manay>
</set>
</class>
</hibernate-mapping>
执行显示sql文如下:
Hibernate:
insert into room(name,roomnumber,id) values(?,?,?)
Hibernate:
insert into userinfo(name,sex,roomid,id) values(?,?,?,?)
注:执行插表语句中的UserInfo.setRoom(room);必须调用不然可能导致插入的roomid为null;
总结:单向关联的功能比双向关联要弱,而且单向关联在操作数据库表时总是会执行三句SQL,因此在一般设计和实现中,通常应该优先考虑使用双向关联。而使用双向关联时,inverse属性也是不能忽视的一个重点。