1 / 65

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

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

milt
Download Presentation

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

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 对象/关系映射 Hibernate作者:赵春晖 要求:熟悉Hibernate的基本配置、操作。 目标:深入理解Hibernate,熟悉其存储方式和加载方式等。

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

  3. POJO和PO的概念 • POJO:Plain Ordinary java object 从字面上理解就是无格式的普通的JAVA对象,可以简单理解为不含逻辑代码的值对象(Value Object),在Hibernate 理解为数据库表所对应的Domain Object ,从数据层面来看,POJO作为数据实体的表现形式,也成为实体类(Entity bean),POJO和Hibernate映射文件构成了Hibernate 的基础代码 • PO:persistent Object 持久化对象 • 也就是说 在一些Objet/Relation Mapping工具中能够做到维护数据库表记录的PO全是符合JAVABEAN规范的纯 JAVA对象。如下:

  4. POJO和PO的概念 public class TUser{ private Integer id; private String name; private Integer age; private Set address; public TUser(){ } ……… seter/geter }

  5. 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中是这样实现的

  6. POJO和PO的区别 • 编写POJO。 • 编译POJO。 • 直接运行,在运行期 由Hibernate的CGLIB将POJO动态的转化为PO。 • 由此可以看出 Hibernate是在运行期将 POJO转化为PO的而CGLIB的效率也很高在运行期生成PO的字节码也很快,效率的损失几乎可以忽略不计。在运行期生成PO的好处是 对于程序员来说是无法接触到PO的可以说PO对他们来说是透明的,程序员可以更自由的以POJO的概念来操作PO。

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

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

  9. 实体对象的管理-对象的3种状态 • 在Hibernate中最核心的概念就是 对实体对象的状态管理(也就是上面提到的 POJO/Domain Object)。 • 实体对象的状态有三种,Transient(瞬时状态)、Persistent(持久状态)、Detached (脱管状态)

  10. 实体对象的管理-对象的3种状态 • Transient(瞬时状态) • 也就是未被持久化的VO(value object)是由JVM维护其生命周期。 • 如: methodA(){ TUser user=new TUser() User.setName(”ZCH”); }

  11. 实体对象的管理-对象的3种状态 • Persistent(持久状态) • 此时实体对象处于由hibernate管理的状态,并且与数据库表记录同步。 • 例如: TUser user=new TUser(); TUser antherUser=new TUser(); user.setName("aa1"); antherUser.setName("aa2"); //此时 user和antherUser都处于transient状态

  12. 实体对象的管理-对象的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");// Persistent antherUser.setName("aa2_2");//transient tx2.commit(); //虽然没有显示调用save(),但此时user处于persistent //状态,hibenate会检查持久化对象的认识改动然后在当前操作单元 //结束后将对象数据与数据库同步

  13. 实体对象的管理-对象的3种状态 Detached(脱管状态) 也就是曾被持久化过,但现在和Session已经detached了,以VO的身份在运行 。 这种和Session已经detached(分离)的PO还能够进入另一个Session,继续进行PO状态管理,此时它就成为PO的第二种状态了。这种PO实际上是跨了Session进行了状态维护的。

  14. 实体对象的管理-对象的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已经关闭

  15. 实体对象的管理-对象的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已经关闭

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

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

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

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

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

  21. 实体对象的管理-实体对象的识别 假设出现这样的情况: 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的记录),这种关系有由 于持久层逻辑的出现而引入的,而这也引入了我们讨论的主题: 实体对象的身份识别。

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

  23. 实体对象的管理-实体对象的识别 • 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方法是否相等。

  24. 实体对象的管理-实体对象的识别 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()); } }

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

  26. 实体对象的管理-实体对象的识别 如果id相同则认为2个对象相同,这实际上也符合持久层的实体对象 身份识别原则。首先我们为TAddress添加equals/hashCode方法: publicint hashCode(){ int result=17; result=37*result+(this.id!=null?this.id.hashCode():0); return result; } publicboolean equals(Object other){ if(this==other)returntrue; if(!(other instanceof TAddress))returnfalse; final TAddress t=(TAddress)other; if(!(this.getId().equals(t.getId())))returnfalse; returntrue; }

  27. 实体对象的管理-实体对象的识别 然后再运行下列代码: 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,而我们在覆盖的e quals方法里面并为对null进行判断,这时我们需要对id=null的情况 进行判断。对TAddress中的equals进行修改

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

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

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

  31. Hibernate数据缓存 • Hibernate数据缓存分为两个层次,以hibernate语义加以区分可分为: 1、内部缓存(Session level,也称之为一级缓存) 2、二级缓存(SessionFactory level,也成为二级缓存)

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

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

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

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

  36. Hibernate数据缓存-二级缓存 1、数据不会被第三方应用修改 2、数据大小是否在可接受范围内 3、数据更新频率较低 4、同一数据可能会被系统频繁引用 在Hibernate中启用二级缓存,需要在hibernate. Cfg.xml中配置下,如: <property name="hibernate.cache.provider_class"> org.hibernate.cache.EhCacheProvider </property> 另外需要针对Cache实现本身进行配置,下面是一 个EhCache配置文件的例子

  37. Hibernate数据缓存-二级缓存 <ehcache> <diskStore path="java.io.tmpdir"/> <defaultCache maxElementsInMemory="1000"//缓存中保存的数据对象数量 eternal="false"//缓存数据是否为常量 timeToIdleSeconds="1200"//缓存数据钝化时间 timeToLiveSeconds="1200"//缓存数据生存时间 overflowToDisk=“false” />//内存不足时是否启用磁盘缓存 </ehcache>

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

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

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

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

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

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

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

  45. 持久层的操作-数据加载 我们看一个例子: 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: [com.ormdemo.domain.TUser#2] DEBUG [main] (DefaultLoadEventListener.java:230) - creating new proxy for entity …………..

  46. 持久层的操作-数据加载 首先是在session内部缓存中查找是否存在符合条件的实 题对象或代理,如果发现则返回,没有的话则创建实体代 理返回。 当我们调用System.out.println("name="+u1.getName()); 的时候,首先初始化代理程序(此时实体代理必须由sessio n管理,否则会报org.hibernate.LazyInitializationExceptio N错误)然后会在内部缓存和二级缓存中查找如果未能命中 则到数据库中查找。

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

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

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

  50. 持久层的操作-数据保存 数据保存 : 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();

More Related