一、实体关系概述
实体关系是指实体与实体之间的关系,从方向上分为单向关联和双向关联;从实体数量上分一对一,一对多和多对多。
1、实体关系的方向性
(1)单向关联
单向关联是一个实体中引用了另外一个实体。简单地说,就是通过一个实体可以获得另一个实体对象。
例如,实体A对实体B的单向关联。
实体A的代码如下:
01 |
public class EntityA {<span style="font-family:Verdana;"></span> private EntityB entityB; | |
02 |
|
03 |
public EntityB getEntityB() { | |
04 |
return entityB; |
05 |
} | |
06 |
|
07 |
public void setEntityB(EntityB entityB) { | |
08 |
this.entityB = entityB; |
09 |
} | |
10 |
} |
实体B的代码如下:
1 |
public class EntityB {} |
(2)双向关联
此时实体A的代码不变,将实体B的代码修改为:
1 |
public class EntityB { | |
2 |
private EntityA entityA; |
3 |
//省略getter、setter方法 | |
4 |
} |
2、实体关系的数量性
(1)一对一
一对一是一个实体只能获得一个实体的引用,如上所述
(2)一对多
一个实体可以获得多个实体的引用,如:
1 |
public class EntityA { | |
2 |
private Collection<EntityB> entitys; |
3 |
// 省略getter、setter方法 | |
4 |
} |
(3)多对多
此时实体A代码如一对多一样,而实体B代码改为:
1 |
public class EntityB { | |
2 |
private Collection<EntityA> entitys; |
3 |
// 省略getter、setter方法 | |
4 |
} |
二、一对一(@OneToOne)
下面以客户实体(CustomerEO)与地址实体(AddressEO)为例
1、单向关联
假设一个客户对应一个地址,在数据库中表customer和表address定义的结构SQL语句如下:
01 |
--客户表 | |
02 |
create table customer ( |
03 |
id int(20) not null auto_increment, | |
04 |
name varchar(100), |
05 |
address_id int(20), | |
06 |
primary key (id) |
07 |
) | |
08 |
--地址表 |
09 |
create table address ( | |
10 |
id int(20) not null auto_increament, |
11 |
province varchar(50), | |
12 |
city varchar(50), |
13 |
postcode varcahr(50), | |
14 |
detail varchar(50), |
15 |
primary key (id) | |
16 |
) |
此时将表customer映射为CustomerEO,代码如下:
01 |
@Entity | |
02 |
@Table(name="customer") |
03 |
public class CustomerEO implements java.io.Serializable { | |
04 |
@Id |
05 |
@GeneratedValue(strategy = GenerationType.AUTO) | |
06 |
private Integer id; |
07 |
| |
08 |
@OneToOne(cascade = { CascadeType.ALL }) |
09 |
@JoinColumn(name = "address_id") | |
10 |
private AddressEO address; |
11 |
| |
12 |
。。。 |
13 |
} |
将表address映射为AddressEO实体,代码如下:
01 |
@Entity | |
02 |
@Table(name="address") |
03 |
public class AddressEO implements java.io.Serializable { | |
04 |
@Id |
05 |
@GeneratedValue(strategy = GenerationType.AUTO) | |
06 |
private Integer id; |
07 |
| |
08 |
private String province; |
09 |
private String city; | |
10 |
... |
11 |
//省略getter、setter方法 | |
12 |
} |
现在来看看@OneToOne的定义:
1 |
@Target({METHOD, FIELD}) @Retention(RUNTIME) | |
2 |
public @interface OneToOne { |
3 |
Class targetEntity() default void.class; | |
4 |
CascadeType[] cascade() default (); |
5 |
FetchType fetch() default EAGER; | |
6 |
boolean optional() default true; |
7 |
String mappedBy() default ""; | |
8 |
} |
使用@OneToOne标记是,需要注意:
a、targetEntity
属性表示默认关联的实体类型,默认为当前标注的实体类。
b、cascade属性表示与此实体一对一关联的实体的级联样式类型。
c、fetch属性是该实体的加载方式,默认为即时加载EAGER
d、optional属性表示关联的该实体是否能够存在null值,默认为ture,如果设置为false,则该实体不能为null,并且要配合使用@JoinColumn标记,将实体关系关系设置为唯一的,不为null而且不能更新,代码如下:
1 |
@OneToOne(optional=false) | |
2 |
@JoinColumn(name="address_id", unique=true, nullable=false, updatable=false) |
3 |
private AddressEo address; |
e、mappedBy属性用于酸性关联实体时,标注在不保存关系的实体中。后讲
下面讲下@JoinColumn标记:
01 |
@Target({METHOD, FIELD}) @Retention(RUNTIME) | |
02 |
public @interface JoinColumn { |
03 |
String name() default ""; | |
04 |
String referencedColumnName() default ""; |
05 |
boolean unique() default false; | |
06 |
boolean nullable() default true; |
07 |
boolean insertable() default true; | |
08 |
boolean updatable default true; |
09 |
String columnDefinition() default ""; | |
10 |
String table() default ""; |
11 |
} |
使用@JoinColumn标记是,需要注意一下几点:
a、@JoinColumn与@Column标记一样,是用来注释表中的字段的,它的属性和@Column有很多相似之处,不在详述。
b、但是它们的区别在于:@JoinColumn注释是保存表与表之间关系的字段
c、如果不设置name,默认为name = 关联表的名称+"-"+关联表主键的字段名,在上面实例中,默认为“address_id”
e、默认情况下,关联的实体的主键一般是用来做外键的,但如果此时不想用主键作为外键,则需要设置referencedColumnName属性,例如:
1 |
create table address ( | |
2 |
id int(20) not null auto_increament, |
3 |
ref_id int(20) not null, | |
4 |
province varchar(50), |
5 |
city varchar(50), | |
6 |
postcode varcahr(50), |
7 |
detail varchar(50), | |
8 |
primary key (id) |
9 |
) |
实体类中修改为:
1 |
@OneToOne | |
2 |
@JoinColumn(name="address_id", referencedColumnName="ref_id") |
3 |
private AddressEO address; |
2、双向关联
前面只能通过实体CustomerEO对象来获得AddressEO对象,是单向关联,若在AddressEO中加入CustomerEO的引用,即为双向关联
双向关联后AddressEO实体的代码为:
01 |
@Entity | |
02 |
@Table(name="address") |
03 |
public class AddressEO implements java.io.Serializable { | |
04 |
... |
05 |
@OneToOne(mappedBy="address") |
06 |
private CustomerEO customer; |
07 |
... | |
08 |
|
09 |
| |
10 |
} |
其中mappedBy属性用来指明所映射的实体关系,它的值为所关联实体中该属性的名称,如在CustomerEO实体中address为它的属性存在。
3、主键关联(@PrimaryKeyJoinColumn)
在上面使用的字段关联,现在谈下主键关联,在两个表的结构中customer表中,去掉address_id字段,其他不变。但是实体类配置修改为:
01 |
@Entity | |
02 |
@Table(name="customer") |
03 |
public class CustomerEO implements java.io.Serializable { | |
04 |
... |
05 |
@OneToOne(cascade = { CascadeType.ALL }) | |
06 |
@PrimaryKeyJoinColumn |
07 |
private AddressEO address; | |
08 |
|
09 |
... | |
10 |
} |
11 |
| |
12 |
@Entity |
13 |
@Table(name="address") | |
14 |
public class AddressEO implements java.io.Serializable { |
15 |
... | |
16 |
@OneToOne |
17 |
@PrimaryKeyJoinColumn | |
18 |
private CustomerEO customer; |
19 |
| |
20 |
... |
21 |
| |
22 |
} |
@PrimarKeyJoinColumn注释定义如下:
1 |
@Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME) | |
2 |
public @interface PrimaryKeyJoinColumn { |
3 |
String name() default ""; | |
4 |
String referencedColumnName() default ""; |
5 |
String columnDefinition() default ""; | |
6 |
} |
4、默认关联
在建立customer和address后,加入外键约束如:
1 |
alter table customer add constraint fk_custommer_address | |
2 |
foreign key (address_id) references address (id); |
则在实体类中只需要加@OneToOne标记即可
注:使用外键利用的是数据库底层的机制,有利于数据的完整性。但是外键减少了数据的灵活性。
5、最后,说下一对一映射的一般步骤:
(1)第一步,确定实体与实体的关系,如果是一对一,则要使用@OneToOne注释
(2)第二步,考虑表结构的设计。
a、若使用外键关联,则考虑默认关联。
b、若使用主键关联,则需要配合@PrimaryKeyColumn注释
c、若使用字段关联,则需要配合@JoinColumn注释使用
(3)第三部,考虑实体关系的方向性。
今天现在到这里,码字累了,明天继续写关于@OneToMany、@ManyToOne和@ManyToMany的内容。