`

hibernate学习 小结

阅读更多

首先了解下hibernate的三种状态:

  Hibernate的对象有3种状态,分别为:瞬时态(Transient)、 持久态(Persistent)、脱管态(Detached)。处于持久态的对象也称为PO(Persistence Object),瞬时对象和脱管对象也称为VO(Value Object)。
 
瞬时态
  由new命令开辟内存空间的java对象,eg. Person person = new Person("xxx", "xx");
如果没有变量对该对象进行引用,它将被java虚拟机回收。
瞬时对象在内存孤立存在,它是携带信息的载体,不和数据库的数据有任何关联关系,在Hibernate中,可通过session的save()或saveOrUpdate()方法将瞬时对象与数据库相关联,并将数据对应的插入数据库中,此时该瞬时对象转变成持久化对象。

 

持久态
  处于该状态的对象在数据库中具有对应的记录,并拥有一个持久化标识。如果是用hibernate的delete()方法,对应的持久对象就变成瞬时对象,因数据库中的对应数据已被删除,该对象不再与数据库的记录关联。
当一个session执行close()或clear()、evict()之后,持久对象变成脱管对象,此时持久对象会变成脱管对象,此时该对象虽然具有数据库识别值,但它已不在HIbernate持久层的管理之下。
  持久对象具有如下特点:
  1. 和session实例关联;
  2. 在数据库中有与之关联的记录。

 

脱管态
  当与某持久对象关联的session被关闭后,该持久对象转变为脱管对象。当脱管对象被重新关联到session上时,并再次转变成持久对象。
  脱管对象拥有数据库的识别值,可通过update()、saveOrUpdate()等方法,转变成持久对象。
  脱管对象具有如下特点:
  1. 本质上与瞬时对象相同,在没有任何变量引用它时,JVM会在适当的时候将它回收;
  2.   比瞬时对象多了一个数据库记录标识值。
——————————————————————————————————————
1、hibernate拦截器、监听器
拦截器

  Interceptor 接口提供了从会话 (session) 回调 (callback) 应用程序 (application) 的机制, 这种回调机制可以允许应用程序在持久化对象被保存、更新、删除或是加载之前,检查并(或)修改其 属性。
  你可以直接实现 Interceptor 接口,也可以(最好)继承自 EmptyInterceptor 。 
  拦截器可以有两种 :Session 范围内的,和 SessionFactory 范围内的:
  当使用某个重载的 SessionFactory.openSession() 使用 Interceptor 作为参数调用打开一个 session 的时候,就指定了 Session 范围内的拦截器。
Session session = sf.openSession( new AuditInterceptor() );
  SessionFactory 范围内的拦截器要通过 Configuration 中注册,而这必须在创建 SessionFactory 之前。在这种情况下,给出的拦截器会被这个 SessionFactory 所打开的所有 session 使用了,除非 session 打开时明确指明了使用的拦截器。 SessionFactory 范围内的拦截器,必须是线程安全的,因为多个 session 可能并发使用这个拦截器,要因此小心不要保存与 session 相关的状态。
new Configuration().setInterceptor( new AuditInterceptor() );

 

监听器

  监听器在运行时被实例化为单例 (singleton) 对象,也就是说,所有同类型的事件的处理共享同一个监听器实例,因此监听器不应该保存任何与请求相关的状态。
用户定制的监听器需要实现相关的事件监听器接口,或者从一个合适的基类继承(甚至是从 Hibernate 自带的默认事件监听器类继承,作者已经通过这些类的 non-final 定义给予我们这样的暗示和权利了)。
  用户定制的监听器可以通过编程使用 Configuration 对象 来注册,也可以在 Hibernate 的 XML 格式的配置文件中进行声明(不支持在 Properties 格式的配置文件声明监听器)。 下面是一个用户定制的加载事件 (load event) 的监听器:
public class MyLoadListener implements LoadEventListener {
 // this is the single method defined by the LoadEventListener interface
public void onLoad(LoadEvent event, LoadEventListener.LoadType loadType) throws HibernateException {
if ( !MySecurity.isAuthorized( event.getEntityClassName(), event.getEntityId() ) )
{ throw MySecurityException("Unauthorized access"); }
}
}

  除此之外你还需要修改一处配置,来告诉 Hibernate ,除了默认的监听器,还要附加选定的监听器。
<hibernate-configuration>
<session-factory> ... <event type="load">
<listener class="com.eg.MyLoadListener"/>
<listener class="org.hibernate.event.def.DefaultLoadEventListener"/>
</event> </session-factory>
</hibernate-configuration>

  看看用另一种方式,通过编程的方式来注册它。
Configuration cfg = new Configuration(); LoadEventListener[] stack = { new MyLoadListener(), new DefaultLoadEventListener() }; cfg.EventListeners().setLoadEventListeners(stack);

 

2、save()和persist()的区别

  persist():只接受临时状态的对象,即主键没有值的对象,如果传递一个非临时状态的对象给它,则会抛出异常。
  (1)persist把一个瞬态的实例持久化,但是并"不保证"标识符被立刻填入到持久化实例中,标识符的填入可能被推迟到flush的时间。
  (2)persist"保证",当它在一个transaction外部被调用的时候并不触发一个Sql Insert,这个功能是很有用的,当我们通过继承Session/persistence context来封装一个长会话流程的时候,一个persist这样的函数是需要的。
  (3)save"不保证"第2条,它要返回标识符,所以它会立即执行Sql insert,不管是不是在transaction内部。

 

3、update和merge的区别
merge的作用:
  如果session中存在相同持久化标识(identifier)的实例,用用户给出的对象的状态覆盖旧有的持久实例
  如果session没有相应的持久实例,则尝试从数据库中加载,或创建新的持久化实例,最后返回该持久实例
  用户给出的这个对象没有被关联到session上,它依旧是脱管的。
  重点是最后一句:
  当我们使用update的时候,执行完成后,我们提供的对象A的状态变成持久化状态,但当我们使用merge的时候,执行完成,我们提供的对象A还是脱管状态,hibernate或者new了一个B,或者检索到一个持久对象B,并把我们提供的对象A的所有的值拷贝到这个B,执行完成后B是持久状态,而我们提供的A还是托管状态。

 

4、 所有的持久化对象都应具备一个无参的默认构造方法 ,使得hibernate可以通过constructor.newIntance()够着实体对象。

5、fecth只支持 inner join 和left join 联合查询的数据加载方式有效。

6、瞬时态和脱管态的区别 :数据库有无对应的信息。瞬时态无,脱管态有。

7、po 和 vo 的区别

  VO,值对象(Value Object),PO,持久对象(Persisent Object),它们是由一组属性和属性的get和set方法组成。从结构上看,它们并没有什么不同的地方。但从其意义和本质上来看是完全不同的。
  (1)VO是用new关键字创建,由GC回收的。
  PO则是向数据库中添加新数据时创建,删除数据库中数据时削除的。并且它只能存活在一个数据库连接中,断开连接即被销毁。
  (2)VO是值对象,精确点讲它是业务对象,是存活在业务层的,是业务逻辑使用的,它存活的目的就是为数据提供一个生存的地方。
  PO则是有状态的,每个属性代表其当前的状态。它是物理数据的对象表示。使用它,可以使我们的程序与物理数据解耦,并且可以简化对象数据与物理数据之间的转换。
  (3)VO的属性是根据当前业务的不同而不同的,也就是说,它的每一个属性都一一对应当前业务逻辑所需要的数据的名称。
  PO的属性是跟数据库表的字段一一对应的。
  (4)PO对象需要实现序列化接口。

 

8、脏数据检查策略

  脏数据并不是废弃和无用的数据,而是状态前后发生变化的数据。我们看下面的代码:
  Transaction tx=session.beginTransaction(); User user=(User)session.load(User.class,”1”);//从数据库中加载符合条件的数据 user.setName(“zx”);//改变了user对象的姓名属性,此时user对象成为了所谓的“脏数据” tx.commit();
  当事务提交时,Hibernate会对session中的PO(持久化对象)进行检测,判断持久化对象的状态是否发生了改变,如果发生了改变就会将改变更新到数据库中。这里就存在一个问题,Hibernate如何来判断一个实体对象的状态前后是否发生了变化。也就是说Hibernate是如何检查出一个数据已经变脏了。
  通常脏数据的检查有如下两种办法:
  A、数据对象监控:
  数据对象监控是通过拦截器对数据对象的setter方法进行监控来实现的,这类似于数据库中的触发器的概念,当某一个对象的属性调用了setter方法而发生了改变,这时拦截器会捕获这个动作,并且将改属性标志为已经改变,在之后的数据库操作时将其更新到数据库中。这个方法的优点是提高了数据更新的同步性,但是这也是它的缺点,如果一个实体对象有很多属性发生了改变,势必造成大量拦截器回调方法的调用,这些拦截器都是通过Dynamic Proxy或者CGLIB实现的,在执行时都会付出一定的执行代价,所以有可能造成更新操作的较大延时。
  B、数据版本比对:
  这种方法是在持久化框架中保存数据对象的最近读取版本,当提交数据时将提交的数据与这个保存的版本进行比对,如果发现发生了变化则将其同步跟新到数据库中。这种方法降低了同步更新的实时性,但是当一个数据对象的很多属性发生改变时,由于持久层框架缓存的存在,比对版本时可以充分利用缓存,这反而减少了更新数据的延迟。
  在Hibernate中是采用数据版本比对的方法来进行脏数据检查的,我们结合下面的代码来讲解  Hibernate的具体实现策略。
Transaction tx=session.beginTransaction(); User user=(User)session.load(User.class,”1”); user.setName(“zx”); tx.commit();
 
  当调用tx.commit();时好戏就此开场,commit()方法会调用session.flush()方法,在调用flush()方法时,会首先调用flushEverything()来进行一些预处理(如调用intercepter,完成级联操作等),然后调用flushEntities()方法,这个方法是进行脏数据检查的关键。
  在继续讲解之前,我要先来介绍一个内部数据结构EntityEntry,EntityEntry是从属于SessionImpl(Session接口的实现类)的内部类,每一个EntityEntry保存了最近一次与数据库同步的实体原始状态信息(如:实体的版本信息,实体的加锁模式,实体的属性信息等)。除了EntityEntry结构之外,还存在一个结构,这个结构称为EntityEntries,它也是SessionImpl的内部类,而且是一个Map类型,它以”key-value”的形式保存了所有与当前session实例相关联的实体对象和原始状态信息,其中key是实体对象,value是EntityEntry。而flushEntities()的工作就是遍历entityEntities,并将其中的实体对象与原始版本进行对比,判断实体对象是否发生来了改变。flushEntities()首先会判断实体的ID是否发生了改变,如果发生了改变则认为发生了异常,因为当前实体与EntityEntry的对应关系非法。如果没有发生异常,而且经过版本比对判断确实实体属性发生了改变,则向当前的更新任务队列中加入一个新的更新任务,此任务将在将在session.flush()方法中的execute()方法的调用中,转化为相应的SQL语句交由数据库去执行。最后Transaction将会调用当前session对应的JDBC Connection的commit()方法将当前事务提交。
  脏数据检查是发生在显示保存实体对象时,所谓显示保存是指在代码中明确使用session调用save,update,saveOrupdate方法对实体对象进行保存,如:session.save(user);但是有时候由于级联操作的存在,会产生一个问题,比如当保存一个user对象时,会根据user对象的状态来对他所关联的address对象进行保存,但是此时并没有根据级联对象的显示保存语句。此时需要Hibernate能根据当前对象的状态来判断是否要将级联对象保存到数据库中。此时,Hibernate会根据unsaved-value进行判断。Hibernate将首先取出目标对象的ID,然后将ID与unsaved-value值进行比较,如果相等,则认为实体对象尚未保存,进而马上将进行保存,否则,则认为实体对象已经保存,而无须再次进行保存。比如,当向一个user对象新加入一个它所关联的address对象后,当进行session.save(user)时,Hibernate会根据unsaved-value的值判断出哪个address对象需要保存,对于新加入的address对象它的id尚未赋值,以此为null,与unsaved-value值相等,因此Hibernate会将其视为未保存对象,生成insert语句加以保存。如果想使用unsaved-value必须如下配置address对象的id属性:
…… <id name=”id” type=”java.lang.Integer” unsaved-value=”null”> <generator class=”increment”/> </id> ……
 
9、在集合缓存策略中read-only只会缓存索引值(ID)不会缓存实体。

10、集合映射中set 对象为String 的时候可能会出现问题,由于set不重复性,和java中String的特性导致。

11、一级缓存只缓存实体,不缓存属性。

12、clear()和evict()区别
  当调用save方法的时候,hibernate把持久化对象添加到缓存,又把该对象添加到一个要进行插入的集合inserts里,而调用evict方法只是把该对象从缓存中清空了,当执行flush操作或者事务提交之后,inserts执行完毕后要更新缓存中该对象的持久化标志位为已经持久化,但是没更新到,就出现上述错误,而调用clear会清空掉缓存和inserts集合、updates集合和deletes集合,所以调用clear之后不会出错,数据库中也不会有记录出现。
  如果hibernate的autocommit配置为true的话,可以在evict之前调用flush
  如果hibernate的autocommit配置为false的话,就不能那么用了,去掉evict

 

13、Session的不同操作对对象状态的影响
  Session的save()方法
  save()方法将一个瞬时对象转变为持久对象。

 

  Session的update()方法
  update()方法将一个分离对象转变为持久对象。

 

  Session的lock()方法
  调用lock()方法将对象同Session相关联而不强制更新。

 

  Session的merge()方法
  拷贝指定对象的状态到具有相同对象标识符的持久对象。

 

  Session的saveOrUpdate()方法
  saveOrUpdate()方法对于瞬时对象,执行save()方法,对于分离对象,执行update()方法。

 

  Session的load()和get()方法
 1、如果找不到符合条件的纪录,get()方法将返回null.而load()将会报出ObjectNotFoundEcception.
   2、load()方法可以返回实体的代理类实例,而get()永远只返回实体类.
(另外,很多地方都注明get()方法不会查询二级缓存,经测试,证明get()和load()方法都会查询二级缓存)

 

Session的delete()方法
   delete()方法用于从数据库中删除与持久化对象对应的记录。如果传入的是一个持久化对象,Session就执行一条delete语句。如果传入的参数是分离对象,先使分离对象与Session关联,使它变为持久化对象,然后才计划执行一个delete语句。

 

Session的evict()方法
   evict()方法从Session的缓存中删除一个持久对象。
   delete()方法用于从数据库中删除与持久化对象对应的记录。如果传入的是一个持久化对象,Session就执行一条delete语句。如果传入的参数是分离对象,先使分离对象与Session关联,使它变为持久化对象,然后才计划执行一个delete语句。
Session的evict()方法
evict()方法从Session的缓存中删除一个持久对象。

 

14、getCurrentSession()与openSession()的区别?
  * 采用getCurrentSession()创建的session会绑定到当前线程中,而采用openSession()创建的session则不会.
  * 采用getCurrentSession()创建的session在commit或rollback时会自动关闭,而采用openSession()创建的session必须手动关闭.
  使用getCurrentSession()需要在hibernate.cfg.xml文件中加入如下配置:
  * 如果使用的是本地事务(jdbc事务)
<property name="hibernate.current_session_context_class">thread</property>
  * 如果使用的是全局事务(jta事务)
<property name="hibernate.current_session_context_class">jta</property>

分享到:
评论

相关推荐

    hibernate学习总结文档

    **Hibernate 框架概述** Hibernate 是一个开源的对象关系映射(ORM)框架,它为 Java 开发者提供了一种在 Java 应用程序中管理关系...通过实践和深入学习,你可以更好地利用 Hibernate 实现高效、可靠的数据库应用。

    J2EE系统之-hibernate学习总结

    ### J2EE系统之-hibernate学习总结 #### 对象持久化理论 - **对象持久化定义**:将对象中的数据转换存储至外部持久性存储设备的过程,如数据库、磁盘等。 - **对象持久化的原因**: - 内存易失性:断电后数据丢失...

    hibernate学习总结

    **hibernate学习总结** Hibernate 是一款开源的对象关系映射(ORM)框架,它极大地简化了Java应用程序与数据库之间的交互。在Java世界中,Hibernate 提供了一种在对象模型和关系数据库之间进行转换的机制,使得开发...

    Struts+Spring+hibernate学习总结

    【Struts+Spring+Hibernate 学习总结】 SSH(Struts、Spring、Hibernate)是Java Web开发中的一个经典企业级应用架构,它将MVC设计模式应用到Web应用的各个层次,实现了良好的分层和解耦。这篇学习总结旨在帮助读者...

    Hibernate学习总结

    ### Hibernate学习总结 #### 一、Hibernate简介 Hibernate是一个开源的对象关系映射(ORM)框架,用于Java应用程序与数据库进行交互。它允许开发人员以面向对象的方式来操作数据库,从而简化了数据访问层的开发工作...

    struts\Hibernate 学习总结.doc

    在学习Hibernate的过程中,需要注意以下几点: 1. Hibernate产生的原因:Hibernate的出现主要是为了解决JDBC编程中的一些问题,如代码和SQL混杂,不利于维护;SQL语句使得程序失去面向对象的特性;数据模型变化需要...

    Hibernate_学习总结

    ### Hibernate学习总结与知识点详解 #### 一、Hibernate产生的背景及优势 Hibernate作为一个持久层框架,它的诞生解决了传统编程模式中业务逻辑与数据库访问代码混杂的问题,这不仅导致程序结构混乱,降低代码的...

    Hibernate学习笔记与总结

    **Hibernate学习笔记与总结** Hibernate 是一款开源的对象关系映射(ORM)框架,它为Java开发者提供了一种在关系数据库上操作对象数据的便捷方式。本文将深入探讨Hibernate的核心概念、配置、实体类、映射文件、...

Global site tag (gtag.js) - Google Analytics