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

在映射一对多表关系的时候存在一个inverse的问题,一直以来对inverse不怎么了解,今天用到它想要弄明白,在网上看了无数个帖子还是不懂,最后终于看到一位高人的一篇帖子,解释得很透彻,终于彻底了解inverse的含义了,下面说说一对多表关系的应用和inverse详解:

以父与子关系为例:父亲可以有多个孩子,而孩子只能有一个父亲。那么反映到数据表是这样实现的:

createtableparent(

pidintauto_incrementprimarykey,

pnamevarchar(20)notnull

)

createtablechild(

cidintauto_incrementprimarykey,

pidintnotnull,//在孩子表中添加父亲表的外键,实现一对多

cnamevarchar(20)notnull

)

用Hibernate自动生成的两个映射类,以Set集合方式表示一对多,以一个父亲对象方式表示多对一:

publicclassChild{

privateIntegercid;

privateParentparent;//属于哪个父亲

privateStringcname;

...

}

publicclassParent{

privateIntegerpid;

privateStringpname;

privateSetchilds=newHashSet(0);//包含孩子的集合

...

}

关键仍然是两个映射文件:

Child.hbm.xml:

<hibernate-mapping>

<classname="com.pojo.Child"table="child"catalog="test">

<idname="cid"type="java.lang.Integer">

<columnname="cid"/>

<generatorclass="native"/>

</id>

//多对一的属性为parent,对应数据库中的字段为pid,级联更新为all

<many-to-onename="parent"class="com.pojo.Parent"fetch="select"cascade="all">

<columnname="pid"/>

</many-to-one>

<propertyname="cname"type="java.lang.String">

<columnname="cname"length="20"not-null="true"/>

</property>

</class>

</hibernate-mapping>

Parent.hbm.xml:

<hibernate-mapping>

<classname="com.pojo.Parent"table="parent"catalog="test">

<idname="pid"type="java.lang.Integer">

<columnname="pid"/>

<generatorclass="native"/>

</id>

<propertyname="pname"type="java.lang.String">

<columnname="pname"length="20"not-null="true"/>

</property>

<setname="childs"inverse="true"cascade="all">//描述孩子集合,把控制权交给孩子

<key>

<columnname="pid"not-null="true"/>//以Pid来分辨哪个孩子是属于这个父亲的

</key>

<one-to-manyclass="com.pojo.Child"/>

</set>

</class>

</hibernate-mapping>

测试类:

Parentp=newParent();

Childc1=newChild();

Childc2=newChild();

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中有这样的描述:

<setname="childs"inverse="true"cascade="all">

...

</set>

首先说明这个映射文件是描述Parent类的,那么set描述的是Child集合,inverse="true"意为交出控制权,整体意思就是Parent把控制权交给了Child,也就是说由Child来维护表关系。如果inverse="false"表示不交出控制权,那么当然由Parent自己来维护表关系了,值得提到的是在Child映射文件中没有提到由谁来控权,默认的是inverse="false",就是由自己控权,即Child,进一步说当Parent那边inverse="false"不交控权的时候,实际上是两个类都有责任来维护表关系。说到这里可能会问到底控权维护表关系是什么意思呢?下面来看测试类代码:

Parentp=newParent();

Childc1=newChild();

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,所以显然,这个父子关系由孩子来维护比较省力.减轻了数据库的负担。

《inverse的作用:在hibernate中是通过inverse的设置来决定是有谁来维护表和表之间的关系的。

我们说inverse设立不当会导致性能低下,其实是说inverse设立不当,会产生多余重复的SQL语句甚至致使JDBCexception的throw。这是我们在建立实体类关系时必须需要关注的地方。一般来说,inverse=true是推荐使用,双向关联中双方都设置inverse=false的话,必会导致双方都重复更新同一个关系。但是如果双方都设立inverse=true的话,双方都不维护关系的更新,这也是不行的,好在一对多中的一端:many-to-one默认是inverse=false,避免了这种错误的产生。但是多对多就没有这个默认设置了,所以很多人经常在多对多的两端都使用inverse=true,结果导致连接表的数据根本没有记录,就是因为他们双分都没有责任维护关系。所以说,双向关联中最好的设置是一端为inverse=true,一端为inverse=false。一般inverse=false会放在多的一端,那么有人提问了,many-to-many两边都是多的,inverse到底放在哪儿?其实hibernate建立多对多关系也是将他们分离成两个一对多关系,中间连接一个连接表。所以通用存在一对多的关系,也可以这样说:一对多是多对多的基本组成部分。》

------感谢frankensteinlin网友的强帖!

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/hnmoma/archive/2010/05/03/5553528.aspx

相关推荐