65
对对 / 对对对对 Hibernate 作作 作作作 作作 作作 Hibernate 作作作作作 作作作 、。 作作 作作作作 Hibernate, 作作作作作作作作作作作作作

对象 / 关系映射 Hibernate 作者:赵春晖

  • Upload
    milt

  • View
    77

  • Download
    2

Embed Size (px)

DESCRIPTION

对象 / 关系映射 Hibernate 作者:赵春晖. 要求:熟悉 Hibernate 的基本配置、操作。 目标:深入理解 Hibernate, 熟悉其存储方式和加载方式等。. 目录. POJO 与 PO 实体对象的管理 Hibernate 数据缓存 持久化的操作. POJO 和 PO 的概念. - PowerPoint PPT Presentation

Citation preview

Page 1: 对象 / 关系映射  Hibernate 作者:赵春晖

对象 / 关系映射 Hibernate 作者:赵春晖

要求:熟悉 Hibernate 的基本配置、操作。目标:深入理解 Hibernate, 熟悉其存储方式和加载方式等。

Page 2: 对象 / 关系映射  Hibernate 作者:赵春晖

目录• POJO 与 PO• 实体对象的管理• Hibernate 数据缓存• 持久化的操作

Page 3: 对象 / 关系映射  Hibernate 作者:赵春晖

POJO 和 PO 的概念• POJO : Plain Ordinary java object 从字面上理解就是无格式的普通的 JAVA 对象 , 可以简单理解为不含逻辑代码的值对象 (Value Object), 在 Hibernate 理解为数据库表所对应的 Domain Object , 从数据层面来看, POJ

O 作为数据实体的表现形式,也成为实体类 (Entity bean),POJO 和 Hibernate 映射文件构成了 Hibernate 的基础代码

• PO : persistent Object 持久化对象• 也就是说 在一些 Objet/Relation Mapping 工具中能够做到维护数据库表记录的 PO 全是符合 JAVABEAN 规范的纯 JAVA 对象。如下:

Page 4: 对象 / 关系映射  Hibernate 作者:赵春晖

POJO 和 PO 的概念public class TUser{ private Integer id;

private String name; private Integer age; private Set address; public TUser(){ } ……… seter/geter}

Page 5: 对象 / 关系映射  Hibernate 作者:赵春晖

POJO 和 PO 的区别• POJO 是由 new 创建 有 GC 回收而 PO 是由 Insert 数据库创建,由数据库 delete 删除,基本上 PO 的生命周期和数据库密切相关,另外 PO 只能存在数据库的一个

Connection 中 当 Connection 关闭 PO 则不存在 而 POJO 只要不被 GC 回收总是存在的。

• 由于诸多差别, PO 和 POJO 在代码上肯定是不同的,起码 PO 会比 POJO 多一些用于管理数据库 entity 的属性和方法。而 ORM 追求的目标就是要在 PO 的使用上尽量要和 POJO 一致,对于程序员来讲他们可以把 PO当作 POJO 来用而感觉不到 PO 的存在。• 在 Hibernate 中是这样实现的

Page 6: 对象 / 关系映射  Hibernate 作者:赵春晖

POJO 和 PO 的区别• 编写 POJO 。• 编译 POJO 。• 直接运行,在运行期 由 Hibernate 的 CGLIB 将 POJO动态的转化为 PO 。• 由此可以看出 Hibernate 是在运行期将 POJO 转化为

PO 的而 CGLIB 的效率也很高在运行期生成 PO 的字节码也很快,效率的损失几乎可以忽略不计。在运行期生成 PO 的好处是 对于程序员来说是无法接触到 PO 的可以说 PO 对他们来说是透明的,程序员可以更自由的以 POJO 的概念来操作 PO 。

Page 7: 对象 / 关系映射  Hibernate 作者:赵春晖

POJO 和 PO 的区别• 当你在程序里面 set/get 的时候, Hibernate 是从 PO 实例中取 values 的,所以即使 Session关闭也一样可以 set/get ,可以进行跨 Session的状态管理。• 所以在分多层的应用中,由于持久层和业务层和 web 层都是分开的,此时 Hibernate 的 PO完全可以当做一个 POJO 来用,也就是当做一个 Value Object ,在各层间自由传递,而不用去管 Session 是开还是关。( lazy loading 除外)。

Page 8: 对象 / 关系映射  Hibernate 作者:赵春晖

目录• POJO 与 PO• 实体对象的管理• Hibernate 数据缓存• 持久层的操作

Page 9: 对象 / 关系映射  Hibernate 作者:赵春晖

实体对象的管理 - 对象的 3 种状态• 在 Hibernate 中最核心的概念就是 对实体对象的状态管理(也就是上面提到的 POJO/Domai

n Object )。• 实体对象的状态有三种, Transient( 瞬时状态 )、 Persistent( 持久状态 ) 、 Detached ( 脱管状态 )

Page 10: 对象 / 关系映射  Hibernate 作者:赵春晖

实体对象的管理 - 对象的 3 种状态• Transient( 瞬时状态 )• 也就是未被持久化的 VO(value object) 是由 JV

M 维护其生命周期。• 如 :

methodA(){TUser user=new TUser()User.setName(”ZCH”);

}

Page 11: 对象 / 关系映射  Hibernate 作者:赵春晖

实体对象的管理 - 对象的 3 种状态• Persistent( 持久状态 )• 此时实体对象处于由 hibernate 管理的状态,并且与数据库表记录同步。• 例如:

TUser user=new TUser();TUser antherUser=new TUser();user.setName("aa1");antherUser.setName("aa2");// 此时 user 和 antherUser 都处于 transient 状态

Page 12: 对象 / 关系映射  Hibernate 作者:赵春晖

实体对象的管理 - 对象的 3 种状态Transaction tx=session.beginTransaction();session.save(user);// 此时 user 处于 Persistent 状态,由 hibernate 管理// 而 antherUser 还处于 transient 状态 tx.commit();// 事务提交之后 数据库中插入 name=“aa1“ 的记录Transaction tx2=session.beginTransaction();user.setName("aa1_1");// PersistentantherUser.setName("aa2_2");//transienttx2.commit();// 虽然没有显示调用 save(), 但此时 user 处于 persistent// 状态, hibenate 会检查持久化对象的认识改动然后在当前操作单元//结束后将对象数据与数据库同步

Page 13: 对象 / 关系映射  Hibernate 作者:赵春晖

实体对象的管理 - 对象的 3 种状态Detached( 脱管状态)也就是曾被持久化过,但现在和 Session已经 det

ached 了,以 VO 的身份在运行 。这种和 Session已经 detached( 分离 ) 的 PO 还能够进入另一个 Session ,继续进行 PO 状态管理,此时它就成为 PO 的第二种状态了。这种

PO 实际上是跨了 Session 进行了状态维护的。

Page 14: 对象 / 关系映射  Hibernate 作者:赵春晖

实体对象的管理 - 对象的 3 种状态例如:TUser user=new TUser();user.setName("aa1");// 此时 user 处于 transient 状态Transaction tx=session.beginTransaction();session.save(user);// 此时 user 处于 Persistent 状态,由 hibernate 管理tx.commit();// 事务提交之后 数据库中插入 name="aa1" 记录session.close();// 此时的 user已经处于 detached 状态 因为与其关联的

session已经关闭

Page 15: 对象 / 关系映射  Hibernate 作者:赵春晖

实体对象的管理 - 对象的 3 种状态• Transient( 自由状态 ) 和 Detached(游离状态 ) 的区别• 区别在于 Datached 对象还可以再次与某个 session 实例相关联而成为 persistent 对象• 例如:

Transaction tx=session.beginTransaction();session.save(user);// 此时 user 处于 Persistent 状态,由 hibernate 管理tx.commit();// 事务提交之后 数据库中插入 name="aa1" 记录session.close();// 此时的 user已经处于 detached 状态 因为与其关联的 session已经关闭

Page 16: 对象 / 关系映射  Hibernate 作者:赵春晖

实体对象的管理 - 对象的 3 种状态Transaction tx2=session2.beginTransaction();session2.update(user);// 此时 user 对象借助 session再次由 hibernate纳入//容器管理//已经由 Detached 状态改变为 Persistent;user.setName("aa1_1");// Persistenttx2.commit();// 由于属性变更将固化到数据库

Page 17: 对象 / 关系映射  Hibernate 作者:赵春晖

实体对象的管理 - 对象的 3 种状态在这里我们通过 update() 方法将 detached 对象再次与 hibernate 持久层关联因此 user 在次转变为 Persistent 状态 。那么自然会有这样一个问题,这个 Detached 的 user 对象和 Transient 的 user 对象到底有何不同 ?不同就在于 transient 的 user 对象并没有存在和数据库记录的对应关系而 Detached 的 user 是和数据库记录存在对应关系,不过Detached 的对象脱离了 session 的管理 其状态更新无法更新到 数据库表中的对应记录 。

Page 18: 对象 / 关系映射  Hibernate 作者:赵春晖

实体对象的管理 - 对象的 3 种状态• 如果我新建一个对象,然后人为的让它属性的值和数据库中的一条记录对应,• 包括 id的取值都一样。此时它能否说是处于游离状态呢?因为它和一条记录想对应呀。• 实际上这些情况都是由于一些不和规范的操作而产生的。在Hibernate应用中,• 无论Java对象处于临时状态、持久化状态还是游离状态,应用程序都不应该修改它的 ID。 ID的值应该由Hibernate来维护和负责。

Page 19: 对象 / 关系映射  Hibernate 作者:赵春晖

实体对象的管理 - 实体对象的识别• 实体对象的识别在 java 中,对象之间的比较主要通过以下 2 种方式 1 、对象引用的比较 (==)引用的作用是判断两个变量是否引用了同一个对象 例如: TUser u1=new TUser(); TUser u2=new TUser(); System.out.println(u1==u2);//false

Page 20: 对象 / 关系映射  Hibernate 作者:赵春晖

实体对象的管理 - 实体对象的识别 2 、内容比较 例如: String str1=“aaa”; String str2=“bbb”; System.out.println(str1.equals(str2));//false 内容比较的目的是为了判断两个对象所包含的数据是否 相同。 而在 hibernate 中,面对持久层逻辑,我们必须面对新的难题; 如何判读两个实体对象是否相等。

Page 21: 对象 / 关系映射  Hibernate 作者:赵春晖

实体对象的管理 - 实体对象的识别假设出现这样的情况: TUser u1=session.load(TUser.class,new Integer(1)); u1.setAge(23); TUser u2=session.load(TUser.class,new Integer(1));上面的实例中 u1 和 u2 这两个对象是否相等? 从 java语言规范的角度而言,这两个对象无论从引用还是从具体的内容都不相等。但是,站在持久层角度而言,这两个对象都代表着数据库中同一条记录 (t_user 表中 id=1 的记录 ) ,这种关系有由于持久层逻辑的出现而引入的,而这也引入了我们讨论的主题:实体对象的身份识别。

Page 22: 对象 / 关系映射  Hibernate 作者:赵春晖

实体对象的管理 - 实体对象的识别如何确定一个实体对象的身份?站在数据库的角度,我们认为,一个库表结构中,主键可以唯一确定一条记录,那么对于拥有同一主键值的实体对象,则认为他们是相同的,如上面的例子 只要其主键、相同,我们则认为他们相同。 对 hibernate 而言,这个规则也成立 org.hibernate.engine.EntityKey封装了用于区分两个实体的识别信息。在 EntityKey 中主要 维持了:实体 ID ,实体类名, ID 类型 等属性,Hibernate 通过实体 ID 实体类名即可确定这个实体在数据库中对应的记录,从而将其与其他对应不同记录的实体对象区分开。另外 EntityKey 在 hibernate 缓存中也扮演着数据标识的角色,Hibernate 将根据 EntityKey 在缓存中寻找是否有对应的数据存在

Page 23: 对象 / 关系映射  Hibernate 作者:赵春晖

实体对象的管理 - 实体对象的识别• equals 和 hashCode 方法:Java Collection 将通过 hashCode/equals 方法判定两个对象是否相等。我们知道 Set集合类型不允许出现两个相同对象。如:Set set=new HashSet();TUser u1=new TUser();TUser u2=new TUser();u1.setId(new Integer(1)),u2.setId(new Integer(1));set.add(u1),set.add(u2);System.out.println(“set size=”+set.size());// set size=2这里的 TUser 对象没有覆盖 Object.equals/hashCode 方法,因此Set 默认调用 Object 的 equals/hashCode 方法是否相等。

Page 24: 对象 / 关系映射  Hibernate 作者:赵春晖

实体对象的管理 - 实体对象的识别Object.equals() 只是默认比较对象的引用是否相当,显然 u1==u2 是不成立的,于是 Collection判定这两个对象不相等,将其分别纳入集合中。现在我们修改下 TUser 类,使之覆盖 Object.equals/hashCode 方法public TUser implements Serializable{………….

public int hashCode(){ return this.getId.hasCode(); } public boolean equals(Object obj){

TUser u=(TUser)obj; return this.getId.equals(u.getId());

} }

Page 25: 对象 / 关系映射  Hibernate 作者:赵春晖

实体对象的管理 - 实体对象的识别在次运行测试代码,我们看到输出: set size=1;Set 认为 u1 和 u2 是 2 个相同对象则在容器内维护一个 TUser 对象Collection 在判断 2 个对象是否相等的时候会先调用 hashCode 方法,如果 hashCode 的值相等则判断 equals 方法,如果两次判断都成功的话则认为 2 个对象相等。对于我们上面改造过的 TUser 类而言,无论其实例之间其他属性值有怎样的差异,只要 id 相等在 Set 中只会维持相同 id 的一个实例。那么,这对 hibernate又意味什么呢?

Page 26: 对象 / 关系映射  Hibernate 作者:赵春晖

实体对象的管理 - 实体对象的识别如果 id 相同则认为 2 个对象相同,这实际上也符合持久层的实体对象身份识别原则。首先我们为 TAddress添加 equals/hashCode 方法:public int hashCode(){

int result=17;result=37*result+(this.id!=null?this.id.hashCode():0);return result;

}public boolean equals(Object other){

if(this==other)return true;if(!(other instanceof TAddress))return false;final TAddress t=(TAddress)other;if(!(this.getId().equals(t.getId())))return false;

return true;

}

Page 27: 对象 / 关系映射  Hibernate 作者:赵春晖

实体对象的管理 - 实体对象的识别然后再运行下列代码:TUser user=session.load(TUser.class,new Integer(1)); TAddress t=new TAddress (“dao li”);TAddress t1=new TAddress (“nan gang”);user. getAddressSet().add(t);user. getAddressSet().add(t1);System.out.println(“address size=”+user. getAddressSet().size());在这里 会报 java.lang.NullPointerException错误,这是因为在添加地址 TAddress 的时候并未对 id 操作,此时 id=null, 而我们在覆盖的 equals 方法里面并为对 null 进行判断,这时我们需要对 id=null 的情况进行判断。对 TAddress 中的 equals 进行修改

Page 28: 对象 / 关系映射  Hibernate 作者:赵春晖

实体对象的管理 - 实体对象的识别那么 我们对 id=null 时判断应该是怎么样的呢?如果认为是相等的话那么, set size 会等于 1 ,这样我们在调用session.save(user) 的时候也只会向数据库发送一条地址数据,当然这不是我们期待的结果因为我添加了两个地址对象,所以对于 id=null 的情况我们应该判断为 不等,这样就不会发生上面的事情,所以修改的 equals 方法如下 :public boolean equals(Object other){

if(this==other)return true;if(!(other instanceof TAddress))return false;final TAddress t=(TAddress)other;if(this.getId()==null&&t.getId()==null)return false;//新添加if(!(this.getId().equals(t.getId())))return false;return true;

}

Page 29: 对象 / 关系映射  Hibernate 作者:赵春晖

实体对象的管理 - 实体对象的识别当然,对于是否采用覆盖 equals/hashCode 方法和如果采用覆盖方法,应该根据具体业务的需求进行判断决策

Page 30: 对象 / 关系映射  Hibernate 作者:赵春晖

目录• POJO 与 PO• 实体对象的管理• Hibernate 数据缓存• 持久层的操作

Page 31: 对象 / 关系映射  Hibernate 作者:赵春晖

Hibernate 数据缓存• Hibernate 数据缓存分为两个层次,以 hiberna

te语义加以区分可分为:1 、内部缓存 (Session level, 也称之为一级缓存 )

2 、二级缓存 (SessionFactory level, 也成为二级缓存 )

Page 32: 对象 / 关系映射  Hibernate 作者:赵春晖

Hibernate 数据缓存 -内部缓存内部缓存在 Hibernate 中又称为一级缓存,属于应用事务级缓存 .Session 在内部维护了一个 Map 数据类型,在 Map 中保持了所有与当前 Session 相关联的数据对象 . 如果我们需要通过 Session 加载摸个数据对象 ,Session 会首先根据所需要加载的数据类和 id ,在 entitysByKey 中寻找是否已有此数据的缓存实例,如果存在且状态判定有效则将此数据实例一结果返回 .内部缓存正常情况下由 Hibernate 自动维护,如需手动干预,可以通过以下方法完成: 1 ) session.evict 将某个特定的对象从缓存中清除。 2 ) Session.clear 清空内部缓存

Page 33: 对象 / 关系映射  Hibernate 作者:赵春晖

Hibernate 数据缓存 -二级缓存在 Hibernate 中,二级缓存涵盖了应用级缓存和分布式缓存 .二级缓存将由从属于本 SessionFactory 的所有Session 的实例共享,因此有时称 SessionFactory Level Cache 。Hibernate 在进行数据查询操作时,会首先在自身内部的一级缓存中进行查找,如果在一级缓存中未能命中,则将在二级缓存中进行查询,如果二级缓存命中则以此数据作为结果返回。

Page 34: 对象 / 关系映射  Hibernate 作者:赵春晖

Hibernate 数据缓存 -二级缓存在应用二级缓存时,我们首先必须考虑以下问题: 1 、数据库是否与其他应用共享 2 、应用是否部署在集群环境下 对于第一种情况,往往也就意味着要放弃二级缓存( 如 表和其他应用共享 ) 对于第二种情况,就要考虑是否需要引入分布式缓存机制,以及引入分布式缓存带来的性能变化 其次,哪些数据需要二级缓存?

Page 35: 对象 / 关系映射  Hibernate 作者:赵春晖

Hibernate 数据缓存 -二级缓存其次,哪些数据需要二级缓存?显然对所有数据都进行二级缓存是最简单的方法,但是如果数据量非常大的情况下反而会对性能产生极大的影响。因此,在考虑缓存机制应用策略的时候,我们你必须对数据逻辑进行考察,如果满足下列条件,则可将其纳入缓存管理

Page 36: 对象 / 关系映射  Hibernate 作者:赵春晖

Hibernate 数据缓存 -二级缓存1 、数据不会被第三方应用修改 2 、数据大小是否在可接受范围内 3 、数据更新频率较低 4 、同一数据可能会被系统频繁引用在 Hibernate 中启用二级缓存,需要在 hibernate.Cfg.xml 中配置下,如:<property name="hibernate.cache.provider_class">

org.hibernate.cache.EhCacheProvider</property>另外需要针对 Cache 实现本身进行配置,下面是一个 EhCache 配置文件的例子

Page 37: 对象 / 关系映射  Hibernate 作者:赵春晖

Hibernate 数据缓存 -二级缓存<ehcache>

<diskStore path="java.io.tmpdir"/><defaultCache

maxElementsInMemory="1000"// 缓存中保存的数据对象数量 eternal="false"// 缓存数据是否为常量 timeToIdleSeconds="1200"// 缓存数据钝化时间 timeToLiveSeconds="1200"// 缓存数据生存时间 overflowToDisk=“false” />//内存不足时是否启用磁盘缓存</ehcache>

Page 38: 对象 / 关系映射  Hibernate 作者:赵春晖

Hibernate 数据缓存 -二级缓存之后需要我们的映射文件中指定各个映射实体 , 的缓存同步策略:<class name=” com.ormdemo.domain.TUser”><cache usage=”read-wirte”/>….<set name=”address”> <cache usage=”read-write”></set></class>

Page 39: 对象 / 关系映射  Hibernate 作者:赵春晖

Hibernate 数据缓存 - 缓存同步策略缓存同步策略可应用于实体类和集合属性下面我们将围绕缓存同步策略进行讨论:缓存同步策略决定了数据对象在缓存中的存取规则。为了使缓存调度遵循正确的应用级事务隔离机制我们必须为每个实体类指定相应的缓存同步策略。Hibernate 提供以下 4 种缓存同步策略:

Page 40: 对象 / 关系映射  Hibernate 作者:赵春晖

Hibernate 数据缓存 - 缓存同步策略1 、 read-only 只读。对于不会发生变化的数据,可使用只读型缓存 2 、 nonstrict-read-wirte 如果程序对并发访问下的数据同步要求不是很严格,且数据更新操作频率较低 ( 几个小时或者更长时间更新一次 ) ,可以采用这种方式。3 、 read-write 严格的可读写缓存。基于时间戳判定机制,实现了 read-committed 事务隔离级。可用于对数据同步要求严格的情况,但不支持分布式缓存,这也是应用中最常用到的同步策略。

Page 41: 对象 / 关系映射  Hibernate 作者:赵春晖

Hibernate 数据缓存 - 缓存同步策略4 、 transactional 事务型缓存,必须运行在 JTA 事务环境中。在事务缓存中,缓存的相关操作也被添加到事务之中 ( 此时的缓存类似于内存数据库 ) ,如果由于某种原因导致事务失败,我们可以连同缓存池中的数据一同回滚到事务开始之前的状态,事务缓存实现了 Repeatable read 事务隔离级别,有效保证了数据的合法性,适用于关键数据的缓存。注:只有 JbossCache支持事务性的 Cache 实现

Page 42: 对象 / 关系映射  Hibernate 作者:赵春晖

目录• POJO 与 PO• 实体对象的管理• Hibernate 数据缓存• 持久层的操作

Page 43: 对象 / 关系映射  Hibernate 作者:赵春晖

持久层的操作下面我们就围绕 Hibernate 中常用的持久层实现原理进行探讨。首先是数据加载:Session.get/loadSession.get/load均可以根据指定的实体类和 id 从数据读取记录,并返回与之对应的实体对象 .

Page 44: 对象 / 关系映射  Hibernate 作者:赵春晖

持久层的操作 - 数据加载那么 get() 和 load 方法有什么区别呢?hibernate 中 get 方法和 load 方法的根本区别在于:如果你使用 load 方法, hibernate 认为该 id 对应的对象(数据库记录)在数据库中是一定存在的,所以它可以放心的使用,它可以放心的使用代理来延迟加载该对象。在用到对象中的其他属性数据时 才到二级缓存中查找,如果命中则返回,否则到数据库中进行查找,一旦数据库不存在该记录就会报org.hibernate.ObjectNotFoundException

Page 45: 对象 / 关系映射  Hibernate 作者:赵春晖

持久层的操作 - 数据加载我们看一个例子:TUser u1=impl.loadUser(new Integer(2));System.out.println("----------------output1-----------------");System.out.println("name="+u1.getName());这个是通过 session.load() 的方法加载数据。下面是 load方法的执行过程。(DefaultLoadEventListener.java:153) - loading entity: [co

m.ormdemo.domain.TUser#2]DEBUG [main] (DefaultLoadEventListener.java:230) - cr

eating new proxy for entity…………..

Page 46: 对象 / 关系映射  Hibernate 作者:赵春晖

持久层的操作 - 数据加载首先是在 session内部缓存中查找是否存在符合条件的实题对象或代理,如果发现则返回,没有的话则创建实体代理返回。当我们调用 System.out.println("name="+u1.getName());的时候,首先初始化代理程序 ( 此时实体代理必须由 sess

ion 管理,否则会报 org.hibernate.LazyInitializationExcepti

oN错误 ) 然后会在内部缓存和二级缓存中查找如果未能命中则到数据库中查找。

Page 47: 对象 / 关系映射  Hibernate 作者:赵春晖

持久层的操作 - 数据加载而对于 get() 方法, hibernate 会确认一下该 id 对应的数据是否存在,首先在 session 缓存中查找,然后在二级缓存中查找,还没有就查数据库,数据库中没有就返 null 。虽然好多书中都这么说:“ get()永远只返回实体类”,但实际上这是不正确的, get 方法如果在 session 缓存中找到了该 id 对应的对象,如果刚好该对象前面是被代理过的,如被 load 方法使用过,或者被其他关联对象延迟加载过,那么返回的还是原先的代理对象,而不是实体类对象,如果该代理对象还没有加载实体数据(就是 id 以外的其他属性数据)

Page 48: 对象 / 关系映射  Hibernate 作者:赵春晖

持久层的操作 - 数据加载那么它会查询二级缓存或者数据库来加载数据,但是返回的还是代理对象,只不过已经加载了实体数据 ( lazy loading 除外, hibernate3已经默认为 lazy loading ) .

Page 49: 对象 / 关系映射  Hibernate 作者:赵春晖

持久层的操作 - 数据加载Session.createQuery.list() 和 iterate()• List() 是执行 Select SQL 从数据库中获取所有符合的记录并构造实体对象,然后再实体对象纳入到缓存。• Iterare() 首先执行一条 Select SQL 以获取符合查询条件的数据 ID ,随即 iterate 方法首先会在本地缓存中根据 ID 查找对应的实体对象是否存在,如果缓存中存在数据,则直接以此数据对象作为查询结果,如果没找到则在执行 Select SQL语句获取对应的库表记录 ( 也就是典型的 N+1 查询 ) ,并购在对象纳入缓存• 关于实体加载请参考 get()/load()

Page 50: 对象 / 关系映射  Hibernate 作者:赵春晖

持久层的操作 - 数据保存数据保存 :Session.save()/pesist()Session.save();Session.save() 方法用于实体对象到数据库的持久化操。也就是说 session.save 方法调用与实体对象所匹配的Insert SQL ,将数据插入库表。 例: TUser user=new TUser(); user.setName(“aa1”); Transaction tx=session.beginTransaction(); Session.save(user); Tx..commit();

Page 51: 对象 / 关系映射  Hibernate 作者:赵春晖

持久层的操作 - 数据保存首先我们创建了 user 对象然后调用 save 方法进行保存,session.save 方法中包含了如下步骤: 1 、在 session 的内部缓存中 寻找待保存的对象,内部缓存命中,则认为以执行过 insert 操作,此时实体对象已经处于持久状态,直接返回

( 即使数据相对之前状态发生了变化,也将在稍后的事务提交时进行脏数据检查,并根据判定结果决定是否执行对应的 Update 操作 )2 、如果实体类实现了 lifecycle 接口,则调用待保存对象的 onSave 方法

Page 52: 对象 / 关系映射  Hibernate 作者:赵春晖

持久层的操作 - 数据保存3 、如果实体实现了 Validatable 接口,则调用其 validata

() 方法4 、调用对应拦截器的 Intercaptor.onSave() 方法 ( 如果有的话 )5 、构造 insert SQL 并执行 (注:只有 native 的情况下会构造 sql语句并执行,如果是由 hibernate 生成标识 identifier 则在 session.flush 中生成并执行 )6 、将 user 对象放入内部缓存 (save 不会把实体对象纳入到二级缓存 )7 、如果存在级联关系,对级联关系进行递归处理。

Page 53: 对象 / 关系映射  Hibernate 作者:赵春晖

持久层的操作 - 数据保存而 session.persist() 和 save() 的区别是这样的:1 、 persist 把一个瞬态的实例持久化,但是并“不保证”标识符被立刻填入到持久化实例中,标识符的填入可能被推迟到 flush 的时间。 2 、 persist“保证”,当它在一个 transaction 外部被调用的时候并不触发一个 Sql Insert ,这个功能是很有用的,当我们通过继承 Session/persistence context来封装一个长会话流程的时候,一个 persist这样的函数是需要的。

Page 54: 对象 / 关系映射  Hibernate 作者:赵春晖

持久层的操作 - 数据保存Session.update() 和 session.saveOrUpdate() 简单来说 update 和 saveOrUpdate 都是用来对跨 session的PO 进行状态管理的 ( 也就是修改 detached 对象 ) 。如果 PO 不需要进行跨 session 的话,则不需要要到,如:打开一个 session 对 PO 进行操作然后进行操作,之后 P

O不会在被用到了,那么则不需要 updateTUser user=session.load(TUser.class,new Integer(1));User.setName(“aa1_1”);Tx.commit();

Page 55: 对象 / 关系映射  Hibernate 作者:赵春晖

持久层的操作 - 数据保存在这里 PO 的操作都是在一个 session 生命周期内完成的,因此不需要对其进行显示的 session.update(user)操作,在 tx.commit() 的时候 hibernate 会对其进行脏数据的检查 判断数据发生变化然后向数据库发送 update sql而跨 Session 的意思就是说这个 PO 对象在 Session 关闭之后,你还把它当做一个 VO 来用,后来你在 Session 外面又修改了它的属性,然后你又想打开一个 Session ,把 VO的属性修改保存到数据库里面,那么你就需要用 update 了。

Page 56: 对象 / 关系映射  Hibernate 作者:赵春晖

持久层的操作 - 数据保存如:TUser user=new TUser();user.setName(“bb1”);Transaction tx=session.beginTransaction();Session.save(user);tx..commit();Session.close();// 此时 user 处于 detached 状态user.setName(“bb1_1”);Transaction tx2=session2.beginTransaction();Session2.update(user);tx2.commit();

Page 57: 对象 / 关系映射  Hibernate 作者:赵春晖

持久层的操作 - 数据保存对于 user 的修改操作 user.setName(“bb1_1”);hibernate是不知掉的因为是在 session 外部操作的,所以就需要显示的调用 update 方法告诉 hibernate 这个是需要 update 操作的。注意 update 方法本身并没有发送 Update SQL 完成数据更新操作 update sql 将在之后的 session.flush() 中执行(Transaction.commit() 在真正提交数据库事务之前会调用session.flush).

Page 58: 对象 / 关系映射  Hibernate 作者:赵春晖

持久层的操作 - 数据保存对于 user 的修改操作 user.setName(“bb1_1”);hibernate是不知掉的因为是在 session 外部操作的,所以就需要显示的调用 update 方法告诉 hibernate 这个是需要 update 操作的。注意 update 方法本身并没有发送 Update SQL 完成数据更新操作 update sql 将在之后的 session.flush() 中执行(Transaction.commit() 在真正提交数据库事务之前会调用session.flush).

Page 59: 对象 / 关系映射  Hibernate 作者:赵春晖

持久层的操作 - 数据保存对于 Session.saveOrUpdate();saveOrUpdate 和 update 的区别就在于在跨 Session 的 PO状态管理中, Hibernate 对 PO采取何种策略。如当你写一个 DAOImpl 的时候,让 user 对象增加一个address ,定义如下:public void add(User user,Address address){

Session session=getSession();Transaction tx=session.beginTransaction();Session.update(user);User.setAddress(address);Tx.commt();

}

Page 60: 对象 / 关系映射  Hibernate 作者:赵春晖

持久层的操作 - 数据保存显然你是需要把 Hibernate 的操作封装在 DAO 里面的,让业务层的程序员和 Web 层的程序员不需要了解Hibernate ,直接对 DAO 进行调用此时问题就来了:上面的代码运行正确有一个必要的前提,那就是方法调用参数TUser 对象必须是一个已经被持久化过的 PO ,也就是来说,它应该首先从数据库查询出来,然后才能这样用。但是业务层的程序员显然不知道这种内部的玄妙,如果他的业务是现在增加一个 user ,然后再增加它的 address ,他显然会这样调用, new 一个 TUser 对象出来,然后就add :

Page 61: 对象 / 关系映射  Hibernate 作者:赵春晖

持久层的操作 - 数据保存TUser user=new TUser();User.setName(“xxx”);这时 user 还是 vo 并不是 po 不能够调用 add 方法所以得这样操作:TUser user=new TUser();User.setName(“xxx”);Address address=new Addresss();User.setAddress(address);DAOImpl.save(user);Address address=new Addresss();User.setAddress(address);DAOImpl.add(user, address)

Page 62: 对象 / 关系映射  Hibernate 作者:赵春晖

持久层的操作 - 数据保存这个时候就可以用到 saveorupdate 了saveOrUpdate() 完成了如下工作: 1 、如果对象已经在这个 session 中持久化过了,什么都不用做 2 、如果对象没有标识值,调用 save() 来保存它 3 、如果对象的标识值与 unsaved-value 中的条件匹配,调用 save() 来保存它 4 、如果对象使用了版本 (version或 timestamp),那么除非设置 unsaved-value="null",版本检查会发生在标识符检查之前 . 5 、如果这个 session 中有另外一个对象具有同样的标识符,抛出一个异常

Page 63: 对象 / 关系映射  Hibernate 作者:赵春晖

持久层的操作 - 数据保存Session.flush();每间隔一段时间, Session 会执行一些必需的 SQ

L语句来把内存中的对象的状态同步到 JDBC连接中。这个过程被称为刷出 (flush) ,默认会在下面的时间点执行: 在某些查询执行之前 在调用 org.hibernate.Transaction.commit() 的时候 在调用 Session.flush() 的时候

Page 64: 对象 / 关系映射  Hibernate 作者:赵春晖

持久层的操作 - 数据保存涉及的 SQL语句会按照下面的顺序发出执行: 1 、所有对实体进行插入的语句,其顺序按照对象执行 Ses

sion.save() 的时间顺序 2 、所有对实体进行更新的语句 3 、所有进行集合删除的语句 4 、所有对集合元素进行删除,更新或者插入的语句 5 、所有进行集合插入的语句 6 、所有对实体进行删除的语句,其顺序按照对象执行 Ses

sion.delete() 的时间顺序 有一个例外是,如果对象使用 native 方式来生成 ID (持久化标识)的话,它们一执行 save 就会被插入。

Page 65: 对象 / 关系映射  Hibernate 作者:赵春晖

参考资料• 深入浅出 Hibernate——夏昕、曹晓钢• Hibernate3.2.0 reference • Javaeye论坛