首先,关系映射,这里并不是指的数据库表之间的关联关系,关系数据库无非就是table,而table之间的关系就只有一种就是,外键关联!,而在Hibernate中指的关联关系值得是对象之间的关联关系,而且指的是一种数量上的关系。主要有七大类:一对一单项,一对一双向,一对多单项,一对多双向,多对一单项,多对多单项,多对多双向。
还有一个要特别说明的就是,无论什么关联,一般我们不是讨论的数据库表的模型,而是说明的是编程模型,比方说我们在对一对一时,不管是单向,还是双向,其实数据库应该是没有区别的,有区别的应该只是我们的程序逻辑。
本文主要讲一对一关联,包括:1、一对一单向外键关联 2、一对一双向外键关联 3、一对一单向主键关联 4、一对一双向主键关联
1:一对一单项外键关联
1.1:Annotation版本
设置两个类:Husband 和Wife。
@Entity
public class Husband {
private int id;
private String name;
private Wife wife;
@Id
@GeneratedValue
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@OneToOne
@JoinColumn(name="wifeId")
public Wife getWife() {
return wife;
}
public void setWife(Wife wife) {
this.wife = wife;
}
}
@Entity
public class Wife {
private int id;
private String name;
@Id
@GeneratedValue
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
我们在Husband中
使用OneToOne表示这是一个一对一的关联,这样会在我们的表husband中添加一个字段,wifeId,它引用的是我们wife表的一个ID。这样我们从Husband就可以找到对应的Wife,但是从Wife却不可以找到对应的Husband。看数据库生成的语句如下所示:
19:45:27,522 DEBUG SchemaExport:377 -
create table Husband (
id integer not null auto_increment,
name varchar(255),
wifeId integer,
primary key (id)
)
19:45:27,581 DEBUG SchemaExport:377 -
create table Wife (
id integer not null auto_increment,
name varchar(255),
primary key (id)
)
19:45:27,637 DEBUG SchemaExport:377 -
alter table Husband
add index FKAEEA401B9E920219 (wifeId),
add constraint FKAEEA401B9E920219
foreign key (wifeId)
references Wife (id)
19:45:27,773 INFO SchemaExport:268 - schema export complete
1.2
:Xml
版本(不建议使用,xml过于繁琐,用的不多)
此版本代码很简单,将上面类中所有的Annotation去掉,添加两个xml文件:Husband.hbm.xml和Wife.hbm.xml接口,生成的数据库一模一样。
Husband.hbm.xml
<hibernate-mapping package="com.zxb.model">
<class name="Husband" table="husband" dynamic-update="true">
<id name="id" column="id">
<generator class="identity"></generator>
</id>
<property name="name" column="name" />
<many-to-one name="wife" column="wife_id" unique="true"></many-to-one>
</class>
</hibernate-mapping>
在这里我们发现,在一对一单向外键关联中,我们使用的是many-to-one,然后加上unique=“true”来约束成one-to-one的。
Wife.hbm.xml
<hibernate-mapping package="com.zxb.model">
<class name="Wife" table="wife" dynamic-update="true">
<id name="id" column="id">
<generator class="identity"></generator>
</id>
<property name="name" column="name" />
</class>
</hibernate-mapping>
2:一对一双向外键关联
首先说明,如果在Husband中都有外键字段的话,那么这种设计本身就是一种失败的设计。数据库库设计的本身就不合理。下面讲解只有一个外键的双向外键关联。
2.1:Annotation方式:
继续上面的例子,既然是双向的关联,那么从wife出发要可以找到husband,那么wife中就一定要有husband的引用,前面我们说明,不管怎么变,我们的关联映射不是针对数据库的结构的,而是针对变成模型的,所以,我们Husband类不变,生成的数据库表也没有任何变化,唯一的变化在于Wife类中多了一个husband的引用。代码如下:
@Entity
public class Wife {
private int id;
private String name;
private Husband husband;
@Id
@GeneratedValue
public int getId() {
return id;
}
public String getName() {
return name;
}
@OneToOne(mappedBy="wife")
public Husband getHusband() {
return husband;
}
public void setHusband(Husband husband) {
this.husband = husband;
}
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
}
在上面的类中,我们加入了一个husband的引用,而是用OneToOne来标记一对一关系,但是为了避免数据库表中出现两个外键字段,我们在这里加入了一个mappedBy,(记住,凡是涉及到双向的关联,必然会设置mappedBy,虽然不是必须,但是这是一个最佳实践!),表明我们的关联关系在Husband中设置了,而且Husband是根据Husband的wife属性来设置的。
2.2:xml版本:
由于我们相对于一对一单向的只改变了Wife类,所以我们一对一双向外键关联也只需要在一对一单向外键关联的基础上改变一下wife的xml文件即可。注意,我们在husband中使用的是many-to-one,在wife.hbm.xml中我们使用的是one-to-one,同时,充当我们在Annotation中的mappedBy的是property-ref属性。代码如下:
<hibernate-mapping package="com.zxb.model">
<class name="Wife" table="wife" dynamic-update="true">
<id name="id" column="id">
<generator class="identity"></generator>
</id>
<property name="name" column="name" />
<one-to-one name="husband" property-ref="wife"></one-to-one>
</class>
</hibernate-mapping>
3.一对一单向主键关联(从来不使用!!!)
3.1:Annotation版本:
此种类型,是不需要添加任何字段的,Husband类的主键直接参考Wife类的主键。代码如下:
@Entity
public class Husband {
private int id;
private String name;
private Wife wife;
@Id
@GeneratedValue
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@OneToOne
@PrimaryKeyJoinColumn(name="wife_id")
public Wife getWife() {
return wife;
}
public void setWife(Wife wife) {
this.wife = wife;
}
}
@Entity
public class Wife {
private int id;
private String name;
@Id
@GeneratedValue
public int getId() {
return id;
}
public String getName() {
return name;
}
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
}
测试输出如下:
20:41:34,076 DEBUG SchemaExport:377 -
create table Husband (
id integer not null auto_increment,
name varchar(255),
primary key (id)
)
20:41:34,253 DEBUG SchemaExport:377 -
create table Wife (
id integer not null auto_increment,
name varchar(255),
primary key (id)
)
20:41:34,400 INFO SchemaExport:268 - schema export complete
可以发现,Wife类没有任何改变,只是在Husband中的wife不再是用@JoinColum来标注,而是使用@PrimaryKyeJoinColumn来标注即可。但是,从输出来看,一对一单向主键关联并没有起作用。这也是是hibernate的一个bug!!
3.2:XML版本:
此种版本下,Husband类中有wife的引用,可以根据husband的id找到对应的wife,但是由于是单向,所以,在Wife类中没有husband的引用,虽然Husband中主键参照了Wife的主键,但是从wife是不可以找到对应的husband的。
对应的xml配置文件如下:
Wife.hbm.xml:
<hibernate-mapping package="com.zxb.model">
<class name="Wife" table="wife" dynamic-update="true">
<id name="id" column="id">
<generator class="identity"></generator>
</id>
<property name="name" column="name" />
</class>
</hibernate-mapping>
Husband.hbm.xml:
<hibernate-mapping package="com.zxb.model">
<class name="Husband" table="husband" dynamic-update="true">
<id name="id" column="id">
<generator class="foreign">
<param name="property">wife</param>
</generator>
</id>
<property name="name" column="name" />
<one-to-one name="wife" constrained="true"></one-to-one>
</class>
</hibernate-mapping>
在上面的Husband中,主键的生成器是foreign,并且在one-to-one中,我们配置了constrained=“true”来约束Husband类的主键参考的是Wife的主键。
运行测试代码:生成表的语句如下:可以发现,一对一单向主键关联下的主键约束在xml环境下是可以测试通过的。
20:52:43,121 DEBUG SchemaExport:377 -
create table husband (
id integer not null,
name varchar(255),
primary key (id)
)
20:52:43,186 DEBUG SchemaExport:377 -
create table wife (
id integer not null auto_increment,
name varchar(255),
primary key (id)
)
20:52:43,253 DEBUG SchemaExport:377 -
alter table husband
add index FK4BB1A83BCD89D348 (id),
add constraint FK4BB1A83BCD89D348
foreign key (id)
references wife (id)
20:52:43,419 INFO SchemaExport:268 - schema export complete
4.1:Annotation方式:
很简单,在wife类中也加一个husband的引用,同时也加上@PrimaryKeyJoinColumn即可。此处只给出改变了的wife类(重申一遍,生成的数据库没有任何其他变化,但是反映在我们编程模型中就会有差别。)
@Entity
public class Wife {
private int id;
private String name;
private Husband husband;
@Id
@GeneratedValue
public int getId() {
return id;
}
public String getName() {
return name;
}
@OneToOne
@PrimaryKeyJoinColumn
public Husband getHusband() {
return husband;
}
public void setHusband(Husband husband) {
this.husband = husband;
}
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
}
运行测试代码结果:
21:04:55,286 DEBUG SchemaExport:377 -
create table Husband (
id integer not null auto_increment,
name varchar(255),
primary key (id)
)
21:04:55,339 DEBUG SchemaExport:377 -
create table Wife (
id integer not null auto_increment,
name varchar(255),
primary key (id)
)
21:04:55,399 INFO SchemaExport:268 - schema export complete
4.2:XML方式:
Wife.hbm.xml:
<hibernate-mapping package="com.zxb.model">
<class name="Wife" table="wife" dynamic-update="true">
<id name="id" column="id">
<generator class="identity"></generator>
</id>
<property name="name" column="name" />
<one-to-one name="husband" property-ref="wife"></one-to-one>
</class>
</hibernate-mapping>
Husband.hbm.xml:
<hibernate-mapping package="com.zxb.model">
<class name="Husband" table="husband" dynamic-update="true">
<id name="id" column="id">
<generator class="foreign">
<param name="property">wife</param>
</generator>
</id>
<property name="name" column="name" />
<one-to-one name="wife" constrained="true"></one-to-one>
</class>
</hibernate-mapping>
运行如上代码,可以发现,约束条件可以成功的建立。