单向多对一的关联关系

具体体现:n的一方有1的引用,1的一方没有n的集合属性

举个例子:订单Order对顾客Customer是一个单向多对一的关联关系。Order是n的一方,有对Customer的引用;而Customer作为1的一方却没有Order的集合属性。

“多对一”的物理意义就是:一个客户可以有多个订单,而一个订单只能归属于一个客户

“单向”的物理意义就是:订单知道它属于哪个客户,而客户却不知道自己有哪些订单。(多么反人类的逻辑!!!)

 

下面看看客户和订单的实体类的属性,很清楚“单向多对一”这种关联关系:

Order实体的属性(Order中有对Customer的引用):

 1 @Table(name="t_order")
 2 @Entity
 3 public class Order {
 4     
 5     private Integer id;
 6     private String orderName;
 7     
 8     // n 的一方有对 1 的一方的引用
 9     private Customer customer;
10     
11     // 省略getter、setter方法...    
12 }

 

Customer实体的属性(Customer中没有对Order的集合的引用):

 1 @Table(name="t_customer")
 2 @Entity
 3 public class Customer {
 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     //省略getter、setter方法  
16 }

 

映射方法:主要是对n的一方使用@ManyToOne和@JoinColumn注解。而1的一方不需要做任何映射性的标注。具体的映射方法

1、采用@ManyToOne注解映射多对一的关联关系。检索n的时候,对其包含的1的一方的引用在默认情况下采用“左外连接”的方式来进行加载。可以通过配置@ManyToOne(fetch=FetchType.LAZY)的来设定为延迟加载(延迟加载要注意懒加载异常的出现)。

2、通过@JoinColumn注解来映射外键,其name属性用来指定外键列的列名。外键列在n的一方对应的数据表中。

 

对应于Customer和Order而言:在检索Order的时候,对Order.customer采用左外连接的方式进行检索(立即加载)。为什么要这样呢??

在实体类中,属性可以分为两种:1、集合属性; 2、非集合属性;

一个大体的原则就是:1、对集合属性默认采用懒加载策略;2、对非集合属性默认采用立即检索策略;

这种默认检索策略是有道理的:1、检索的时候我们并不知道集合中到底包含了多少条记录,可能是几条,也可能是几十亿条记录。如果对一个庞大的集合属性采用立即检索策略,那么很有可能直接将内存全部占用了(比如说,集合中包含了100亿条记录),系统直接崩溃。2、对一个非集合属性而言,即便是一个其它实体类的引用(该引用中的集合依然会采用延迟检索)所占资源也是十分有限,不会像检索集合那样直接脱离我们的掌控。所以,对于非集合属性默认采用立即检索策略。

 

在持久化多对一的时候有一个可以优化的地方值得注意一下:先保存1的一方,后保存n的一方,按这个顺序进行保存就不需要发送update语句

 

下面列出清单:

List_1. Customer作为1的一方,没有Order的集合属性
  1 package com.magicode.jpa.helloworld;
  2 
  3 import java.util.Date;
  4 
  5 import javax.persistence.Column;
  6 import javax.persistence.Entity;
  7 import javax.persistence.GeneratedValue;
  8 import javax.persistence.GenerationType;
  9 import javax.persistence.Id;
 10 import javax.persistence.Table;
 11 import javax.persistence.TableGenerator;
 12 //import javax.persistence.TableGenerator;
 13 import javax.persistence.Temporal;
 14 import javax.persistence.TemporalType;
 15 import javax.persistence.Transient;
 16 
 17 /**
 18  * @Entity 用于注明该类是一个实体类
 19  * @Table(name="t_customer") 表明该实体类映射到数据库的 t_customer 表
 20  */
 21 @Table(name="t_customer")
 22 @Entity
 23 public class Customer {
 24 
 25     private Integer id;
 26     private String lastName;
 27 
 28     private String email;
 29     private int age;
 30     
 31     private Date birthday;
 32     
 33     private Date createdTime;
 34 
 35     /**
 36      * @TableGenerator 标签的属性解释:
 37      * 
 38      *     ①、allocationSize 属性需要赋一个整数值。表示了bucket的容量。其默认值为50。
 39      *     ②、table 属性用于指定辅助表的表名。这里指定为t_id_generator数据表
 40      * 
 41      * 其基本思想就是:从table指定的辅助表中读取一个bucket段id号范围内的第一个数值,记为first_id。在后面持久化过程中的id号是从first_id开始依次递增1得到
 42      * 当递增到first_id + allocationSize 的时候,就会再一次从辅助表中读取一个first_id开始新一轮的id生成过程。
 43      * 
 44      * 我们知道,要从数据库中确定一个值,则必须确定其“行”和“列”。JPA自动产生的t_id_generator只有两列。当然,如果该表
 45      * 为n个表产生id,则会在t_id_generator表中保存“n行2列”。
 46      * 那么,如何从数据表t_id_generator中确定出seed_id用于为Customer实体计算id呢??JPA会依据Customer实体的
 47      * @TableGenerator 属性值来依据下面的规则的到seed_id:
 48      *     ③、valueColumnName 属性指定了seed_id的列名。valueColumnName="PK_VALUE"也就是指定了
 49      *        seed_id位于PK_VALUE列中。同时,规定了这一列必须是数值型(int,long等)。
 50      *             剩下的任务就是如何从n行中确定出是哪一行??
 51      *     ④、pkColumnName="PK_NAME",pkColumnValue="seed_t_customer_id" 两个一起来确定具体的行:
 52      *            在PK_NAME列中,值为seed_t_customer_id的那一行。
 53      *     ⑤、由上面③和④中确定出来的“行”和“列”就可以得到一个int型的整数值。这个值就是first_id。
 54      * 
 55      * 注意:我们的数据库中可以没有t_id_generator这张表,JPA会自动帮助我们完成该表的创建工作。自动创建的表只有两列:
 56      * PK_NAME(VARCHAR)和PK_VALUE(int)。同时会自动添加一条记录(seed_t_customer_id, 51) 依据优化策略的不同,辅助表中记录的数值有区别
 57      */
 58     @TableGenerator(name="ID_GENERATOR",
 59             table="t_id_generator",
 60             pkColumnName="PK_NAME",
 61             pkColumnValue="seedId_t_customer",
 62             valueColumnName="PK_VALUE",
 63             allocationSize=20,
 64             initialValue=10
 65             )
 66     @GeneratedValue(strategy=GenerationType.TABLE, generator="ID_GENERATOR")
 67     @Id
 68     @Column(name="ID")
 69     public Integer getId() {
 70         return id;
 71     }
 72 
 73     /**
 74      * @Column 指明lastName属性映射到表的 LAST_NAME 列中
 75      * 同时还可以指定其长度、能否为null等数据限定条件
 76      */
 77     @Column(name="LAST_NAME", length=50, nullable=false)
 78     public String getLastName() {
 79         return lastName;
 80     }
 81     
 82     /**
 83      * 利用 @Temporal 来限定birthday为DATE型
 84      */
 85     @Column(name="BIRTHDAY")
 86     @Temporal(TemporalType.DATE)
 87     public Date getBirthday() {
 88         return birthday;
 89     }
 90 
 91     /*
 92      * 通过 @Column 的 columnDefinition 属性将CREATED_TIME列
 93      * 映射为“DATE”类型
 94      */
 95     @Column(name="CREATED_TIME", columnDefinition="DATE")
 96     public Date getCreatedTime() {
 97         return createdTime;
 98     }
 99     
100     /*
101      * 通过 @Column 的 columnDefinition 属性将email列
102      * 映射为“TEXT”类型
103      */
104     @Column(name="EMAIL",columnDefinition="TEXT")
105     public String getEmail() {
106         return email;
107     }
108     
109     /*
110      * 工具方法,不需要映射为数据表的一列
111      */
112     @Transient
113     public String getInfo(){
114         return "lastName: " + lastName + " email: " + email;
115     }
116 
117     @Column(name="AGE")
118     public int getAge() {
119         return age;
120     }
121 
122     @SuppressWarnings("unused")
123     private void setId(Integer id) {
124         this.id = id;
125     }
126 
127     public void setLastName(String lastName) {
128         this.lastName = lastName;
129     }
130 
131     public void setEmail(String email) {
132         this.email = email;
133     }
134 
135     public void setAge(int age) {
136         this.age = age;
137     }
138 
139     public void setBirthday(Date birthday) {
140         this.birthday = birthday;
141     }
142 
143     public void setCreatedTime(Date createdTime) {
144         this.createdTime = createdTime;
145     }
146     
147 }
Customer.java

相关文章:

  • 2022-12-23
  • 2021-12-08
  • 2021-10-03
  • 2021-09-26
  • 2022-12-23
  • 2022-12-23
  • 2021-07-13
  • 2021-09-14
猜你喜欢
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-06-28
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
相关资源
相似解决方案