@OneToMany、@ManyToOne以及@ManyToMany讲解
一、一对多(@OneToMany)
1、单向一对多模型
假设通过一个客户实体可以获得多个地址信息。
对于一对多的实体关系而言,表结构有两种设计策略,分别是外键关联和表关联。
(1) 映射策略---外键关联
在数据库中表customer和表结构address定义,如下:
create table customer (
id int(20)notnull auto_increment,
name varchar(100),
primary key(id)
)
create table address (
id int(20)notnull auto_increment,
province varchar(50),
city varchar(50),
postcode varchar(50),
detail varchar(50),
customer_id int(20),
primary key (id)
)
1
|
|
注意此时外键定义在多的一方,也就是address表中。
此时,表customer映射为实体CustomerEO,代码如下:
@Entity
@Table(name="customer")
public class CustomerEO implements java.io.Serializable {
@OneToMany(cascade={ CascadeType.ALL })
@JoinColumn(name="customer_id")
private Collection
...
}
1
|
|
注释@OneToMany的定义代码如下:
1
|
|
使用时要注意一下几点问题:
a、targetEntity属性表示默认关联的实体类型。如果集合类中指定了具体类型了,不需要使用targetEntity.否则要指定targetEntity=AddressEO.class。
b、mappedBy属性用于标记当实体之间是双向时使用。
(2) 映射策略---表关联
在上面address表中去掉customer_id字段,在增加一个表ref_customer_address,如下: --客户地址关系表
create table ref_customer_address (
customer_id int(20)notnull,
address_id int(20)notnull unique
)
1
|
|
此时表customer映射为CustomerEO实体,代码如下:
@Entity
@Table(name = "customer")
public class CustomerEO implements java.io.Serializable {
...
@OneToMany(cascade = {CascadeType.ALL })
@JoinTable(name="ref_customer_address",
joinColumns={ @JoinColumn(name="customer_id",referencedColumnName="id")},
inverseJoinColumns={@JoinColumn(name="address_id",referencedColumnName="id")})
private Collection
...
}
1
|
|
表关联@JoinTable,定义如下:
@Target({METHOD,FIELD})
public @interface JoinTable {
String name() default "";
String catalog() default "";
String schema() default "";
JoinColumn[]joinColumns()default {};
JoinColumn[]inverseJoinColumns()default {};
UniqueConstraint[]uniqueConstraintsdefault {};
}
1
|
|
其中:
a、该标记和@Table相似,用于标注用于关联的表。
b、name属性为连接两张表的表名。默认的表名为:“表名1”+“-”+“表名2”,上面例子默认的表名为customer_address。
c、joinColumns属性表示,在保存关系中的表中,所保存关联的外键字段。
d、inverseJoinColumns属性与joinColumns属性类似,不过它保存的是保存关系的另一个外键字段。
(3) 默认关联
在数据库底层为两张表添加约束,如下:
create table customer_address (
customer_id int(20)notnull,
address_id int(20)notnull unique
)
alter table customer_address addconstraint fk_ref_customer foreignkey (customer_id)references customer (id);
alter table customer_address addconstraint fk_ref_address foreignkey (address_id)references address (id);
1
|
|
这样,在CustomerEO中只需要在标注@OneToMany即可!
二、多对一@ManyToOne
1、单向多对一模型。
(1) 外键关联
配置AddressEO实体如下:
@Entity
@Table(name="address")
public class AddressEO implements java.io.Serializable {
@ManyToOne(cascade = { CascadeType.ALL })
@JoinColumn(name="customer_id")
private CustomerEO customer;
// ...
}
1
|
|
@ManyToOne定义如下:
1
|
|
(2) 默认关联
数据库脚本定义的相关字段的约束,创建外键后,直接使用@ManyToOne
三、高级一对多和多对一映射
即双向关联模型,确定了双向关联后,多的一方AddressEO不变使用@ManyToOne,而CustomerEO实体修改为:
@Entity
@Table(name="customer")
public class CustomerEO {
@OneToMany(mappedBy="customer")
private Collection
}
1
|
|
其中,@OneToMany标记中的mappedBy属性的值为AddressEO实体中所引用的CustomerEO实体的属性名。
四、多对多(@ManyToMany)
和一对多类型,不在赘述。@ManyToMany标记的定义如下:
1
|
现在再来讲解一下这几个注解中的属性含义:
一,
CascadeType
|
CascadeType.PERSIST 级联新增(又称级联保存):
获取A对象里也同时也重新获取最新的B时的对象。即会重新查询数据库里的最新数据,并且,只有A类新增时,会级联B对象新增。若B对象在数据库存(跟新)在则抛异常(让B变为持久态),对应EntityManager的presist方法,调用JPA规范中的persist(),不适用于Hibernate的save()方法
CascadeType.MERGE 级联合并(又称级联更新)
指A类新增或者变化,会级联B对象(新增或者变化) ,对应EntityManager的merge方法,调用JPA规范中merge()时,不适用于Hibernate的update()方法 CascadeType.REMOVE 级联删除
只有A类删除时,会级联删除B类,即在设置的那一端进行删除时,另一端才会级联删除,对应EntityManager的remove方法,调用JPA规范中的remove()时,适用于Hibernate的delete()方法 CascadeType.REFRESH 级联刷新
获取order(一或多)对象里也同时也重新获取最新的items(多)的对象,对应EntityManager的refresh(object),调用JPA规范中的refresh()时,适用于Hibernate的flush()方法 CascadeType.ALL
包含所有持久化方法
综上:大多数情况用CascadeType.MERGE就能达到级联跟新又不报错,用CascadeType.ALL时要斟酌下CascadeType.REMOVE
二.FetchType2、FetchType.EAGER:急加载,加载一个实体时,定义急加载的属性会立即从数据库中加载。
3、比方User类有两个属性,name跟address,就像 百度知道 ,登录后用户名是需要显示出来的,此属性用到的几率极大,要马上到数据库查,用急加载;而用户地址大多数情况下不需要显示出来,只有在查看用户资料是才需要显示,需要用了才查数据库,用懒加载就好了。所以,并不是一登录就把用户的所有资料都加载到对象中,于是有了这两种加载模式。