在映射一对多表关系的时候存在一个inverse的问题,一直以来对inverse不怎么了解,今天学到它想要弄明白,在网上看了无数个帖子仍是不懂,最后终于看到一位高人的一篇帖子,解释得很透彻,终于完全了解inverse的含义了,历时5个小时!如今若不是在喜悦的掩盖下我早已吐血了。。。下面说说一对多表关系的应用和inverse详解:java
以父与子关系为例:父亲能够有多个孩子,而孩子只能有一个父亲。那么反映到数据表是这样实现的:
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,因此显然,这个父子关系由孩子来维护比较省力.减轻了数据库的负担。