8、双向一对多的关联关系(等同于双向多对一。1的一方有对n的一方的集合的引用,同时n的一方有对1的一方的引用)
双向一对多关联关系
“双向一对多关联关系”等同于“双向多对一关联关系”:1的一方有对n的一方的集合的引用,同时n的一方有对1的一方的引用。
还是用客户Customer和订单Order来解释:
“一对多”的物理意义:一个客户可以有多个订单,某个订单只能归宿于一个客户。
“双向”的物理意义:客户知道自己有哪些订单,订单也知道自己归宿于哪个客户。也就是说,通过客户对象可以检索到其拥有哪些订单;同时,通过订单也可以查找到其对应的客户信息。这是符合我们业务逻辑需求。
到现在为止(结合前面两节的阐述)我们可以很深刻的理解“双向”、“单向”、“一对多”、“多对一”这四个词语了:
①、“一对多”讲的是一个实体类中是否包含有对另外一个实体类的集合的引用。
②、“多对一”包含两层含义:a、一个实体类Ea是否包含有对另外一个实体类Eb的引用;b、是否允许实体类Ea的多个对象{ea1, ea2, ea3,...}同时对实体类Eb的某个对象eb有引用关系(如果不允许“多个对一个”,那么就是后面要讲的“一对一关联关系”),如下Figure_1所示:
Figure_1. 允许多对一关联
③、“双向”包含两个缺一不可的层面:a、1的一方有对n的一方的集合的引用;b、同时,n的一方也有对1的一方的对象的引用;也就是同时满足①、②两点。
④、“单向”就是③中阐述的两个层面只出现一个。也就是只满足①、②两点中的一点。
注:我们上面①~④所讲的集合是实体类中的集合,同时集合之上还要有映射注解(或映射配置文件)进行相关的关联映射。如果单单只是一个集合,却没有表示映射关系的注解或配置文件,那么这个集合就不是我们映射层面上的“集合”。实体类中的对象也是一样的道理。
下面我们通过Customer和Order的关系来印证我们上面的这种理解:
List_1. Customer实体类(有对Order的集合的引用)
@Table(name=“t_double_one2many_customer”)
@Entity
public class Customer2 { private Integer id;
private String lastName; private String email;
private int age; private Date birthday; private Date createdTime; // 有对Order2的集合的引用
//(这个引用还要被注解表示为一种映射关系才行)
private Set<Order2> orders = new HashSet<Order2>();
// 省略getter、setter方法
}
List_2. Order实体类(有对Customer的实体的对象的引用)
@Table(name=“t_double_one2many_order”)
@Entity
public class Order2 { private Integer id;
private String orderName; //n的一方有对1的一方的对象的引用
//①要有映射注解表明为映射;②、允许多对一,否则就是一对一
private Customer2 customer;
// 省略getter、setter方法
}
从Customer实体类和Order实体类的属性定义我们可以得出下面的东西:
1、满足上面③的要求,所以是“双向”。
2、同时还满足①、②两点要求(这里也可以推导出“双向”)
从以上可以看出,Customer和Order是“双向一对多”或“双向多对一”关联关系。
双向关联关系的默认策略和两个单项是一致的:
1、对1的一端的集合引用的检索采用采用延迟加载方式;对n的一端的对象引用的检索采用立即加载方式;可以通过设置@ManyToOne或@OneToMany的fetch属性来修改默认策略。
2、可以自由的删除n的一方的某个对象;但是,对1的一方的对象而言,如果还有n的一方的某个对象引用它,那么就不能够删除1的一方的该对象。可以通过设置@ManyToOne或@OneToMany的cascade属性来修改默认的删除策略;
配置双向多对一关联关系的具体操作:
1、如果双边都维护关联关系:
①n的一端做如下配置
List_3. n的一方的配置
@ManyToOne
@JoinColumn(name=“CUSTOMER_ID”)
public Customer2 getCustomer() {
return customer;
}
②、1的一端做如下配置
list_4. 1的一方的配置
@OneToMany
@JoinColumn(name=“CUSTOMER_ID”)
public Set<Order2> getOrders() {
return orders;
}
要注意的是,两边的@JoinColumn的name属性要一致(这里都是CUSTOMER_ID)。最后建立得到的数据表效果就是在n的一方对应的数据表中有一列外键,而1的一方没有外键列(可以想象,1的一方无法放置外键列)
这种双边都维护关联关系的时候,保存对象不可避免的要发送多余的update语句,和单向一对多关联关系一样。无论你是先保存1的一端,还是先保存n的一端都无法避免update语句的发送。
2、只靠n的一方维护关联关系(推荐使用):
在双向多对一关联关系中,1的一方没有必要维护关联关系,只靠n的一方维护就够了。这样做的好处就是:保存对象的时候先保存1的一端,后保存n的一端,可以避免发送update语句(和单向多对一关联关系一样)。
具体的配置方法就是:a、n的一方配置如 “List_3. n的一方的配置” 一样; b、1的一方配置不能使用@JoinColumn(name=“CUSTOMER_ID”)注解(如果有此注解,则会报错),同时在@OneToMany中配置mappedBy=“customer”属性指定由n的一方的哪个属性来维护关联关系(注意,这里的customer是n一端的对象引用的名称),如下List_5:
List_5. 1的一方不维护关联关系
/**
* 1、双向1-n关联的时候,一般1的一方放弃维护关联关系,而由n的一方维护关联关系。
* 这样做的好处就是:先保存1的一端,再保存n的一端的时候不会有多余的update sql语句
* 2、使用@OneToMany(mappedBy=“customer”)来指明由n的一方的哪个属性来维护关联关系
* 3、有一个值得注意的地方:如果指明了mappedBy=“customer”,那么就不能够再使用@JoinColumn注解了。 *
*/
// @JoinColumn(name=“CUSTOMER_ID”)
@OneToMany(mappedBy=“customer”)
public Set<Order2> getOrders() {
return orders;
}
说明:就创建的两张数据表而言,“双边维护关联关系”与“单边维护关联关系”所创建的数据表没有区别,都是由n的一方对应的数据表有一个外键参照列,而1的一方没有任何外键列。两张数据表如下:
Figure_2. Customer实体对应的数据表
Figure_3. Order实体对应的数据表
注意这里的“CUSTOMER_ID”是@JoinColumn(name=“CUSTOMER_ID”)中指定的CUSTOMER_ID。注意这里的Order实体表中有外键列,而Customer实体表中没有外键列。
下面是Customer和Order实体类:
List_6. Customer2.java(作为1的一方,有对n的一方的集合的引用,不维护关联关系)
package com.magicode.jpa.doubl.many2one; import java.util.Date;
import java.util.HashSet;
import java.util.Set; import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.TableGenerator;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient; /**
* @Entity 用于注明该类是一个实体类
* @Table(name=“t_customer”) 表明该实体类映射到数据库的 t_customer 表
*/
@Table(name=“t_double_one2many_customer”)
@Entity
public class Customer2 { private Integer id;
private String lastName; private String email;
private int age; private Date birthday; private Date createdTime; private Set<Order2> orders = new HashSet<Order2>();
@TableGenerator(name=“ID_GENERATOR_2”,
table=“t_id_generator”,
pkColumnName=“PK_NAME”,
pkColumnValue=“seedId_t_customer2”,
valueColumnName=“PK_VALUE”,
allocationSize=20,
initialValue=10
)
@GeneratedValue(strategy=GenerationType.TABLE, generator=“ID_GENERATOR_2”)
@Id
@Column(name=“ID”)
public Integer getId() {
return id;
} /**
* 1、双向1-n关联的时候,一般1的一方放弃维护关联关系,而由n的一方维护关联关系。
* 这样做的好处就是:先保存1的一端,再保存n的一端的时候不会有多余的update sql语句
* 2、使用@OneToMany(mappedBy=“customer”)来指明由n的一方的哪个属性来维护关联关系
* 3、有一个值得注意的地方:如果指明了mappedBy=“customer”,那么久不能够再使用@JoinColumn注解了。
*/
//@JoinColumn(name=“CUSTOMER_ID”)
@OneToMany(mappedBy=“customer”)
public Set<Order2> getOrders() {
return orders;
} public void setOrders(Set<Order2> orders) {
this.orders = orders;
} @Column(name=“LAST_NAME”, length=50, nullable=false)
public String getLastName() {
return lastName;
} @Column(name=“BIRTHDAY”)
@Temporal(TemporalType.DATE)
public Date getBirthday() {
return birthday;
} @Column(name=“CREATED_TIME”, columnDefinition=“DATE”)
public Date getCreatedTime() {
return createdTime;
} @Column(name=“EMAIL”,columnDefinition=“TEXT”)
public String getEmail() {
return email;
} /*
* 工具方法,不需要映射为数据表的一列
*/
@Transient
public String getInfo(){
return “lastName: ” + lastName + “ email: ” + email;
} @Column(name=“AGE”)
public int getAge() {
return age;
} @SuppressWarnings(“unused”)
private void setId(Integer id) {
this.id = id;
} public void setLastName(String lastName) {
this.lastName = lastName;
} public void setEmail(String email) {
this.email = email;
} public void setAge(int age) {
this.age = age;
} public void setBirthday(Date birthday) {
this.birthday = birthday;
} public void setCreatedTime(Date createdTime) {
this.createdTime = createdTime;
} }
List_7. Order2.java(作为n的一方,有对1的一方的对象的引用,维护关联关系)
package com.magicode.jpa.doubl.many2one; import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.TableGenerator; @Table(name=“t_double_one2many_order”)
@Entity
public class Order2 { private Integer id;
private String orderName; private Customer2 customer;
@TableGenerator(name=“order_id_generator_2”,
table=“t_id_generator”,
pkColumnName=“PK_NAME”,
pkColumnValue=“seedId_t_order2”,
valueColumnName=“PK_VALUE”,
initialValue=0,
allocationSize=20)
@GeneratedValue(generator=“order_id_generator_2”, strategy=GenerationType.TABLE)
@Id
@Column(name=“ID”)
public Integer getId() {
return id;
} /**
* 这里的name=“CUSTOMER_ID”会作为Order对象存放的数据库表的一个外键列
* 用于维护关联关系
*/
@ManyToOne
@JoinColumn(name=“CUSTOMER_ID”)
public Customer2 getCustomer() {
return customer;
} public void setCustomer(Customer2 customer) {
this.customer = customer;
} @Column(name=“ORDER_NAME”)
public String getOrderName() {
return orderName;
} @SuppressWarnings(“unused”)
private void setId(Integer id) {
this.id = id;
} public void setOrderName(String orderName) {
this.orderName = orderName;
} }
List_8. 测试代码
package com.magicode.jpa.doubl.many2one; import java.util.Date; import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence; import org.junit.After;
import org.junit.Before;
import org.junit.Test; public class DoubleMany2OneTest { EntityManagerFactory emf = null;
EntityManager em = null;
EntityTransaction transaction = null; @Before
public void before(){
emf = Persistence.createEntityManagerFactory(“jpa-1”);
em = emf.createEntityManager();
transaction = em.getTransaction();
transaction.begin();
} @After
public void after(){
transaction.commit();
em.close();
emf.close();
} @Test
public void testPersist(){ int i = 1; char c = (char) ('A' + i);
String strName = (“ ” + c + c).trim();
int age = 25 + i; Customer2 customer = new Customer2();
customer.setAge(age);
customer.setEmail(strName + “@163.com”);
customer.setLastName(strName);
customer.setBirthday(new Date());
customer.setCreatedTime(new Date()); Order2 order1 = new Order2();
order1.setOrderName(“O-” + strName + “-1”); Order2 order2 = new Order2();
order2.setOrderName(“O-” + strName + “-2”); //设置关联关系
customer.getOrders().add(order1);
customer.getOrders().add(order2); order1.setCustomer(customer);
order2.setCustomer(customer); //持久化操作
/**
* 双向1-n的关联关系中,1的一方放弃维护关联关系,由n的一方维护关联关系。
* 建议“先保存1的一端,再保存n的一端”,这样就不会有多余的update sql语句
*/
em.persist(customer);
em.persist(order1);
em.persist(order2);
} @Test
public void testFind(){
/**
* 双向多对一在查询时默认的策略如下:
* 1、检索1的一方的时候,其包含的对n的集合属性的检索默认采用延迟加载,
* 可以设置@OneToMany(fetch=FetchType.EAGER)来修改为立即加载策略;
*
* 2、检索n的一方的时候,对其包含的1的一方默认采用立即加载策略,
* 可以设置@ManyToOne(fetch=FetchType.LAZY)来修改为延迟加载策略;
*
* 很容易记混淆。但是我们可以深入思考一下,这样做是有道理的:
* ①、1的一方包含了对n的一方的集合属性,在检索的时候集合中到底有多少个元素我们根本
* 就不知道,可能是几个,也可能是1000000个呢!!!如果默认采用立即检索策略,可以想
* 象后果有多严重。
* ②、n的一方包含了1的一方的一个对象,这和一个Integer或者是String类型的对象
* 没有区别。也不会像集合那样可能占用巨大的内存资源。
*
*/
Customer2 customer = em.find(Customer2.class, 11); System.out.println(“---------”);
System.out.println(customer.getOrders().iterator().next().getOrderName()); System.out.println(“---------”);
Order2 order = em.find(Order2.class, 1);
System.out.println(“---------”);
System.out.println(order.getCustomer().getEmail());
} @Test
public void testRemove(){
/**
* 双向n-1关联关系,在默认的情况下,如果1的一方集合中还保存有n的一方的引用,那么是无法删除1的一方的;
* 但是可以任意删除n的一方。
* 可以设置@OneToMany(cascade={CascadeType.REMOVE})来进行级联删除:删除1的同时,把其
* 关联的n的一方同时删除;
*/
// Customer2 customer = em.find(Customer2.class, 11);
// em.remove(customer); Order2 order = em.find(Order2.class, 1);
em.remove(order);
}
}
8、双向一对多的关联关系(等同于双向多对一。1的一方有对n的一方的集合的引用,同时n的一方有对1的一方的引用)的更多相关文章
- JPA中实现双向一对多的关联关系
场景 JPA入门简介与搭建HelloWorld(附代码下载): https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/103473937 ...
- Hibernate5.2关联关系之双向一对多(三)
Hibernate之双向一对多(三) 一.简介 本篇博文接着上一章的内容接着开展,代码也是 ...
- JPA学习笔记(8)——映射双向一对多关联关系
双向一对多关联关系 前面的博客讲的都是单向的,而本问讲的是双向的(双向一对多 = 双向多对一) 什么是双向? 我们来对照一下单向和双向 单向/双向 User实体类中是否有List< Order& ...
- Mybatis框架中实现双向一对多关系映射
学习过Hibernate框架的伙伴们很容易就能简单的配置各种映射关系(Hibernate框架的映射关系在我的blogs中也有详细的讲解),但是在Mybatis框架中我们又如何去实现 一对多的关系映射呢 ...
- JPA(七):映射关联关系------映射双向多对一的关联关系
映射双向多对一的关联关系 修改Customer.java package com.dx.jpa.singlemanytoone; import java.util.Date; import java. ...
- Hibernate 双向一对多的关联映射
双向的一对多的关联关系是单项的一对多和单项的多对一的情况下产生的. 1.设计表结构 虽然关联关系变为双向的一对多,但是我们表结构不会发生改变,只是指向变了. 2.创建student对象 3.创建Gra ...
- Hibernate关联映射(单项多对一和一对多、双向一对多)
最近总是接触着新的知识点来扩展自己的知识面:不停的让自己在原地接触天空的感觉真的很美好!!!革命没有成功,程序员的我们怎么能不努力呢...... 一.用员工和部门来剖析关联映射的原理. 1)从这张截图 ...
- hibernate 自生双向一对多 多对一管理 (树)
<span style="font-size: large;">package com.javacrazyer.test; import java.io.Seriali ...
- hibernate中配置单向多对一关联,和双向一对多,双向多对多
什么是一对多,多对一? 一对多,比如你去找一个父亲的所有孩子,孩子可能有两个,三个甚至四个孩子. 这就是一对多 父亲是1 孩子是多 多对一,比如你到了两个孩子,它们都是有一个共同的父亲. 此时孩子就是 ...
随机推荐
- 使用RX方式模拟DoubanFm的登陆
WP7下的Get Post都是异步的 关于RX http://www.cnblogs.com/yangecnu/archive/2012/11/03/Introducting_ReactiveExte ...
- 使用本地光盘安装Microsoft .NET Framework 3.5 for Win8.1/WinServer2012R2
.NET Framework 3.5 作为的SQL Server 2012的先决条件,假如使用图形化方式需要使用internet,对于服务器部署时缓慢的一点(需要下载后安装) 以下提供一个使用使用安装 ...
- erp与电子商务集成的结构图
集约化采购管理系统和电子商务平台统一规划.统一设计,通过系统之间的安全接口全面集成,进而实现资源共享和数据共享,企业内外部系统运作的一体化,建立企业同上.下游合作伙伴的电子数据交互,从而提高电子商务的 ...
- C#模拟键盘鼠标事件 SendKeys 的特殊键代码表(转)
使用 SendKeys 将键击和组合键击发送到活动应用程序.此类无法实例化.若要发送一个键击给某个类并立即继续程序流,请使用 Send.若要等待键击启动的任何进程,请使用 SendWait. 每个键都 ...
- Project Settings -> Editor 设置详解
Default Behavior Mode (默认行为模式) 定义项目在导入Assets时的默认导入设置,当设置为3D模式时,Unity假设将导入的文件创建为纹理类型(如:PNG文件):当设置为2D时 ...
- 初涉Node.js
Node.js的是建立在Chrome的JavaScript的运行时,可方便地构建快速,可扩展的网络应用程序的平台. Node.js使用事件驱动.非阻塞I/ O模型,是轻量级.高效.完美的跨分布式设备运 ...
- 【转】 java自定义注解
java注解是附加在代码中的一些元信息,用于一些工具在编译.运行时进行解析和使用,起到说明.配置的功能. 注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用.包含在 java.lang.annot ...
- 1891: 丘比特的烦恼 - BZOJ
Description 随着社会的不断发展,人与人之间的感情越来越功利化.最近,爱神丘比特发现,爱情也已不再是完全纯洁的了.这使得丘比特很是苦恼,他越来越难找到合适的男女,并向他们射去丘比特之箭.于是 ...
- protocol buffer 整数序列化
http://blog.csdn.net/csfreebird/article/details/7624807 varints用于正整数 (无符号整数) varints 是 一个很不错的技术.将一个整 ...
- EXT经验--查询EditorGridPanel的tbar的默认配置对象
前言:EXT的API可谓熟悉EXT的葵花宝典,会看API可谓对于配置EXT,学习EXT最重要的基本功,这点相对于学习轻量级的Easyui来说更加明显. 比如下面的一段代码:注:在Ext.grid.Ed ...