码迷,mamicode.com
首页 > 其他好文 > 详细

8、双向一对多的关联关系(等同于双向多对一。1的一方有对n的一方的集合的引用,同时n的一方有对1的一方的引用)

时间:2015-12-01 01:37:27      阅读:137      评论:0      收藏:0      [点我收藏+]

标签:

 

双向一对多关联关系

”双向一对多关联关系“等同于”双向多对一关联关系“: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的集合的引用)
 1 @Table(name="t_double_one2many_customer")
 2 @Entity
 3 public class Customer2 {
 4 
 5     private Integer id;
 6     private String lastName;
 7 
 8     private String email;
 9     private int age;
10     
11     private Date birthday;
12     
13     private Date createdTime;
14     
15     // 有对Order2的集合的引用
16     //(这个引用还要被注解表示为一种映射关系才行)
17     private Set<Order2> orders = new HashSet<Order2>();
18 
19     // 省略getter、setter方法
20 }
List_2. Order实体类(有对Customer的实体的对象的引用)
 1 @Table(name="t_double_one2many_order")
 2 @Entity
 3 public class Order2 {
 4     
 5     private Integer id;
 6     private String orderName;
 7     
 8     //n的一方有对1的一方的对象的引用
 9     //①要有映射注解表明为映射;②、允许多对一,否则就是一对一
10     private Customer2 customer;
11 
12         // 省略getter、setter方法
13 }

 

从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的一方的配置
1 @ManyToOne
2 @JoinColumn(name="CUSTOMER_ID")
3 public Customer2 getCustomer() {
4     return customer;
5 }

  ②、1的一端做如下配置

  list_4. 1的一方的配置
1 @OneToMany
2 @JoinColumn(name="CUSTOMER_ID")
3 public Set<Order2> getOrders() {
4     return orders;
5 }

  要注意的是,两边的@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 /**
 2  * 1、双向1-n关联的时候,一般1的一方放弃维护关联关系,而由n的一方维护关联关系。
 3  *    这样做的好处就是:先保存1的一端,再保存n的一端的时候不会有多余的update sql语句
 4  * 2、使用@OneToMany(mappedBy="customer")来指明由n的一方的哪个属性来维护关联关系
 5  * 3、有一个值得注意的地方如果指明了mappedBy="customer",那么就不能够再使用@JoinColumn注解了10  * 
11  */
12 //    @JoinColumn(name="CUSTOMER_ID")
13 @OneToMany(mappedBy="customer")
14 public Set<Order2> getOrders() {
15     return orders;
16 }

 

说明就创建的两张数据表而言,“双边维护关联关系”与“单边维护关联关系”所创建的数据表没有区别,都是由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的一方的集合的引用,不维护关联关系)
  1 package com.magicode.jpa.doubl.many2one;
  2 
  3 import java.util.Date;
  4 import java.util.HashSet;
  5 import java.util.Set;
  6 
  7 import javax.persistence.Column;
  8 import javax.persistence.Entity;
  9 import javax.persistence.GeneratedValue;
 10 import javax.persistence.GenerationType;
 11 import javax.persistence.Id;
 12 import javax.persistence.OneToMany;
 13 import javax.persistence.Table;
 14 import javax.persistence.TableGenerator;
 15 import javax.persistence.Temporal;
 16 import javax.persistence.TemporalType;
 17 import javax.persistence.Transient;
 18 
 19 /**
 20  * @Entity 用于注明该类是一个实体类
 21  * @Table(name="t_customer") 表明该实体类映射到数据库的 t_customer 表
 22  */
 23 @Table(name="t_double_one2many_customer")
 24 @Entity
 25 public class Customer2 {
 26 
 27     private Integer id;
 28     private String lastName;
 29 
 30     private String email;
 31     private int age;
 32     
 33     private Date birthday;
 34     
 35     private Date createdTime;
 36     
 37     private Set<Order2> orders = new HashSet<Order2>();
 38 
 39     @TableGenerator(name="ID_GENERATOR_2",
 40             table="t_id_generator",
 41             pkColumnName="PK_NAME",
 42             pkColumnValue="seedId_t_customer2",
 43             valueColumnName="PK_VALUE",
 44             allocationSize=20,
 45             initialValue=10
 46             )
 47     @GeneratedValue(strategy=GenerationType.TABLE, generator="ID_GENERATOR_2")
 48     @Id
 49     @Column(name="ID")
 50     public Integer getId() {
 51         return id;
 52     }
 53     
 54     /**
 55      * 1、双向1-n关联的时候,一般1的一方放弃维护关联关系,而由n的一方维护关联关系。
 56      *       这样做的好处就是:先保存1的一端,再保存n的一端的时候不会有多余的update sql语句
 57      * 2、使用@OneToMany(mappedBy="customer")来指明由n的一方的哪个属性来维护关联关系
 58      * 3、有一个值得注意的地方:如果指明了mappedBy="customer",那么久不能够再使用@JoinColumn注解了。
 59      */
 60     //@JoinColumn(name="CUSTOMER_ID")
 61     @OneToMany(mappedBy="customer")
 62     public Set<Order2> getOrders() {
 63         return orders;
 64     }
 65 
 66     public void setOrders(Set<Order2> orders) {
 67         this.orders = orders;
 68     }
 69     
 70     @Column(name="LAST_NAME", length=50, nullable=false)
 71     public String getLastName() {
 72         return lastName;
 73     }
 74     
 75     @Column(name="BIRTHDAY")
 76     @Temporal(TemporalType.DATE)
 77     public Date getBirthday() {
 78         return birthday;
 79     }
 80 
 81     @Column(name="CREATED_TIME", columnDefinition="DATE")
 82     public Date getCreatedTime() {
 83         return createdTime;
 84     }
 85     
 86     @Column(name="EMAIL",columnDefinition="TEXT")
 87     public String getEmail() {
 88         return email;
 89     }
 90     
 91     /*
 92      * 工具方法,不需要映射为数据表的一列
 93      */
 94     @Transient
 95     public String getInfo(){
 96         return "lastName: " + lastName + " email: " + email;
 97     }
 98 
 99     @Column(name="AGE")
100     public int getAge() {
101         return age;
102     }
103 
104     @SuppressWarnings("unused")
105     private void setId(Integer id) {
106         this.id = id;
107     }
108 
109     public void setLastName(String lastName) {
110         this.lastName = lastName;
111     }
112 
113     public void setEmail(String email) {
114         this.email = email;
115     }
116 
117     public void setAge(int age) {
118         this.age = age;
119     }
120 
121     public void setBirthday(Date birthday) {
122         this.birthday = birthday;
123     }
124 
125     public void setCreatedTime(Date createdTime) {
126         this.createdTime = createdTime;
127     }
128     
129 }
List_7. Order2.java(作为n的一方,有对1的一方的对象的引用,维护关联关系)
 1 package com.magicode.jpa.doubl.many2one;
 2 
 3 import javax.persistence.Column;
 4 import javax.persistence.Entity;
 5 import javax.persistence.GeneratedValue;
 6 import javax.persistence.GenerationType;
 7 import javax.persistence.Id;
 8 import javax.persistence.JoinColumn;
 9 import javax.persistence.ManyToOne;
10 import javax.persistence.Table;
11 import javax.persistence.TableGenerator;
12 
13 @Table(name="t_double_one2many_order")
14 @Entity
15 public class Order2 {
16     
17     private Integer id;
18     private String orderName;
19     
20     private Customer2 customer;
21     
22     @TableGenerator(name="order_id_generator_2",
23             table="t_id_generator",
24             pkColumnName="PK_NAME",
25             pkColumnValue="seedId_t_order2",
26             valueColumnName="PK_VALUE",
27             initialValue=0,
28             allocationSize=20)
29     @GeneratedValue(generator="order_id_generator_2", strategy=GenerationType.TABLE)
30     @Id
31     @Column(name="ID")
32     public Integer getId() {
33         return id;
34     }
35 
36     /**
37      * 这里的name="CUSTOMER_ID"会作为Order对象存放的数据库表的一个外键列
38      * 用于维护关联关系
39      */
40     @ManyToOne
41     @JoinColumn(name="CUSTOMER_ID")
42     public Customer2 getCustomer() {
43         return customer;
44     }
45 
46     public void setCustomer(Customer2 customer) {
47         this.customer = customer;
48     }
49 
50     @Column(name="ORDER_NAME")
51     public String getOrderName() {
52         return orderName;
53     }
54     
55     @SuppressWarnings("unused")
56     private void setId(Integer id) {
57         this.id = id;
58     }
59     
60     public void setOrderName(String orderName) {
61         this.orderName = orderName;
62     }
63 
64 }
List_8. 测试代码
  1 package com.magicode.jpa.doubl.many2one;
  2 
  3 import java.util.Date;
  4 
  5 import javax.persistence.EntityManager;
  6 import javax.persistence.EntityManagerFactory;
  7 import javax.persistence.EntityTransaction;
  8 import javax.persistence.Persistence;
  9 
 10 import org.junit.After;
 11 import org.junit.Before;
 12 import org.junit.Test;
 13 
 14 
 15 
 16 public class DoubleMany2OneTest {
 17     
 18     EntityManagerFactory emf = null;
 19     EntityManager em = null;
 20     EntityTransaction transaction = null;
 21     
 22     @Before
 23     public void before(){
 24         emf = Persistence.createEntityManagerFactory("jpa-1");
 25         em = emf.createEntityManager();
 26         transaction = em.getTransaction();
 27         transaction.begin();
 28     }
 29     
 30     @After
 31     public void after(){
 32         transaction.commit();
 33         em.close();
 34         emf.close();
 35     }
 36     
 37     @Test
 38     public void testPersist(){
 39         
 40         int i = 1;
 41         
 42         char c = (char) (‘A‘ + i);
 43         String strName = (" " + c + c).trim();
 44         int age = 25 + i;
 45         
 46         Customer2 customer = new Customer2();
 47         customer.setAge(age);
 48         customer.setEmail(strName + "@163.com");
 49         customer.setLastName(strName);
 50         customer.setBirthday(new Date());
 51         customer.setCreatedTime(new Date());
 52         
 53         Order2 order1 = new Order2();
 54         order1.setOrderName("O-" + strName + "-1");
 55         
 56         Order2 order2 = new Order2();
 57         order2.setOrderName("O-" + strName + "-2");
 58         
 59         //设置关联关系
 60         customer.getOrders().add(order1);
 61         customer.getOrders().add(order2);
 62         
 63         order1.setCustomer(customer);
 64         order2.setCustomer(customer);
 65         
 66         //持久化操作
 67         /**
 68          * 双向1-n的关联关系中,1的一方放弃维护关联关系,由n的一方维护关联关系。
 69          * 建议“先保存1的一端,再保存n的一端”,这样就不会有多余的update sql语句
 70          */
 71         em.persist(customer);
 72         em.persist(order1);
 73         em.persist(order2);
 74     }
 75     
 76     @Test
 77     public void testFind(){
 78         /**
 79          * 双向多对一在查询时默认的策略如下:
 80          * 1、检索1的一方的时候,其包含的对n的集合属性的检索默认采用延迟加载,
 81          *     可以设置@OneToMany(fetch=FetchType.EAGER)来修改为立即加载策略;
 82          * 
 83          * 2、检索n的一方的时候,对其包含的1的一方默认采用立即加载策略,
 84          *     可以设置@ManyToOne(fetch=FetchType.LAZY)来修改为延迟加载策略;
 85          * 
 86          * 很容易记混淆。但是我们可以深入思考一下,这样做是有道理的:
 87          * ①、1的一方包含了对n的一方的集合属性,在检索的时候集合中到底有多少个元素我们根本
 88          * 就不知道,可能是几个,也可能是1000000个呢!!!如果默认采用立即检索策略,可以想
 89          * 象后果有多严重。
 90          * ②、n的一方包含了1的一方的一个对象,这和一个Integer或者是String类型的对象
 91          * 没有区别。也不会像集合那样可能占用巨大的内存资源。
 92          * 
 93          */
 94         Customer2 customer = em.find(Customer2.class, 11);
 95         
 96         System.out.println("---------");
 97         System.out.println(customer.getOrders().iterator().next().getOrderName());
 98         
 99         System.out.println("---------");
100         Order2 order = em.find(Order2.class, 1);
101         System.out.println("---------");
102         System.out.println(order.getCustomer().getEmail());
103     }
104     
105     @Test
106     public void testRemove(){
107         /**
108          * 双向n-1关联关系,在默认的情况下,如果1的一方集合中还保存有n的一方的引用,那么是无法删除1的一方的;
109          * 但是可以任意删除n的一方。
110          * 可以设置@OneToMany(cascade={CascadeType.REMOVE})来进行级联删除:删除1的同时,把其
111          * 关联的n的一方同时删除;
112          */
113 //        Customer2 customer = em.find(Customer2.class, 11);
114 //        em.remove(customer);
115         
116         Order2 order = em.find(Order2.class, 1);
117         em.remove(order);
118     }
119 }

 

8、双向一对多的关联关系(等同于双向多对一。1的一方有对n的一方的集合的引用,同时n的一方有对1的一方的引用)

标签:

原文地址:http://www.cnblogs.com/lj95801/p/5008519.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!