Hibernate原理分析

  1.整体架构

  Hibernate工作原理

  Criteria是一种比hql面向对象询方式。Criteria 可使用 Criterion 和 Projection 设置查询条件。可以设置 FetchMode( 联合查询抓取的模式 ) ,设置排序方式,Criteria 还可以设置 FlushModel (冲刷 Session 的方式)和 LockMode (数据库锁模式)。

  ANTLR—Another Tool for Language Recognition,是一个开源语法分析器,其前身是PCCTS,它为包括Java,C++,C#在内的语言提供了一个通过语法描述来自动构造自定义语言的识别器(recognizer),编译器(parser)和解释器(translator)的框架。ANTLR可以通过断言(Predicate)解决识别冲突;支持动作(Action)和返回值(Return Value)来;更棒的是,它可以根据输入自动生成语法树并可视化的显示出来。由此,计算机语言的翻译变成了一项普通的任务。

  JTA,即Java Transaction API,JTA允许应用程序执行分布式事务处理——在两个或多个网络计算机资源上访问并且更新数据。
  JNDI(Java Naming and Directory Interface,Java命名和目录接口)是一组在Java应用中访问命名和目录服务的API。命名服务将名称和对象联系起来,使得我们可以用名称访问对象。目录服务是一种命名服务,在这种服务里,对象不但有名称,还有属性。

  2.流程

  (1)启动Hibernate
  (2)构建Configuration实例,初始化实例中的所有变量:
    Configuration cfg = Configuration.configure();
  (3)加载hibernate.cfg.xml文件至cfg实例所分配的内存
  (4)通过hibernate.cfg.xml文件 中格的mapping节点进行配置,并加载.hbm.xml文件至cfg实例中
  (5)由cfg实例构建一个SessionFactory实例:
    SessionFactory sf = cfg.buildSessionFactory();
  (6)由上面得到的sf实例创建Session连接:
    Session s = sf.openSession();
  (7)由Session实例创建事务操作接口Transaction的一个实例:
    Transaction tx = s.beginTransaction();
  (8)通过Session接口提供的各种方法操作对数据库的访问
  (9)提交数据库操作结果:
    tx.commit();
  (10)关闭Session连接:
    s.close();

  3.对象的状态

  

  *临时状态 (transient)

  1】不处于Session 缓存中 
  2】数据库中没有对象记录

  Java如何进入临时状态 
  1】通过new语句刚创建一个对象时 
  2】当调用Session 的delete()方法,从Session 缓存中删除一个对象时。

  *持久化状态(persisted) :它拥有持久化标识((持久化标识可以认为映射表的主键),Hibernate保证任何持久化标识和数据库行标识的关系

  1】处于Session 缓存中 
  2】持久化对象数据库中有对象记录 
  3】Session 在特定时刻会保持二者同步

  Java如何进入持久化状态 
  1】Session 的save()把临时-》持久化状态 
  2】Session 的load(),get()方法返回的对象 
  3】Session 的find()返回的list集合中存放的对象 
  4】Session 的update(),saveOrupdate()使游离-》持久化

  *游离状态(detached) :它也拥有持久化标识, 不保证和数据库的关联

  1】不再位于Session 缓存中 
  2】游离对象由持久化状态转变而来,数据库中可能还有对应记录。

  Java如何进入持久化状态-》游离状态 
  1】Session 的close()方法 
  2】Session 的evict()方法,从缓存中删除一个对象。提高性能。少用。

  3个状态的实例

  为了方便,先创建一个管理Session的工具类HibernateUtils.java,代码如下:

 1 package com.ysj.utils;
 2 
 3 import org.hibernate.Session;
 4 import org.hibernate.SessionFactory;
 5 import org.hibernate.cfg.Configuration;
 6 
 7 
 8 public class HibernateUtils {
 9     
10     /**
11      * 创建静态块,用于初始化SessionFactory
12      */
13     private static SessionFactory factory ;
14     static{
15         try{
16             // 默认获取src根目录下的 hibernate.cfg.xml配置文件
17             Configuration cfg = new Configuration().configure();
18             // 通过配置文件获取 sessionFactory
19             factory = cfg.buildSessionFactory() ; 
20         }catch(Exception e){
21             e.printStackTrace() ;
22         }
23     }
24     
25     /**
26      * 获得SessionFactory
27      */
28     public static SessionFactory getSessionFactory(){
29         return factory ;
30     }
31     
32     /**
33      * 获得Session
34      */
35     public static Session getSession(){
36         if(factory != null){
37             return factory.openSession() ;
38         }else{
39             return null ;
40         }
41     }
42     
43     /**
44      * 关闭Session
45      */
46     public static void closeSession(Session session){
47         if(session != null){
48             if(session.isOpen()){
49                 session.close() ;
50                 session = null ;
51             }
52         }
53     }
54 }
View Code

  测试类SessionTest.java,代码如下:

 1 package com.ysj;
 2 
 3 import java.util.Date;
 4 
 5 import junit.framework.TestCase;
 6 
 7 import org.hibernate.Session;
 8 import org.hibernate.Transaction;
 9 
10 import com.ysj.utils.HibernateUtils;
11 
12 public class SessionTest extends TestCase {
13     
14     /**
15      * 持久化对象生命周期的过程演示
16      */
17     public void testSave(){
18         Session session = null ;
19         Transaction tran = null ;
20         User user = null ;
21         try{
22             session = HibernateUtils.getSession() ;
23             tran = session.beginTransaction() ;
24             /**
25              * Transient(瞬态) :
26              * 1、在数据库中没有与之匹配的数据;
27              * 2、没有纳入session的管理;
28              * 3、如果在瞬态session 调用了 save()等方法就变为了持久态;
29              */  
30             user = new User() ;
31             user.setName("小一") ;
32             user.setPassword("xiaoyi") ;
33             user.setCreateTime(new Date()) ;
34             user.setExpireTime(new Date()) ;
35             /**
36              * Persistent(持久态) :
37              * 1、persistent状态的对象在数据库中有与之匹配的数据;
38              * 2、纳入了session的管理;
39              * 3、在清理缓存(脏数据检查)的时候,会和数据库同步40              * 4、在session关闭后,在持久态里面的内容,变为托管态,数据如果修改不会影响数据库对应值;
41              */
42             session.save(user);
43             
44             user.setName("小二");
45             /*
46              * 注意 :
47              * 在执行完tran.commit()方法后,会在控制台输出两条SQL语句,分别是
48              * Hibernate: insert into t_user (name, password, createTime, expireTime, id) values (?, ?, ?, ?, ?)
49              * Hibernate: update t_user set name=?, password=?, createTime=?, expireTime=? where id=?
50              * 
51              * session.save(user); // 执行到save()方法时插入数据库一条记录。
52              * user.setName("小二"); // 当对持久态对象进行修改时,会执行update操作。相当于在该条词句下面显示执行session.update(user);操作。 
53              */
54             tran.commit() ;
55             
56         }catch(Exception e){
57             if(tran != null){
58                 tran.rollback() ;
59             }
60             e.printStackTrace() ;
61         }finally{
62             HibernateUtils.closeSession(session);
63         }
64         /**
65          * Detached(托管态) :
66          * 1、在数据库中有与之匹配的数据;
67          * 2、没有纳入session的管理;
68          * 3、托管态对象中有主键值,瞬态的id没有主键值
69          */
70         user.setName("小三") ;
71         try{
72             session = HibernateUtils.getSession() ;
73             session.beginTransaction() ;
74             /*
75              * 将detached对象重新纳入session管理,此时将变为persistent状态的对象。
76              * persistent状态的对象,在清理缓存时会和数据库同步。
77              */
78             session.update(user);
79             session.getTransaction().commit() ;
80         }catch(Exception e){
81             session.getTransaction().rollback();
82             e.printStackTrace();
83         }finally{
84             HibernateUtils.closeSession(session);
85         }
86     }
87 }

  4.反射运用

  每次开发项目时,在做数据库开发时,对于不同类都有对应的Dao类,这就要要编写大量的Dao类,其中大多是代码堆砌,但有时我们要完成特定的操作,开发独立的Dao类是必须的,但如果只是实现数据的插入、读取、更新、删除,那么如果有一个通用的Dao类可以对数据库中的所有表进行操作,可以免去编写大量同质代码的负担。

  Reflection是Java被视为动态语言的一个关键性质,这个机制允许程序在运行时通过Reflection APIs却任何一个一直名称的的class的内部信息,包括modifiers、superclass、实现的interfaces、fields和methods等所有信息,并可于运行时改变fields内容和调用methods。

    1)在运行时判断人一个对象所属的类(在插入一个类时,判断他属于哪张表,哪个配置XML)

    2)在运行是构造任意一个类的对象(查询时,获取数据库中的数据后,构建出目标类来)

    3)在运行时判断任意一个类所具有的成员变量和方法

    4)在运行时调用任意一个对象的方法(判断一个类有什么方法,才能在插入时get,查询时set)

  利用Java的反射机制就可以解决之前必须对知道每个类的属性和方法来调用的问题。

  5.动态代理

  Hibernate主要用了CGLIB和javassist。

  主要是延迟加载使用了这个技术:

  当 Hibernate 从数据库中初始化某个持久化实体时,该实体的关联实体属性是否随持久化类一起初始化呢?如果关联实体非常大,又不用马上调用它,没有必要一次加载所有的属性。所谓延迟加载就是等系统需要使用属性时才从数据库装载关联的数据。

public class Person 
 { 
     // 标识属性
     private Integer id; 
     // Person 的 name 属性
     private String name; 
     // 保留 Person 的 age 属性
     private int age; 
     // Family属性,是另一个表的属性
     private Family family; 
     // 下面省略了各属性的 setter 和 getter 方法
 ... 
 } 

  当Family属性特别大时,又不用马上访问,这时就用延迟加载。

  当初始化一个Person实体时,没有去访问Family属性,此时 Person实体所关联的 Family实体并不是 Family对象,而是一个 Family_$$_javassist_0 类的实例,这个类是 Hibernate 使用 Javassist 项目动态生成的代理类——当 Hibernate 延迟加载关联实体时,将会采用 Javassist 生成一个动态代理对象,这个代理对象将负责代理“暂未加载”的关联实体(所以必须需要继承或接口)。

  只要应用程序需要使用“暂未加载”的关联实体,Person_$$_javassist_0 代理对象会负责去加载真正的关联实体,并返回实际的关联实体——这就是最典型的代理模式。 

  6.缓存机制

  缓存是介于应用程序和物理数据源之间,其作用是为了降低应用程序对物理数据源访问的频次,从而提高了应用的运行性能。缓存内的数据是对物理数据源中的数据的复制,应用程序在运行时从缓存读写数据,在特定的时刻或事件会同步缓存和物理数据源的数据。

  缓存的介质一般是内存,所以读写速度很快。但如果缓存中存放的数据量非常大时,也会用硬盘作为缓存介质。缓存的实现不仅仅要考虑存储的介质,还要考虑到管理缓存的并发访问和缓存数据的生命周期
  Hibernate的缓存包括Session的缓存和SessionFactory的缓存,其中SessionFactory的缓存又可以分为两类:内置缓存和外置缓存。Session的缓存是内置的,不能被卸载,也被称为Hibernate的第一级缓存。SessionFactory的内置缓存和Session的缓存在实现方式上比较相似,前者是SessionFactory对象的一些集合属性包含的数据,后者是指Session的一些集合属性包含的数据。SessionFactory的内置缓存中存放了映射元数据和预定义SQL语句,映射元数据是映射文件中数据的拷贝,而预定义SQL语句是在Hibernate初始化阶段根据映射元数据推导出来,SessionFactory的内置缓存是只读的,应用程序不能修改缓存中的映射元数据和预定义SQL语句,因此SessionFactory不需要进行内置缓存与映射文件的同步。SessionFactory的外置缓存是一个可配置的插件。在默认情况下,SessionFactory不会启用这个插件。外置缓存的数据是数据库数据的拷贝,外置缓存的介质可以是内存或者硬盘。SessionFactory的外置缓存也被称为Hibernate的第二级缓存。
  Hibernate的这两级缓存都位于持久化层,存放的都是数据库数据的拷贝,为了理解二者的区别,需要深入理解持久化层的缓存的两个特性:缓存的范围和缓存的并发访问策略。

  缓存的范围

  缓存的范围决定了缓存的生命周期以及可以被谁访问。缓存的范围分为三类。
  1 、事务范围:缓存只能被当前事务访问。缓存的生命周期依赖于事务的生命周期,当事务结束时,缓存也就结束生命周期。在此范围下,缓存的介质是内存。事务可以是数据库事务或者应用事务,每个事务都有独自的缓存,缓存内的数据通常采用相互关联的的对象形式。
  2 、进程范围:缓存被进程内的所有事务共享。这些事务有可能是并发访问缓存,因此必须对缓存采取必要的事务隔离机制。缓存的生命周期依赖于进程的生命周期,进程结束时,缓存也就结束了生命周期。进程范围的缓存可能会存放大量的数据,所以存放的介质可以是内存或硬盘。缓存内的数据既可以是相互关联的对象形式也可以是对象的松散数据形式。松散的对象数据形式有点类似于对象的序列化数据,但是对象分解为松散的算法比对象序列化的算法要求更快。
  3 、集群范围:在集群环境中,缓存被一个机器或者多个机器的进程共享。缓存中的数据被复制到集群环境中的每个进程节点,进程间通过远程通信来保证缓存中的数据的一致性,缓存中的数据通常采用对象的松散数据形式。
  事务范围的缓存是持久化层的第一级缓存,通常它是必需的;进程范围或集群范围的缓存是持久化层的第二级缓存,通常是可选的。

  并发访问(类似数据库的隔离级别)

  当多个并发的事务同时访问持久化层的缓存的相同数据时(二级缓存),会引起并发问题,必须采用必要的事务隔离措施。
  *事务型:仅仅在托管环境中适用。它提供了Repeatable Read事务隔离级别。对于经常被读但很少修改的数据,可以采用这种隔离类型,因为它可以防止脏读和不可重复读这类的并发问题。
  *读写型:提供了Read Committed事务隔离级别。仅仅在非集群的环境中适用。对于经常被读但很少修改的数据,可以采用这种隔离类型,因为它可以防止脏读这类的并发问题。
  *非严格读写型:不保证缓存与数据库中数据的一致性。如果存在两个事务同时访问缓存中相同数据的可能,必须为该数据配置一个很短的数据过期时间,从而尽量避免脏读。对于极少被修改,并且允许偶尔脏读的数据,可以采用这种并发访问策略。
  *只读型:对于从来不会修改的数据,如参考数据,可以使用这种并发访问策略。
  事务型并发访问策略是事务隔离级别最高,只读型的隔离级别最低。事务隔离级别越高,并发性能就越低。

  二级缓存

  Hibernate提供了两级缓存,第一级是Session的缓存。由于Session对象的生命周期通常对应一个数据库事务或者一个应用事务,因此它的缓存是事务范围的缓存。第一级缓存是必需的,不允许而且事实上也无法卸除。在第一级缓存中,持久化类的每个实例都具有唯一的OID
  第二级缓存是一个可插拔的的缓存插件,它是由SessionFactory负责管理。由于SessionFactory对象的生命周期和应用程序的整个过程对应,因此第二级缓存是进程范围或者集群范围的缓存。这个缓存中存放的对象的松散数据。第二级对象有可能出现并发问题,因此需要采用适当的并发访问策略,该策略为被缓存的数据提供了事务隔离级别。缓存适配器用于把具体的缓存实现软件与Hibernate集成。第二级缓存是可选的,可以在每个类或每个集合的粒度上配置第二级缓存。
  适合存放到第二级缓存中的数据
  1 很少被修改的数据
  2 不是很重要的数据,允许出现偶尔并发的数据
  3 不会被并发访问的数据
  4 参考数据
  不适合存放到第二级缓存的数据
  1 经常被修改的数据
  2 财务数据,绝对不允许出现并发
  3 与其他应用共享的数据。
  特点
  1) 条件查询的时候,总是发出一条select * from table_name where …. (选择所有字段)这样的SQL语句查询数据库,一次获得所有的数据对象。
  2) 把获得的所有数据对象根据ID放入到第二级缓存中。
  3) 当Hibernate根据ID访问数据对象的时候,首先从Session一级缓存中查;查不到,如果配置了二级缓存,那么从二级缓存中查;查不到,再查询数据库,把结果按照ID放入到缓存。
  4) 删除、更新、增加数据的时候,同时更新缓存。
  Hibernate的二级缓存策略,是针对于ID查询的缓存策略,对于条件查询则毫无作用。为此,Hibernate提供了针对条件查询的Query缓存
  Hibernate的Query缓存策略的过程如下:
  1) Hibernate首先根据这些信息组成一个Query Key,Query Key包括条件查询的请求一般信息:SQL, SQL需要的参数,记录范围(起始位置rowStart,最大记录个数maxRows),等。
  2) Hibernate根据这个Query Key到Query缓存中查找对应的结果列表。如果存在,那么返回这个结果列表;如果不存在,查询数据库,获取结果列表,把整个结果列表根据Query Key放入到Query缓存中。
  3) Query Key中的SQL涉及到一些表名,如果这些表的任何数据发生修改、删除、增加等操作,这些相关的Query Key都要从缓存中清空。

  7.Hibernate和JDBC的异同

  JDBC与Hibernate在性能上相比,JDBC灵活性有优势。而Hibernate在易学性,易用性上有些优势。当用到很多复杂的多表联查和复杂的数据库操作时,JDBC有优势。

  相同点:

  1两者都是java的数据库操作中的中间件

  2.两者对于数据库进行直接操作的对象都不是线程安全的,都需要及时关闭。

  3.两者都可以对数据库的更新操作进行显示的事务处理。

  不同点:

  1.使用的SQL语言不同:JDBC使用的是基于关系型数据库的标准SQL语言,Hibernate使用的是HQL语言。

  2.操作的对象不同:JDBC操作的是数据将数据通过SQL语句直接传送到数据库中执行,Hibernate操作的是持久化数据,由Hibernate底层将持久化对象中的数据更新到数据库中

  3.数据状态不同:JDBC操作的数据是“瞬时”的,变量的值无法与数据库中的值保持一致,而Hibernate操作的数据是可持久化的,即持久化对象的数据属性的值是可以跟数据库中的值保持一致的。

 

参考:http://blog.sina.com.cn/s/blog_4b81125f010157rn.html

http://www.itzhai.com/hibernate-framework-structure-and-work-processes.html

http://blog.csdn.net/myyate/article/details/1932846

http://ysj5125094.iteye.com/blog/1897505

百科

posted on 2013-11-22 21:24  依蓝jslee  阅读(334)  评论(0编辑  收藏  举报

导航