文档章节

一对一映射的三种方式以及对lazyload的特别关注

猪刚烈
 猪刚烈
发布于 2014/10/12 11:41
字数 1754
阅读 22
收藏 0

      一对一映射几乎涉及了所有可以使用的映射方式:共享主键、外键和关联表。每一种方式都有相对特定的使用场合。而与此同时,one-to-one关系的 lazy loading总是一个让人非常疑惑的问题,下文在介绍每一种一对一映射方案的时候也会对lazy loding的问题做一个说明。


 一.共享主键方式


     这是一对一方案所特有的一种映射方式,这种方式特别适合非空的并且具有一致生命周期(同生同灭)的一对一关系。以User,Address为例,我为 Address的主键再添加一个外键,来参照User,这样就是声明让Address做“受约束”端,也就是constrained="true"的那一端。关于constrained="true"我们会在后面作出解释。下面是表关系:

User端的映射较为简单:

      @OneToOne
      @PrimaryKeyJoinColumn
      private Address shippingAddress;

Address端相对复杂,因为我们需要在配置中指明它的主键生成策略,这个策略是要参照User主键的。

@Entity @Table(name = "ADDRESS") 
public class Address { 
	@Id @GeneratedValue(generator = "myForeignGenerator") 
	@org.hibernate.annotations.GenericGenerator( 
		name = "myForeignGenerator", 
		strategy = "foreign", 
		parameters = @Parameter(name = "property", value = "user") ) 
	@Column(name = "ADDRESS_ID") 
	private Long id; 
	...
	private User user; 
}

       下面来讨论一个使用共享主键方式的lazy loding问题。在上面的例子中,其实很容易知道Address的user字段是可以lazy loding的,而User和shippingAddress是不能的!为什么呢?因为外键在Address表上,当load一条Address记录时,它能从外键上确定有没有关联对象,从而能确定是生成代理对象还是一个null,反观 User,由于它没有外键参照到Address,这样,在加载User时,无法通过User本身的纪录来判定它有没有一个对应的Address,进而无法确定是生成一个代理还是一个null值,因此,hibernate必须hit数据库(往往是在加载时直接使用left join一起把address也查出来).


      在基于xml配制的方式下,one-to-one有一个配制项:constrained="true"。JPWH一书566页曾提及:在共享主键方式的映射方式中,只有在one-to-one中标记了constrained="true"那个关联对象才是可以被lazy loading的。下面是Address的xml配置:

<class name="Address" table="ADDRESS"> 
	<id name="id" column="ADDRESS_ID"> 
	<generator class="foreign"> <param name="property">user</param> </generator> </id>
	...
	<one-to-one name="user" class="User" constrained="true"/> 
</class>


官方文件对constrained的解释是:


  在解释这一配置项之前必须声明两个概念:关联方和被关联方。关联方是当前正在配置的类,被关联方是指当前被配置的字段所属的类(在Address的 user字段配置上,关联方就是Address,被关联方就是user)。那么,constrained=“true”的解释就是:它指明在关联方表的主键上会同时有一个外键,这个外键指向被关联方表的主键。简单说,它指明外键是放在关联方上的。这样,对于关联方来说,当它加载时,它就可以通过外键列来确知被关联方存不存在,进而确保了在关联方加载时,被关联方能否被lazy loading。


  实际上在注解方式下是没有也不需要这一选项的,映射了使用外键策略生成主键的对象(也就是主键同时做外键的表)应该自动具有了lazy lode另一端关联对象的能力。


二,外键映射方式


  这种方式是通过对外键列加unique约束来模拟一对一关联,这种映射方法较为简单,是被推荐的做一对一映射的最自然方式。还是以User,Address为例:在User端的注解配置是:
public class User {
    ...
    @OneToOne
    @JoinColumn(name="SHIPPING_ADDRESS_ID",unique=true)//unique=true确保了一对一关系
    private Address shippingAddress;
    ...
}

 

  需要特别说明的是:从外键的角度来看,虽然从User到Address是一个加了unique约束的“多对一”的关系,但是在使用注解时,JPA规范还是要求使用@OneToOne。在hibernate的xml配置方式中,这里就不会写<one-to-one>,而是写成了:其实这种写法更能揭示配置的本质。
Address端的注解配置是:
public class Address {
    ...
    @OneToOne(mappedBy = "shippingAddress")
    private User user;
    ...
}
  OK,那我们回过头来看lazy loading的问题。这与共享主键方式非常相似,外键所在的那一方,也就是many方,也就是有@JoinColumn的那一方,它可以lazy loading,而另一方则不能!(已经代码验证!)


三.关联表映射方式


  我们可以看到,由于关联表方式,关联双方都没有指向对方的外键,因些,在这种方式下,双方都不可能是lazy loading的。
下面是Desk的user字段映射配置。从实际代码执行中可以确定:fetch=FetchType.LAZY是无效的。

    @OneToOne(fetch=FetchType.LAZY)
    @JoinTable(
        name="Desk_Employee",
        joinColumns=@JoinColumn(name="deskId"),// You did't specify this column as primary key, but it's set primary key auto!
        inverseJoinColumns=@JoinColumn(name="employeeId",unique=true)//unique=true make sure one-to-one relationship.
    )



总结


最后对一对一三种映射方式做一个简单的总结:
  1.共享主键方式适合那些关联双方不能为空,且永远不会发生变化的一对一关系。对象模型来看,大部分这类情形应该双方具有同等生命周期的组成关系。
  2.外键关联加唯一约束的方式适合非空,但关连双方会发生成变化的一对一关系。这显然是聚集和一般的关联关系。
  3.关联表方式无疑最为灵活,它适合会经常发生变化并且可以为空的一对一关系。典型的场景就是桌子和员工的分配问题。一张桌子总是对应一个员工,但并不总是一个特定的员工,有时候会发生调换,有时候可能没有人使用,而员工方面也是如此。另外一个例子是JPWH一书中提到的Item和Shipment之间的一对一关系。一个物品一旦售出,就会生成一个运送记录,用于追踪商品的运送过程。这个例子和桌子员工的情形有细微不同的地方在于,桌子和员工之间的关系还存在一种可变更的情况, 而Item和Shipment之间只是一个可选的一对一关系,也就是说只有Item售出才会生成一条Shipment,并不是所有Item一定会有一个对应的Shipment!实际上这正是JPWH一书在映射Item和Shipment时选择使用关联表的最主要原因,因为共享主键方式肯定不行,而外键关联方式则需要置外键列为空(因为如上面提及,这种一对一是可选的,而不是必然的),这样,只有关联表方式最为适合。

本文转载自:http://blog.csdn.net/bluishglc/article/details/6021352

共有 人打赏支持
猪刚烈
粉丝 22
博文 708
码字总数 110
作品 1
海淀
程序员
私信 提问
scrollLoad_MingGe 2.52 更新,滚动加载插件

2.52升级内容:加入container容器操作,这个功能在2.52的时候忘记了,修复各种事件机制,以及各种优化修复等 MingGe.scrollLoad2.52插件运行速度非常快, demo.html演试采用的大量图片以瀑布...

明哥先生
2016/01/11
1K
5
18、vue-lazyload实现图片懒加载

前言:GitHub:https://github.com/Ewall1106/mall(请选择分支18) 1、安装 vue-lazyload官网:https://github.com/hilongjw/vue-lazyload 2、main.js中引入 (1)引入并注册vue-lazyload ......

Ewall_
07/24
0
0
jquery lazy load

LazyLoad是一个Js编写的Jq插件,它可以延迟加载页面中的图片,在浏览器可视范围中的图片会被加载。 如何使用:LazyLoad依赖于Jquery,在html的结尾处 ,就是在前。 你必须改变图片的标签,图片的...

张子浩
08/20
0
0
jQuery.lazyload详解

<script src="http://libs.baidu.com/jquery/1.9.1/jquery.min.js"></script> <script src="http://file.ithome.com/js/jquery.lazyload.js"></script>   jQuery实现图片延迟加载,不知道是......

地球家园
2014/03/05
0
0
ie6下png去背景

今天用 DD_belatedPNG.js做ie6下的png图片去背景,无论如何,都去不掉。无奈去官网下了最新版本的 DD_belatedPNG,还是不行。把图片的其他属性全删了,只留下一个src属性,用DD_belatedPNG.f...

qii
2013/09/26
253
9

没有更多内容

加载失败,请刷新页面

加载更多

初探Spring Cloud(一)

1. 什么是Spring Cloud? Spring提供了一系列工具,可以帮助开发人员迅速搭建分布式系统中的公共组件(比如:配置管理,服务发现,断路器,智能路由,微代理,控制总线,一次性令牌,全局锁,...

__HuWei
14分钟前
0
0
Mac配置ssh免密钥登录

Mac终端每次使用ssh -p 22 user@ip登录很是麻烦,下面介绍配置ssh免密钥登录: cd ~/.ssh下创建conf文件,写入以下配置: Host test HostName ip Port 22 IdentityFile /Users/t/key/test.p...

littlemesieV
29分钟前
1
0
Spark2.0操作ES

ES提供了支持包来方便的操作ES。首先添加ES的依赖maven: <dependency> <groupId>org.elasticsearch</groupId> <artifactId>elasticsearch-spark-20_2.11</artifactId> <version>6.2.0</ver......

守望者之父
31分钟前
1
0
专业术语

1、防御性编程 DruidDataSource类有一个init方法,我们在spring中配置druid时,都会指定 init-method='init'. 而且DruidDataSource也在其他地方,诸如getConnection()方法里作了防御性编程, 也就...

still5656
33分钟前
1
0
微信开发--测试账号相关

1、微信公众平台选择代码开发后,其平台上的部分功能会不可用 可在开发者工具中选择开通测试账号,进行相关的功能调试(测试账号出微信支付不可调试外,其他功能基本上都可以调试) 2、测试账号...

Code辉
39分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部