单向多对一的关联关系

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

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

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

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

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

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

 @Table(name="t_order")
@Entity
public class Order { private Integer id;
private String orderName; // n 的一方有对 1 的一方的引用
private Customer customer;

// 省略getter、setter方法...
}

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

 @Table(name="t_customer")
@Entity
public class Customer { private Integer id;
private String lastName; private String email;
private int age; private Date birthday; private Date createdTime; //省略getter、setter方法
}

映射方法:主要是对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的集合属性
 package com.magicode.jpa.helloworld;

 import java.util.Date;

 import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.TableGenerator;
//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_customer")
@Entity
public class Customer { private Integer id;
private String lastName; private String email;
private int age; private Date birthday; private Date createdTime; /**
* @TableGenerator 标签的属性解释:
*
* ①、allocationSize 属性需要赋一个整数值。表示了bucket的容量。其默认值为50。
* ②、table 属性用于指定辅助表的表名。这里指定为t_id_generator数据表
*
* 其基本思想就是:从table指定的辅助表中读取一个bucket段id号范围内的第一个数值,记为first_id。在后面持久化过程中的id号是从first_id开始依次递增1得到
* 当递增到first_id + allocationSize 的时候,就会再一次从辅助表中读取一个first_id开始新一轮的id生成过程。
*
* 我们知道,要从数据库中确定一个值,则必须确定其“行”和“列”。JPA自动产生的t_id_generator只有两列。当然,如果该表
* 为n个表产生id,则会在t_id_generator表中保存“n行2列”。
* 那么,如何从数据表t_id_generator中确定出seed_id用于为Customer实体计算id呢??JPA会依据Customer实体的
* @TableGenerator 属性值来依据下面的规则的到seed_id:
* ③、valueColumnName 属性指定了seed_id的列名。valueColumnName="PK_VALUE"也就是指定了
* seed_id位于PK_VALUE列中。同时,规定了这一列必须是数值型(int,long等)。
* 剩下的任务就是如何从n行中确定出是哪一行??
* ④、pkColumnName="PK_NAME",pkColumnValue="seed_t_customer_id" 两个一起来确定具体的行:
* 在PK_NAME列中,值为seed_t_customer_id的那一行。
* ⑤、由上面③和④中确定出来的“行”和“列”就可以得到一个int型的整数值。这个值就是first_id。
*
* 注意:我们的数据库中可以没有t_id_generator这张表,JPA会自动帮助我们完成该表的创建工作。自动创建的表只有两列:
* PK_NAME(VARCHAR)和PK_VALUE(int)。同时会自动添加一条记录(seed_t_customer_id, 51) 依据优化策略的不同,辅助表中记录的数值有区别
*/
@TableGenerator(name="ID_GENERATOR",
table="t_id_generator",
pkColumnName="PK_NAME",
pkColumnValue="seedId_t_customer",
valueColumnName="PK_VALUE",
allocationSize=20,
initialValue=10
)
@GeneratedValue(strategy=GenerationType.TABLE, generator="ID_GENERATOR")
@Id
@Column(name="ID")
public Integer getId() {
return id;
} /**
* @Column 指明lastName属性映射到表的 LAST_NAME 列中
* 同时还可以指定其长度、能否为null等数据限定条件
*/
@Column(name="LAST_NAME", length=50, nullable=false)
public String getLastName() {
return lastName;
} /**
* 利用 @Temporal 来限定birthday为DATE型
*/
@Column(name="BIRTHDAY")
@Temporal(TemporalType.DATE)
public Date getBirthday() {
return birthday;
} /*
* 通过 @Column 的 columnDefinition 属性将CREATED_TIME列
* 映射为“DATE”类型
*/
@Column(name="CREATED_TIME", columnDefinition="DATE")
public Date getCreatedTime() {
return createdTime;
} /*
* 通过 @Column 的 columnDefinition 属性将email列
* 映射为“TEXT”类型
*/
@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;
} }

Customer.java

List_2. Order作为n的一方,有对Customer的引用,需要配置关联关系
 package com.magicode.jpa.many2one;

 import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
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; import com.magicode.jpa.helloworld.Customer; @Table(name="t_order")
@Entity
public class Order { private Integer id;
private String orderName; private Customer customer;  // n 的一方有对 1 的一方的引用
@TableGenerator(name="order_id_generator",
table="t_id_generator",
pkColumnName="PK_NAME",
pkColumnValue="seedId_t_order",
valueColumnName="PK_VALUE",
initialValue=0,
allocationSize=20)
@GeneratedValue(generator="order_id_generator", strategy=GenerationType.TABLE)
@Id
@Column(name="ID")
public Integer getId() {
return id;
} @Column(name="ORDER_NAME")
public String getOrderName() {
return orderName;
} /**
* 1、通过 @ManyToOne 来配置单向多对一的关联关系,同时可以配置fetch=FetchType.LAZY
* 来指定懒加载查询策略;
* 2、@JoinColumn来映射外键
*/
//@ManyToOne(fetch=FetchType.LAZY)
    @ManyToOne
@JoinColumn(name="CUSTOMER_ID")
public Customer getCustomer() {
return customer;
} public void setCustomer(Customer customer) {
this.customer = customer;
} @SuppressWarnings("unused")
private void setId(Integer id) {
this.id = id;
} public void setOrderName(String orderName) {
this.orderName = orderName;
} }
List_3. persistence.xml的配置文件
 <?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> <!-- 注意这里的 persistence-unit标签的name属性值,main函数中会用到它 -->
<persistence-unit name="jpa-1" transaction-type="RESOURCE_LOCAL"> <!-- 配置使用什么样的ORM产品作为JPA的实现
1、实际上配置的是 javax.persistence.spi.PersistenceProvider 接口的实现类
2、若JPA项目中只有一个JPA的实现产品,则可以不配置provider节点
-->
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider> <!-- 添加持久化类 -->
<class>com.magicode.jpa.helloworld.Customer</class>
<class>com.magicode.jpa.many2one.Order</class> <properties>
<!-- 连接数据库的基本信息 -->
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
<property name="javax.persistence.jdbc.url" value="jdbc:mysql:///jpa" />
<property name="javax.persistence.jdbc.user" value="root" />
<property name="javax.persistence.jdbc.password" value="tiger123" /> <!-- 配置JPA实现产品的基本属性:配置Hibernate的基本属性 -->
<property name="hibernate.format_sql" value="true" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.hbm2ddl.auto" value="update" />
<!--
Setting is relevant when using @GeneratedValue.
It indicates whether or not the new IdentifierGenerator implementations
are used for javax.persistence.GenerationType.AUTO,
javax.persistence.GenerationType.TABLE and
javax.persistence.GenerationType.SEQUENCE.
Default to false to keep backward compatibility.
-->
<property name="hibernate.id.new_generator_mappings" value="true"/> </properties> </persistence-unit>
</persistence>
List_4. 测试方法分别测试了增、删、改、查
 package com.magicode.jpa.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; import com.magicode.jpa.helloworld.Customer; public class Many2OneTest { 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();
} /**
* 先持久化 1 的一方,后持久化 n 的一方会少发送3条update SQL语句
*/
@Test
public void testPersist(){ for(int i = 0; i < 3; i++){
char c = (char) ('A' + i);
String strName = (" " + c + c).trim();
int age = 25 + i; Customer customer = new Customer();
customer.setAge(age);
customer.setEmail(strName + "@163.com");
customer.setLastName(strName);
customer.setBirthday(new Date());
customer.setCreatedTime(new Date()); Order order1 = new Order();
order1.setOrderName("O-" + strName + "-1"); Order order2 = new Order();
order2.setOrderName("O-" + strName + "-2"); //设置关联关系
order1.setCustomer(customer);
order2.setCustomer(customer); //持久化操作
em.persist(customer);
em.persist(order1);
em.persist(order2);
}
} /**
* 查询单向多对一关联关系的时候,默认情况下采用的是left outer join策略。
* 可以通过 @ManyToOne(fetch=FetchType.LAZY) 来配置为懒加载查询策略
*/
@Test
public void testFind(){
Order order = em.find(Order.class, 1); System.out.println("-----1------");
System.out.println("orderName: " + order.getOrderName()); System.out.println("-----2------");
System.out.println("order.customer.email: " + order.getCustomer().getEmail());
} @Test
public void testRemove(){
//删除多的一端可以随便删除
// Order order = em.find(Order.class, 1);
// em.remove(order); //删除1的一端的时候,如果还有多的一端引用它,则会删除失败
Customer customer = em.find(Customer.class, 11);
em.remove(customer);
} @Test
public void testUpdate(){
Customer customer = em.find(Customer.class, 12);
customer.setAge(1000); Order order = em.find(Order.class, 5);
order.setOrderName("OOOO");
order.getCustomer().setAge(1000);
}
}

6、JPA_映射单向多对一的关联关系(n的一方有1的引用,1的一方没有n的集合属性)的更多相关文章

  1. JPA(五):映射关联关系------映射单向多对一的关联关系

    映射单向多对一的关联关系 新建Customer.java: package com.dx.jpa.singlemanytoone; import java.util.Date; import java ...

  2. JPA 映射单向多对一的关联关系

    1.首先在多的一端加入一的一端的实体类 //映射单向n-1的关联关 //使用@ManyToOne 来映射多对一的关系 //使用@JoinColumn 来映射外键/可以使用@ManyToOne的fetc ...

  3. 10、JPA_映射双向多对多的关联关系

    双向多对多的关联关系 双向多对多的关联关系(抽象成A-B)具体体现:A中有B的集合的引用,同时B中也有对A的集合的引用.A.B两个实体对应的数据表靠一张中间表来建立连接关系. 同时我们还知道,双向多对 ...

  4. JPA_映射双向多对多的关联关系(转)

    双向多对多的关联关系 转自(http://www.cnblogs.com/lj95801/p/5011537.html) 双向多对多的关联关系(抽象成A-B)具体体现:A中有B的集合的引用,同时B中也 ...

  5. JPA(六):映射关联关系------映射单向一对多的关联关系

    映射单向一对多的关联关系 新建项目项目请参考<JPA(二):HellWord工程>,基于上一章讲解的<JPA(五):映射关联关系------映射单向多对一的关联关系>中的例子进 ...

  6. JPA(七):映射关联关系------映射双向多对一的关联关系

    映射双向多对一的关联关系 修改Customer.java package com.dx.jpa.singlemanytoone; import java.util.Date; import java. ...

  7. JPA中实现单向多对一的关联关系

    场景 JPA入门简介与搭建HelloWorld(附代码下载): https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/103473937 ...

  8. JPA学习(四、JPA_映射关联关系)

    框架学习之JPA(四) JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中 ...

  9. JPA_映射关联关系

    一:单项多对一的关联关系 例如:订单和客户 1.新创建订单类 package com.atguigu.jpa.helloworld; import javax.persistence.Column; ...

随机推荐

  1. thinkpad t440p 解决无线网卡驱动

    $ wget https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1239578/+attachment/4057550/+files/rtl_9 ...

  2. 网络笔记01-3 socket 实现百度页面的两种方式

    scoket 实现百度页面的两种方式: 1.利用系统自带    //1.创建URL NSURL *url=[NSURL URLWithString:@"http://m.baidu.com& ...

  3. java使用.net的webservice

    1.下载最新的axis2 http://mirrors.hust.edu.cn/apache//axis/axis2/java/core/1.6.3/axis2-1.6.3-bin.zip 2.解压使 ...

  4. linux驱动之I2C

    include/linux/i2c.h struct i2c_msg;struct i2c_algorithm;struct i2c_adapter;struct i2c_client;struct ...

  5. MYSQL远程登录权限设置

    Mysql默认关闭远程登录权限,如下操作允许用户在任意地点登录: 1. 进入mysql,GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY ...

  6. [resource]Python机器学习库

    reference: http://qxde01.blog.163.com/blog/static/67335744201368101922991/ Python在科学计算领域,有两个重要的扩展模块: ...

  7. 常用git 命令

    1.取消跟踪某些文件或文件夹: 删除文件: $git rm --cached FILENAME 删除文件夹: $git rm -r --cached Path 2.忽略某些文件或文件夹 $vi .gi ...

  8. ZendStudio中设置SVN:ignore

    使用ZendStudio开发SVN中的代码时,经常容易将 .project..settings..buildpath 这类的zend的工程文件提交上去,非常麻烦,有几种方法可以去掉这个麻烦. 1.在Z ...

  9. java基础知识回顾之java Thread类--java线程实现常见的两种方式实现Runnable接口(二)

    创建线程的第二中方式: /** *      步骤: 1定义类实现Runnable接口      2.实现Runnable接口中的run方法.      3.通过Thread类建立线程对象,并将Run ...

  10. 《架构探险——从零开始写Java Web框架》这书不错,能看懂的入门书

    这书适合我. 哈哈,结合 以前的知识点,勉强能看懂. 讲得细,还可以参照着弄出来. 希望能坚持 完成啦... 原来,JSTL就类似于DJANGO中的模板. 而servlet类中的res,req,玩了D ...