PO 即Persistence Object 
VO 即Value Object

PO 和VO 是Hibernate 中两个比较关键的概念。 
首先,何谓VO,很简单,VO 就是一个简单的值对象。 
如: 
TUser user = new TUser(); 
user.setName("Emma");

这里的user 就是一个VO。VO 只是简单携带了对象的一些属性信息。 
何谓PO? 即纳入Hibernate 管理框架中的VO。看下面两个例子:

TUser user = new TUser(); 
TUser anotherUser = new TUser(); 
user.setName("Emma"); 
anotherUser.setName("Kevin");

//此时user和anotherUser都是VO

Transaction tx = session.beginTransaction(); 
session.save(user);

//此时的user已经经过Hibernate的处理,成为一个PO ,而anotherUser仍然是个VO

tx.commit();

//事务提交之后,库表中已经插入一条用户”Emma”的记录,对于anotherUser则无任何操作

Transaction tx = session.beginTransaction(); 
user.setName("Emma_1"); //PO 
anotherUser.setName("Kevin_1");//VO 
tx.commit();

//事务提交之后,PO的状态被固化到数据库中,也就是说数据库中“Emma”的用户记录已经被更新为“Emma_1”,此时anotherUser仍然是个普通Java对象,它的属性更改不会对数据库产生任何影响,另外,通过Hibernate返回的对象也是PO: 由Hibernate返回的PO ,如:

TUser user = (TUser)session.load(TUser.class,new Integer(1));

VO经过Hibernate进行处理,就变成了PO。上面的示例代码session.save(user)中,我们把一个VO “user”传递给Hibernate的Session.save方法进行保存。在save方法中,Hibernate对其进 
行如下处理: 
1. 在当前session所对应的实体容器(Entity Map)中查询是否存在user对象的引用。 
2. 如果引用存在,则直接返回user对象id,save过程结束. Hibernate中,针对每个Session有一个实体容器(实际上是一个Map对象), 如果此容器中已经保存了目标对象的引用,那么hibernate 会认为此对象已经与Session相关联。 
对于save操作而言,如果对象已经与Session相关联(即已经被加入Session 的实体容器中),则无需进行具体的操作。因为之后的Session.flush 过程中,Hibernate会对此实体容器中的对象进行遍历,查找出发生变化的实体,生成并执行相应的update语句。 
3. 如果引用不存在,则根据映射关系,执行insert操作。 
a) 在我们这里的示例中,采用了native的id生成机制,因此hibernate会从数据库取得insert操作生成的id并赋予user对象的id属性。 
b) 将user对象的引用纳入Hibernate的实体容器。 
c) save 过程结束,返回对象id. 
而Session.load方法中,再返回对象之前,Hibernate 就已经将此对象纳入其实体容器中。

VO和PO的主要区别在于:

. VO是独立的Java Object。 
. PO是由Hibernate纳入其实体容器(EntityMap)的对象,它代表了与数 
据库中某条记录对应的Hibernate实体,PO的变化在事务提交时将反应到实 
际数据库中。如果一个PO与Session对应的实体容器中分离(如Session 关闭后的PO),那么 
此时,它又会变成一个VO。

关于unsaved-value

在非显示数据保存时,Hibernate 将根据这个值来判断对象是否需要保存。 
所谓显式保存,是指代码中明确调用session 的save 、update 、saveOrupdate 方法对对象进行持久化。如:

session.save(user);

而在某些情况下,如映射关系中,Hibernate 根据级联(Cascade )关系对联接类进行保存。此时代码中没有针对级联对象的显示保存语句,需要Hibernate 根据对象当前状 
态判断是否需要保存到数据库。此时,Hibernate 即将根据unsaved-value 进行判定。 
首先Hibernate 会取出目标对象的id。之后,将此值与unsaved-value 进行比对,如果相等,则认为目标对象尚未保存,否则,认为对象已经保存,无需再进行保存操作。 
如:user 对象是之前由hibernate 从数据库中获取,同时,此user 对象的若干个关联对象address 也被加载,此时我们向user 对象新增一个address 对象,此时调用 
session.save(user),hibernate 会根据unsaved-value 判断user 对象的数个address 
关联对象中,哪些需要执行save 操作,而哪些不需要。

对于我们新加入的address 对象而言,由于其id(Integer 型)尚未赋值,因此为null,与我们设定的unsaved-value(null )相同,因此hibernate 将其视为一个未保存对象,将为其生成insert 语句并执行。

这里可能会产生一个疑问,如果“原有”关联对象发生变动(如user 的某个“原有” 的address 对象的属性发生了变化,所谓“原有”即此address 对象已经与user 相关联,而不是我们在此过程中为之新增的),此时id 值是从数据库中读出,并没有发生改变,自然 
与unsaved-value(null)也不一样,那么Hibernate 是不是就不保存了?

上面关于PO、VO 的讨论中曾经涉及到数据保存的问题,实际上,这里的“保存”, 实际上是“insert”的概念,只是针对新关联对象的加入,而非数据库中原有关联对象的 
“update”。所谓新关联对象,一般情况下可以理解为未与Session 发生关联的VO。而“原有”关联对象,则是PO。:

对于save操作而言,如果对象已经与Session相关联(即已经被加入Session的实体容器中),则无需进行具体的操作。因为之后的Session.flush 过程中,Hibernate 
会对此实体容器中的对象进行遍历,查找出发生变化的实体,生成并执行相应的update 语句。

Inverse和Cascade

Inverse,直译为“反转”。在Hibernate语义中,Inverse 指定了关联关系中的方向。关联关系中,inverse=”false”的为主动方,由主动方负责维护关联关系。具体可参见一对多关系中的描述。

而Cascade,译为“级联”,表明对象的级联关系,如TUser 的Cascade设为all, 就表明如果发生对user对象的操作,需要对user所关联的对象也进行同样的操作。如对user对象执行save操作,则必须对user对象相关联的address也执行save操作。

初学者常常混淆inverse和cascade,实际上,这是两个互不相关的概念。Inverse 指的是关联关系的控制方向,而cascade指的是层级之间的连锁操作。

延迟加载(Lazy Loading)

为了避免一些情况下,关联关系所带来的无谓的性能开销。Hibernate引入了延迟加载的概念。如,示例中user对象在加载的时候,会同时读取其所关联的多个地址(address)对象, 
对于需要对address进行操作的应用逻辑而言,关联数据的自动加载机制的确非常有效。但是,如果我们只是想要获得user的性别(sex)属性,而不关心user的地址(address) 
信息,那么自动加载address的特性就显得多余,并且造成了极大的性能浪费。为了获得user 的性别属性,我们可能还要同时从数据库中读取数条无用的地址数据,这导致了大量无谓的系统开销。

延迟加载特性的出现,正是为了解决这个问题。所谓延迟加载,就是在需要数据的时候,才真正执行数据加载操作。

对于我们这里的user对象的加载过程,也就意味着,加载user对象时只针对其本身的属性, 而当我们需要获取user对象所关联的address信息时(如执行user.getAddresses时),才 
真正从数据库中加载address数据并返回。 
尝试执行以下代码:

Criteria criteria = session.createCriteria(TUser.class);

criteria.add(Expression.eq("name","Erica")); 
List userList = criteria.list(); 
TUser user =(TUser)userList.get(0);

System.out.println("User name => "+user.getName()); 
Set hset = user.getAddresses();

session.close();//关闭Session

TAddress addr = (TAddress)hset.toArray()[0]; 
System.out.println(addr.getAddress());

运行时抛出异常: 
LazyInitializationException - Failed to lazily initialize a collection - no session or session was closed

如果我们稍做调整,将session.close放在代码末尾,则不会发生这样的问题。这意味着,只有我们实际加载user 关联的address时,Hibernate 才试图通过 
session从数据库中加载实际的数据集,而由于我们读取address之前已经关闭了session,所以报出session已关闭的错误。

这里有个问题,如果我们采用了延迟加载机制,但希望在一些情况下,实现非延迟加载时的功能,也就是说,我们希望在Session关闭后,依然允许操作user的addresses 
属性。如,为了向View层提供数据,我们必须提供一个完整的User对象,包含其所关联的address信息,而这个User对象必须在Session关闭之后仍然可以使用。

Hibernate.initialize方法可以通过强制加载关联对象实现这一功能:

Hibernate.initialize(user.getAddresses()); 
session.close(); 
//通过Hibernate.initialize方法强制读取数据 
//addresses对象即可脱离session进行操作

Set hset= user.getAddresses(); 
TAddress addr = (TAddress)hset.toArray()[0]; 
System.out.println(addr.getAddress());

为了实现透明化的延迟加载机制,hibernate进行了大量努力。其中包括JDK Collection接口的独立实现。 
如果我们尝试用HashSet强行转化Hibernate返回的Set 型对象:

Set hset = (HashSet)user.getAddresses();

就会在运行期得到一个java.lang.ClassCastException, 实际上,此时返回的是一个Hibernate的特定Set实现“net.sf.hibernate.collection.Set”对象,而非 
传统意义上的JDK Set实现。这也正是我们为什么在编写POJO时,必须用JDKCollection 接口(如Set,Map), 而非特定的JDKCollection 实现类(如HashSet、HashMap)申明Collection属性的原因。

关于延迟加载(lazy)和强制加载(Hibernate.initialize(Object proxy) )的更多相关文章

  1. [转]PO和VO、关于延迟加载(lazy)和强制加载(Hibernate.initialize(Object proxy) )

    摘自http://www.cnblogs.com/kelin1314/archive/2009/11/13/1602778.html PO和VO PO 即Persistence Object VO 即 ...

  2. JPA数据懒加载LAZY和实时加载EAGER(二)

    懒加载LAZY和实时加载EAGER的概念,在各种开发语言中都有广泛应用.其目的是实现关联数据的选择性加载,懒加载是在属性被引用时,才生成查询语句,抽取相关联数据.而实时加载则是执行完主查询后,不管是否 ...

  3. JPA数据懒加载LAZY和实时加载EAGER(转)

    原文:https://www.cnblogs.com/MrSi/p/8081811.html 懒加载LAZY和实时加载EAGER的概念,在各种开发语言中都有广泛应用.其目的是实现关联数据的选择性加载, ...

  4. mybatis 启用延迟加载和按需加载配置

    启用延迟加载和按需加载 Mybatis配置文件中通过两个属性lazyLoadingEnabled和aggressiveLazyLoading来控制延迟加载和按需加载. lazyLoadingEnabl ...

  5. 使用jquery插件实现图片延迟加载技术(懒加载)

    有时我们看到一些大型网站,页面如果有很多图片的时候,当你滚动到相应的行时,当前行的图片才即时加载的,这样子的话页面在打开只加可视区域的图片,而其它隐藏的图片则不加载,一定程序上加快了页面加载的速度,对 ...

  6. 文件映射spring 使用classpath方式加载hibernate映射文件

    在改章节中,我们主要介绍文件映射的内容,自我感觉有个不错的建议和大家分享下 <!-- 批量指定到classpath下面 --> <property name="mappin ...

  7. MyBatis 延迟加载的三种加载方式深入,你get了吗?

    延迟加载 延迟加载对主对象都是直接加载,只有对关联对象是延迟加载. 延迟加载可以减轻数据库的压力, 延迟加载不可是一条SQL查询多表信息,这样构不成延迟加载,会形成直接加载. 延迟加载分为三种类型: ...

  8. Spring加载Hibernate 映射的几种方式及区别

    LocalSessionFactoryBean有好几个属性用来查找hibernate映射文件: mappingResources.mappingLocations.mappingDirectoryLo ...

  9. lazy图片懒加载使用

    看到一个小伙子写的言简意赅很不错,摘录如下: https://www.npmjs.com/package/vue-lazyload 首先我们先在npm上下载vue-lazyload的包 1 npm i ...

随机推荐

  1. python编程学习day03

    1.文件操作 (1)打开文件 f = open ("文件名称",mode='' ",encoding="utf-8") mode=操作方式 encod ...

  2. H5新增的postMessage跨域解决方案Demo

    Demo背景:html中使用iframe嵌入了跨域的vue项目,在html中将参数传入到跨越的vue项目中. 向跨越的子窗口中发送数据 function sendMessage(data) { // ...

  3. 【转】Git 修改已提交的commit注释

    https://www.jianshu.com/p/098d85a58bf1 [重点] 通过git rebase -i HEAD~2 你想修改哪条注释 就把哪条注释前面的pick换成edit git ...

  4. js Date.parse() format.

    date format android chrome linux chrome Mobile safari ios chrome windows safari linux firefox window ...

  5. hdu多校第七场 1011 (hdu6656) Kejin Player 概率dp

    题意: 一个游戏,有许多关,到下一关要花费金钱,做出尝试,有概率成功,若成功则到达下一关,若失败则停在此关或退回到前面某关,询问第l关到第r关的期望费用 题解: 显然,第r关到第l关的费用是dp[r] ...

  6. HDU-6070 Dirt Ratio(二分+线段树+分数规划)

    目录 目录 思路: (有任何问题欢迎留言或私聊 && 欢迎交流讨论哦 目录 题意:传送门  原题目描述在最下面.  求\(sum/len\)最小值.\(sum\)是一段区间内不同数字的 ...

  7. SpringBoot通过maven打包成jar,设定主清单属性。

    文章目录 原来普通的jar包一直是 <build> <plugins> <plugin> <groupId>org.apache.maven.plugi ...

  8. 剑指offer——06二叉树的下一个节点

    题目描述 给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回.注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针.   题目的意思是,在一颗二叉树的中序遍历中,给出其中一 ...

  9. BAT大神推荐:看懂英文文档,每天只需要10分钟做这件事……

    程序员这个行业是很特殊的.之所以说特殊,就是因为它所有的技术大多来自欧美,所以最主流,最新鲜,最正确的技术文章都是英文,遗憾的是,大部分还没有译本. 有些译文还比较差.与其等待别人的翻译,不如直接阅读 ...

  10. WinCE下的第二个窗口程序

    MFC写的,有些简陋,但是还是感觉不错,一个小小的计算器,各个方面的功能都完成了 但是唯独那个CEdit里面的文字不能右对齐.那个扩展风格用不了