zoukankan      html  css  js  c++  java
  • 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的集合的引用)
     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 }
  • 相关阅读:
    堆栈
    一元多项式的乘法和加法
    单链表每隔k反转
    python命令行参数
    markdown学习笔记
    ansible学习笔记
    Linux进程管理和性能相关工具
    二分查找,折半查找,特殊情况二分查找
    插入排序,冒泡排序,归并排序, 稳定排序
    不稳定排序,选择排序,快速排序,快速排序优化, 内部排序
  • 原文地址:https://www.cnblogs.com/lj95801/p/5008519.html
Copyright © 2011-2022 走看看