摘自http://www.cnblogs.com/kelin1314/archive/2009/11/13/1602778.html

  • PO和VO

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

显式保存,是指代码中明确调用session 的save 、update 、saveOrupdate 方法对对象进行持久化。如:
session.save(user);
在非显式保存时,Hibernate 将根据unsaved-value的值来判断对象是否需要保存。
(而在某些情况下,如映射关系中,Hibernate 根据级联(Cascade )关系对联接类进行保存。此时代码中没有针对级联对象的显示保存语句,需要Hibernate 根据对象当前状态判断是否需要保存到数据库。此时,Hibernate 即将根据unsaved-value 进行判定。 )

过程解释:(以下例子前提是配置了级联Cascade)
首先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)

[详细情况可见相关sxt Hibernate笔记]

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

为了获得user 的性别属性,我们可能还要同时从数据库中读取数条无用的地址数据,这导致了大量无谓的系统开销。

延迟加载特性的出现,正是为了解决这个问题。所谓延迟加载,就是在需要数据的时候,才真正执行数据加载操作。
对于我们这里的user对象的加载过程,也就意味着,初次只加载user对象本身的普通属性, 而当我们需要获取user对象所关联的address对象信息时(如执行user.getAddresses时),才真正从数据库中加载address数据并返回。

尝试执行以下代码:
Criteria criteria = session.createCriteria(TUser.class); //QBC用法,无视即可
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属性的原因。

[转]PO和VO、关于延迟加载(lazy)和强制加载(Hibernate.initialize(Object proxy) )的更多相关文章

  1. 关于延迟加载(lazy)和强制加载(Hibernate.initialize(Object proxy) )

    PO 即Persistence Object VO 即Value Object PO 和VO 是Hibernate 中两个比较关键的概念. 首先,何谓VO,很简单,VO 就是一个简单的值对象. 如:  ...

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

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

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

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

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

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

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

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

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

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

  7. lazy图片懒加载使用

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

  8. js延迟加载,提升网页加载速度

    JS延迟加载,简单例子,不多说: 代码如下: 程序代码 <script language="JavaScript" src="" id="my& ...

  9. jquery循环延迟加载,用于在图片加载完成后再加载js

    <html> <head> <script type="text/javascript" src="http://lib.sinaapp.c ...

随机推荐

  1. 设备、像素和点 、 9切片技术 、 颜色和外观 、 NavigationBar的美化

    1 TMessage项目的输入面板界面 1.1 问题 IOS中经常会使用到九切片技术对图片进行处理.本案例使用九切片技术完成Tmessage项目的输入板界面,如图-1所示: 图-1 1.2 方案 首先 ...

  2. TruSeq 应该指的是试剂盒名字 NEB

    现在中心用的是NEB试剂盒,建库步骤更简单一些.TruSeq和NEB差不多,既可以建DNA又可以建RNA. TruSeq Technology     TruSeq technology repres ...

  3. (实用篇)php无限遍历目录

    使用的函数有: isset()判断某个变量是否定义 chdir() 将当前目录改变为指定的目录. opendir() 打开目录. readdir()读取目录. getcwd().获取当前目录. 还用到 ...

  4. [转载] C++ string, const char*, char* 之间互相转换

    1, string转const char* 类型 string str = "abcdef"; const char* con_str = string.c_str() 2, co ...

  5. Java-->多线程断点续传

    --> 在多线程复制的基础上加入断点续传的功能 -->Test 测试类 package com.dragon.java.multithreaddownload; import java.i ...

  6. 图像处理之image stitching

    背景介绍 图像拼接是一项应用广泛的图像处理技术.根据特征点的相互匹配,可以将多张小视角的图像拼接成为一张大视角的图像,在广角照片合成.卫星照片处理.医学图像处理等领域都有应用.早期的图像拼接主要是运用 ...

  7. MongoDB安装、配置和基本使用

    一.搭建MongoDB的yum源 如果安装epel扩展源,可以安装MongoDB2.4版本 如要安装3.0版本,需要自己配置yum源,官方提供的源只支持64位系统 # vim /etc/yum.rep ...

  8. 改造dede 后台会员目录

    可以在后台目录(默认 dede),里面的inc->inc_menu.php 文件里进行添加或更改,实现功能!

  9. HDU-5785 Interesting(Manacher算法+区间处理)

    题目大意:给一个字符串,求所有相邻两回文子串的外侧下标之积的和 题目分析:另L[i]为所有以 i 为右端点的回文字串的左端点之和,同理,另R[i]表示所有以 i 为左端点的回文子串的右端点之和.显然, ...

  10. flash全屏输入模式

    params.allowscriptaccess = "sameDomain"; params.allowfullscreen = "true"; params ...