hibernate是持久层ORM框架,关系映射解决了对象型实体与关系型数据库不相匹配的问题,它是Hibernate的核心内容,在实际操作中我们必须要根据数据表的相互关系,根据业务需求,建立相互适应的关系映射,这样才能提高开发效率和系统运行效率。
关系映射主要包括以下:
关系型映射分为单向和双向映射两种,上图中除了继承和component映射均可以建立单向和双向两种映射方式。而且1:N这种映射如果建立单向映射就会存在很多问题,让我们来分析一下各个映射应用场景的实现方式。
(1)多对一映射
如上图,User和Group属于N:1映射,单向的多对一映射,要求我们访问User时能够找到对应的Group,所以考虑在多的一方t_user添加groupid,通过维护grouid来维护user与Group之间的关系,在代码实现过程中,User类中添加Group类的引用,xml配置gruopid:
<class name="User" table="t_user">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<many-to-one name="group" column="groupId" not-null="true">
</class>
(2) 一对一映射
一对一关系映射分为主键关联映射和唯一外键关联映射两种,主键关联映射通过共享主键的方式,不需要添加维护字段,靠主键相等来维护数据表关系。
单向映射,只需要在Person中添加IdCard的引用,其xml映射文件如下:
<class name="com.whp.hibernate.Person" table="t_table">
<id name="id">
<generator class="foreign">
<param name="property">idCard</param>
</generator>
</id>
<property name="name"/>
<one-to-one name="idCard" constrained="true"/> <!--constrained为约束-->
</class>
双向映射,则需要在IdCard中添加person引用:
<class name="com.whp.hibernate.IdCard" table="t_idcard">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<one-to-one name="person"/>
</class>
一对一外键关联映射在一方添加维护字段,使其保持唯一性,与对应一方建立唯一数据联系。对比于主键关联映射,它更具有扩展性,可以随时更改为多对一关系。
<class name="com.whp.hibernate.Person" table="t_pserson">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<many-to-one name="idCard" unique="true"/>
</class>
<class name="com.whp.hibernate.IdCard" table="t_idcard">
<id name="id">
<generator class="native"/>
</id>
<property name="cardNo"/>
<one-to-one name="person" property-ref="idCard"/>
</class>
(3)一对多关系
上面有提到一对多映射存在很多问题,我们从其映射图来分析:
在单向的多对一关系映射中,班级属于一,当插入classes中字段时,首先插入其基本属性的数据,然后通过大量的更新语句来更新student实体,这样如果一个班级的学生人数很多的情况下,班级表的插入语句将引发大量的update语句造成执行效率下降。单纯由classes来维护关系,student实体将不知道自己对应的哪个班级,因此多数情况下的一对多关系都是双向关联映射。
好的实现方式是在一的一端加入set,由多的一端维护关系:
<class name="com.whp.hibernate.Classes" table="t_classes">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<set name="students" inverse="true">
<key clomun="classesid"/>
<one-to-many class="com.whp.hibernate.Student"/>
</set>
</class>
<class name="com.whp.hibernate.Student" table="t_student">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<many-to-one name="classes" column="classesid"/>
</class>
由上面实现过程,Classes的set属性中添加inverse="true"属性让出控制权,让控制反转,交给student来维护关系。
(4)多对多关系
多对多关系则需要抽象出第三张维护表来维护关系
<class name="com.whp.hibernate.User" table="t_user">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<set name="roles" table="t_user_role">
<key column="user_id"/>
<many-to-many class="com.whp.hibernate.Role" column="role_id"/>
</set>
</class>
(5)继承映射
继承映射通常有两种方式实现,第一种是在同一张表中添加各个子类的冗余字段和标识字段;第二种方式是建立多张表与维护字段。第一种方式通过一张表维护,执行效率高,但存在冗余字段,且这些冗余字段必须允许为空;第二种方式该类必须实现序列化,只有这样才能让数据跨网络传输,将数据保存到本地,尤其在分布式系统应用中极为重要。
第一种方式实现方式:
<class name="Animal" table="t_animal" lazy="false">
<id name="id">
<generator class="native"/>
</id>
<discriminator column="type" type="string"/>
<property name="name"/>
<property name="sex"/>
<subclass name="Pig" discriminator-value="P">
<property name="weight"/>
</subclass>
<subclass name="Bird" discriminator-value="B">
<property name="height"/>
</subclass>
</class>
(6)component映射
component映射属于细粒度划分,提高复用率,如上图中抽出公共属性部分放入Contact中,可以分别组合User和Employee。
<class name="com.bjpowernode.hibernate.Employee" table="t_emplyee">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<component name="employeeContact">
<property name="email"/>
<property name="address"/>
<property name="zipCode"/>
<property name="contactTel"/>
</component>
</class>
<class name="com.bjpowernode.hibernate.User" table="t_user">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<component name="userContact">
<property name="email"/>
<property name="address"/>
<property name="zipCode"/>
<property name="contactTel"/>
</component>
</class>