您的位置:首页 > 其它

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

2013-03-06 11:02 281 查看
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,所以显然,这个父子关系由孩子来维护比较省力.减轻了数据库的负担。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: