多对多关联映射
1、多对多单向关联映射:
如同一般的数据库设计,多对多的关系需要一个第三方表来维护关系,Hibernate中的映射关系也一样,需要一个中间表一样来维护关系,Hibernate会自动生成中间表。使用<many-to-many>标签来表示多对多的关联,采用集合的方式来映射字段。
单向关联的映射如下图所示:
用户User类:在User类中持有对Role的引用,采用Set集合方式,其映射文件如下:
<hibernate-mapping>
<class name="com.bjpowernode.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"></key>
<many-to-many class="com.bjpowernode.hibernate.Role" column="role_id"></many-to-many>
</set>
</class>
</hibernate-mapping>
在<set>标签中的table就是中间表的名称,key值是在中间表中添加一个字段,名称为user_id,<many-to-many>标签是告诉Hibernate,中间表维护的另一个关系是哪个,column是在中间表中添加一个role_id字段。
由上面的映射文件可知,多对多的关系映射,需要一个中间表来维护,在中间表由有两端的主键组成。因为在单向关联中,只有用户一端持有对角色一端的引用,所以我们可以在用户一端查看都有哪些角色,但是在角色下面就不知道有哪些用户了。下面采用双向关联的关系进行映射,这样在两端都可以相互引用了。
2、多对多双向关联映射:
双向关联是两端都持有对方的引用,在User类中有Role放的引用,在Role方有User的引用,都是采用Set集合的方式进行的。映射文件如下:
<hibernate-mapping>
<class name="com.bjpowernode.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"></key>
<many-to-many class="com.bjpowernode.hibernate.Role" column="role_id"></many-to-many>
</set>
</class>
</hibernate-mapping>
中间表名称为t_user_role,在中间表中加入一个字段user_id
<hibernate-mapping>
<class name="com.bjpowernode.hibernate.Role" table="t_role">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<set name="users" table="t_user_role">
<key column="role_id"></key>
<many-to-many class="com.bjpowernode.hibernate.User" column="user_id"></many-to-many>
</set>
</class>
</hibernate-mapping>
同样维护的中间表为t_user_role,在中间表中添加的字段为role_id,这里关于中间表的信息,两个映射文件中的表名称和字段名称要一致,否则维护的关系就会发生混乱,造成数据冗余。
继承映射
如下就是继承的UML图:
Hibernate的框架设计同样也是基于面向对象的,自然离不开面向对象最主要的特点,继承,在Hibernate中存在三种继承关系的映射:
1、单表继承:每棵类继承树使用一个表
2、具体表继承:每个类一个表
3、类表继承:每个具体类一个表
下面我们介绍这三种继承结构:
单表继承:
所谓的单表继承就是把这三个对象放在一张数据表中存储,所得到的数据库表:
我们把不同类的信息放到同一张表中,就需要不同的字段对不同类信息有一个标识,用来区分是哪类的信息,例如,这里的Type就是这个标识,P代表Pig类,B代表Bird类。
单表继承的映射文件如下:
<hibernate-mapping package="com.bjpowernode.hibernate">
<class name="Animal" table="t_animal" lazy="false">
<id name="id">
<generator class="native"/>
</id>
<discriminator column="type" type="string"/><!-- 这样会在
t_animal表中加入一个type字段,类型为字符串型 -->
<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>
</hibernate-mapping>
父类用<class>标签定义,<discriminator>是用来指定区分字段的名称和类型。子类映射使用<subclass标签discriminator-value属性是区分字段的值。
这种继承策略的映射文件关键就是父类使用<class>标签定义,子类采用<subclass>标签定义。
具体表继承,这种方式采用每个类一张表继承,形成的数据库表如下:
我们把父类和子类的信息都单独的放在一张表中,每个表中的信息都是自己独有的信息,映射文件如下:
<hibernate-mapping package="com.bjpowernode.hibernate">
<!-- 每个类一张表的映射 -->
<class name="Animal" table="t_animal">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<property name="sex"/>
<joined-subclass name="Pig" table="t_pig">
<key column="pid"/>
<property name="weight"/>
</joined-subclass>
<joined-subclass name="Bird" table="t_bird">
<key column="bid"/>
<property name="height"/>
</joined-subclass>
</class>
</hibernate-mapping>
这种映射父类同样采用<class>标签,但是不需要在使用<discriminator>标签了,子类则使用<joined-subclass>标签,其中需要一个<key>标签来指定子类和父类之间是通过哪个字段联系的。
类表继承:最后一种继承映射的方式是每个具体类一张表,如图:
映射文件如下:
<hibernate-mapping package="com.bjpowernode.hibernate">
<class name="Animal" table="t_animal" abstract="true"><!-- 设置为抽象的这样就不会在生成这个表 -->
<id name="id">
<generator class="assigned"/><!-- 生成策略为手动方式 -->
</id>
<property name="name"/>
<property name="sex"/>
<union-subclass name="Pig" table="t_pig">
<property name="weight"/>
</union-subclass>
<union-subclass name="Bird" table="t_bird">
<property name="height"/>
</union-subclass>
</class>
</hibernate-mapping>
这种策略的映射文件子类采用<union-subclass>标签定义,子类包含的表既包含了从父类继承下来的属性,又包括自己的属性,在<union-subclass>标签中不要指定key值,设置主键的生成策略为assigned手动方式。虽然在这个标签中只有子类的属性,但是因为它继承了父类,所以在映射数据库表的时候会自动映射父类的属性。
这三种映射各有优缺点,根据数据库的设计三范式,我们一般采用第三种方式,减少数据库冗余。采用单表继承时父类使用<class>标签,子类使用<subclass>标签,采用具体表继承父类使用<class>标签,不需要使用<discriminator>标签,子类采用<joined-subclass>标签,需要设置key值,采用类表继承,父类采用<class>标签定义,子类使用<union-subclass>标签,其中的属性只有子类的独有属性,主键的生成策略采用手动设置。