Hibernate表关系之一对多(inverse详解)

来源:互联网 发布:淘宝卖家服务热线 编辑:程序博客网 时间:2024/05/02 01:12

http://hi.baidu.com/gym_1119/item/6752e1d77b479b4cfa576873

在映射一对多表关系的时候存在一个inverse的问题,一直以来对inverse不怎么了解,今天学到它想要弄明白,在网上看了无数个帖子还是不懂,最后终于看到一位高人的一篇帖子,解释得很透彻,终于彻底了解inverse的含义了,历时5个小时!现在若不是在喜悦的掩盖下我早已吐血了。。。下面说说一对多表关系的应用和inverse详解:
以父与子关系为例:父亲可以有多个孩子,而孩子只能有一个父亲。那么反映到数据表是这样实现的:
create table parent(
pid int auto_increment primary key,
pname varchar(20) not null
)
create table child(
cid int auto_increment primary key,
pid int not null,       //在孩子表中添加父亲表的外键,实现一对多
cname varchar(20) not null
)

用Hibernate自动生成的两个映射类,以Set集合方式表示一对多,以一个父亲对象方式表示多对一:
public class Child {
private Integer cid;
private Parent parent;     //属于哪个父亲
private String cname;
...
}
public class Parent {
private Integer pid;
private String pname;
private Set childs = new HashSet(0);     //包含孩子的集合
...
}

关键仍然是两个映射文件:
Child.hbm.xml:
<hibernate-mapping>
     <class name="com.pojo.Child" table="child" catalog="test">
         <id name="cid" type="java.lang.Integer">
             <column name="cid" />
             <generator class="native" />
         </id>
        //多对一的属性为parent,对应数据库中的字段为pid,级联更新为all
        <many-to-one name="parent" class="com.pojo.Parent" fetch="select" cascade="all">
             <column name="pid" />
         </many-to-one>
         <property name="cname" type="java.lang.String">
             <column name="cname" length="20" not-null="true" />
         </property>
     </class>
</hibernate-mapping>

Parent.hbm.xml:
<hibernate-mapping>
     <class name="com.pojo.Parent" table="parent" catalog="test">
         <id name="pid" type="java.lang.Integer">
             <column name="pid" />
             <generator class="native" />
         </id>
         <property name="pname" type="java.lang.String">
             <column name="pname" length="20" not-null="true" />
         </property>
       <set name="childs" inverse="true" cascade="all">   //描述孩子集合,把控制权交给孩子
             <key>
                 <column name="pid" not-null="true" />   //以Pid来分辨哪个孩子是属于这个父亲的
             </key>
             <one-to-many class="com.pojo.Child" />
         </set>
     </class>
</hibernate-mapping>

测试类:
Parent p=new Parent();
Child c1=new Child();
Child c2=new Child();
p.setPname("xpc5");
c1.setCname("cloud");
c1.setParent(p);     //设置孩子1的父亲为p
c2.setCname("cloud1");
c2.setParent(p);     //设置孩子2的父亲为p
p.getChilds().add(c1);     //为父亲添加孩子1
p.getChilds().add(c2);     //为父亲添加孩子2

session.save(p);

运行后在插入一个父亲记录的同时,两个孩子记录被自动插入

下面详细说明一下inverse的作用:
inverse是反转的意思,在Hibernate中inverse决定谁掌有控制权,即表之间的关系谁来维护,那么当inverse="true"表示交出控制权,inverse="false"表示不交出控制权,这样理解有些难度,网上大多对此块讲解不清楚,下面以我自己的理解说明,首先来看实例,还以上面为例,在Parent.hbm.xml中有这样的描述:
<set name="childs" inverse="true" cascade="all"> 
...
</set>
首先说明这个映射文件是描述Parent类的,那么set描述的是Child集合,inverse="true"意为交出控制权,整体意思就是Parent把控制权交给了Child,也就是说由Child来维护表关系。如果inverse="false"表示不交出控制权,那么当然由Parent自己来维护表关系了,值得提到的是在Child映射文件中没有提到由谁来控权,默认的是inverse="false",就是由自己控权,即Child,进一步说当Parent那边inverse="false"不交控权的时候,实际上是两个类都有责任来维护表关系。说到这里可能会问到底控权维护表关系是什么意思呢?下面来看测试类代码:
Parent p=new Parent();
Child c1=new Child();
p.setPname("xpc5");
c1.setCname("cloud");
c1.setParent(p);   
p.getChilds().add(c1);   
session.save(p);
这样写无论谁负责维护数据插入都没问题,那么我们改动一下试试,现在由Child负责维护,c1.setParent(p)是Child类与Parent类关联的关键,那么把句注释掉再试试,运行后执行语句是一样的,但是查看表记录你会发现Parent表能正确插入记录,而Child表中pid字段为空,这正是因为c1.setParent(p)这句是Child与Parent关联的关键而你又把它注释了,做为控权的Child没有设置与Parent关联,那么这两表之间的联系就消失了,即便有p.getChilds().add(c1)这句也无法实现关联,因为p不是控权。而当你修改Parent映射文件中的inverse="false"的时候,父亲和孩子都负责维护,那么什么都不动再试上面的程序,执行后的结果双方都可正确被插入,因为此时有p.getChilds().add(c1)这句存在就可以了,因为Parent也有控权。

那么为什么要使用inverse呢,让双方都有控权不好吗?下面来看,当Parent负责维护关系的时候,由于它包含一个孩子集合,它无法知道哪些孩子已经指向自己了,所以为了确保正确性,它要更新所有孩子的pid,让它指向自己,这样就引出了效率问题,若存在100个孩子,那么就无形中多做了100个update,所以显然,这个父子关系由孩子来维护比较省力.减轻了数据库的负担。

原创粉丝点击