今天我们一起来探讨一下有关Hibernate四种关系映射,大家可能会有疑惑,关系映射不就是只有三种(一对一,一对多,多对多)吗?这里我们再多说一个继承映射关系,虽然它使用频繁度没有前三个那么多,但是它在一些特定情况下,还是非常有用的,至于有什么用下面马上就要讲。这些关系映射实际上就是对应着关系型数据库中的几种关系的类型。我们都知道Hibernate的作用是将面向关系数据操作,转换成面向对象的操作,其中关系映射文件就是实现这个转换的核心。我们都知道如果我们仅仅使用SQL语句建表并且去维护表与表之间一对一,一对多,多对多关系,这样单纯SQL操作是很简单的。我们通过上篇博客就知道我们要实现的是写好Bean类,然后对应配置好相应的映射文件,运行测试类后就可以实现自动在数据库建立数据表。而我们现在就要实现,如何利用Bean类和映射文件自动建立多个表,并且还要体现出多个表之间的一对一,一对多,多对多的关系。
说白的一点,就是要通过编写多个不同的Bean类(一个Bean类就是对应一张表),和配置映射文件,实现自动创建带有关系(一对一、一对多、多对多关系)的数据表。那我们开始第一个。
实际上通过我们上篇博客我们不难发现,实现表的建立主要体现两个方面,一个是Bean类(两个Bean类之间首先要区分:主表类和从表类),另一个则是Bean类对应的映射文件(首先需要区分:主方和从方),所以接下来的每一种映射关系的讨论也将围绕着这几个方面来展开。
第一种关系:如何去自动建立一个一对一的关系数据表:
注意:在建立一对一映射关系的数据表,实际上有两种方法来实现:一种是通过主键来映射关系,另一种则是通过外键来映射关系。
第一种实现方式:通过主键的方式映射关系:实际上就是通过主表的主键来生成从表的主键,这就是所谓主键生成方式,也就是从表的主键生成机制是有限制,必须要参照主表的中的主键,不可能出现主表中不存在的主键。然后通过主表的主键来寻找从表的主键,由于主键是不可能重复,所以也就形成通过主键的映射方式来实现和维护了两个表之间的一对一的关系。
1、在Bean类中的体现:假如有Person类和Card类两个Bean类,来自动创建两张具有一对一关系的数据表。Person类为主方,Card类为从方
这里面还分为单向维护和双向维护,单向维护就是在主方类中保存一个从方类中的引用或者在从方类中保存一个主方类的引用。为了讲解全面点,我们全都以双向维护为例。
可能有人会有疑问,什么是单向维护什么又是双向维护,单向维护可以通过维护的主动方来找到被维护的一方;而双向维护则是双方都能互相找到。
Person类
package com.mikyou.day05.one2one;
/**
* @Mikyou
* @Person主方类
* 注意:
* 1、在编写Person类时,不要把从方类的引用写入到构造器中
* 2、两个构造器:一个无参数构造器,另一个则是有参数的构造器
* 3、在重写toString方法的时候,注意不要把从方表引用写进去。
* */
public class Person {
private Long id;
private String name;
private Card card;//保存着从方类Card的一个引用
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Card getCard() {
return card;
}
public void setCard(Card card) {
this.card = card;
}
public Person(Long id, String name) {
super();
this.id = id;
this.name = name;
}
public Person() {
// TODO Auto-generated constructor stub
}
@Override
public String toString() {
return "Person [id=" + id + ", name=" + name + ", card=" + card + "]";
}
}
Card类:
package com.mikyou.day05.one2one;
/**
* @Mikyou
* @Card从方类
* 注意:
* 1、在编写Card类时,不要把主方类的引用写入到构造器中
* 2、两个构造器:一个无参数构造器,另一个则是有参数的构造器
* 3、在重写toString方法的时候,注意不要把主方表引用写进去。
* */
public class Card {
private Long id;
private String num;//身份证号
private String address;//地址
private Person person;//保存主方类的一个引用
public Card(Long id, String num, String address) {
super();
this.id = id;
this.num = num;
this.address = address;
}
public Card() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getNum() {
return num;
}
public void setNum(String num) {
this.num = num;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
}
2、在映射文件中的体现:
主方表映射文件person.hbm.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.mikyou.day05.one2one">
<class name="Person" table="tbl_person">
<id name="id" column="id">
<generator class="increment" />
</id>
<property name="name" />
<!-- 在主方一对一 配置one-to-one-->
<one-to-one name="card" class="Card" />
</class>
</hibernate-mapping>
从方类的映射文件card.hbm.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.mikyou.day05.one2one">
<class name="Card" table="tbl_card">
<id name="id" column="id" >
<generator class="foreign"><!-- 以外键的形式来产生主键 -->
<param name="property">person</param>
</generator>
</id>
<property name="num" />
<property name="address" />
<one-to-one name="person" class="Person" constrained="true"></one-to-one><!-- constrained="true"表示从表中的主键必须依赖主表中的主键 -->
</class>
</hibernate-mapping>
注意:在配置主键关系来维护一对一的关系的时候,在映射文件中的表现,首先双方都通过<one-to-one>来维护关系。在主方person.hbm.xml映射文件中
配置<one-to-one name="card" class="Card" />其中的name对应着Person类中保存的card引用,然后对应card引用的Class,Card类。在从方card.hbm.xml映射文件中也是配置
<one-to-one>但是需要加上constrained="true"属性,表示从表card主键必须依赖主表person的主键,此外在card.hbm.xml中它的主键生成方式不再是increment自增的方式了,而是 <id name="id" column="id" ><generator class="foreign"><!-- 以外键的形式来产生主键 --><param name="property">person</param><!-- --></generator></id>
从表中的主键引用了主表中的主键,所以需要在主键生成方式中设置成foreign,并将参数传进去,告诉hibernate应该使用哪一个主键。在配置上,双方都为one-to-one
第二种实现方式:通过外键来映射关系:(注意:只要是通过外键来映射关系的,必须在主表和从表中都要有体现)
这第二种方式可以说是维护一对一关系常用的方式,第一种通过主键来映射的方式使用的情况不是很多。通过外键来映射关系主要是通过映射文件来体现的,它的Bean类表现形式和第一种情况一致。它的映射文件主要怎么体现呢?它是在主方表中配置一个<one-to-one>并且要添加一个属性<propery-ref>,在从表中配置一个<many-to-one>,为什么要配置一个<many-to-one>这不是一对多吗?我们知道一对一实际上就是一对多的一个特例,所以我们可以将一个一对多的情况转换成一个一对一的情况只需要在从表中的<many-to-one>中配置一个unique属性设为true即可并且还得指定一个属性作为外键通过column属性来指定,但这种方式下的主键生成方式没有要求就采取自增方式即可。
person.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.mikyou.day05.one2one">
<class name="Person" table="tbl_person">
<id name="id" column="id">
<generator class="increment" />
</id>
<property name="name" />
<!-- 在主方一对一,因为在主表和从表都要有体现,使用property-ref="person" 因为Card类中参考的是person属性,通过从表中的外键去匹配主表中主键-->
<one-to-one name="card" class="Card" property-ref="person" />
</class>
</hibernate-mapping>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.mikyou.day05.one2one">
<class name="Card" table="tbl_card">
<id name="id" column="id">
<generator class="increment" />
</id>
&l