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