Hibernate学习2--理解Hibernate的的核心接口,实例状态及缓存相关

第一篇已经介绍了一个完整的hibernate的运行环境,大概地找到了一些hibernate使用的感觉,这里不才,简单的介绍一下hibernate的一些概念,大部分内容都是在网上看到总结的,如果有错,还望各位斧正。

首先我们谈一下Hibernates的几个接口

1Configuration接口

这个接口其实就是Hiberante的configue文件,里面有数据库的信息,和Hibernate的配置属性,其主要作用就是用来查找文档并读取其中内容,并且可以用来创建对应的sessionfactory实例。因为我们的开发环境是使用了spring代理了这个接口,所以在程序中并没有遇到他,我们的sessionfactory也是直接从spring中取到的。单其本质还是使用这个接口实例化的。

2 SessionFactory接口

SessionFactory采用了工厂模式用来获取Session实例的。他和一个数据源配置是绑定的,也是线程安全的,一个实例可以被多个线程共享,所以一个应用中存在一个SessionFacotry就够了,它内部存放了大量预定义的SQL及元数据映射,所以是一个大对象。

3 Session接口

Session接口是从SessionFactory接口中获取的对象,用来做数据库的对象的增删改查。Session对象不是线程安全的,所以不能在多个线程中公用一个Session。例外Session接口是轻量级的对象,其创建和销毁不需要太多的资源。Session中有一个缓存,用来缓存当前工作单元加载的持久化对象。

4 Transcation接口

Trancscation接口主要用来控制数据库的事务接口,Hibernate本身底层实现了一个事务处理机制,也可以采用Tracnscation接口的实现,主要封装了底层的事务JDBC JTA CORBA接口。

5 Query和Criteria接口

Query和Criteria是Hibernate的查询接口,用于做数据库的查询,Query包装了HQL来查询,Criteia是完全分装了基于字符串形式的查询语句,比Query更面向对象,更擅长执行动态查询。

比如我们例子中的Query查询就可以改成Criteria查询

        Criteria criteria=session.createCriteria(Student.class);
        criteria.setFirstResult(firstResult);
        criteria.setMaxResults(maxResults);
        return criteria.list();

接下来,我们看看Hibernate中的3中状态,瞬时态,托管态和持久态,一个持久化类的实例可能处于三种不同的状态中的某一种。这三种状态的定义则与所谓的持久化上下文(persistence context)有关。Hibernate的Session对象就是这个所谓的持久化上下文:

1 瞬时态(transient),这种状态的实例没有与任何持久化上下文关联,是由new开辟内存空间的java对象,没有和session实例关联,也没有在数据库中存在数据。当其save后变为持久态。delete托管态后变为瞬时态。

2 持久态(persistent),这种状态的实例与某个持久化上下文有关联,拥有持久化标识(相当于主键)。可能和数据库有一个对应的行。save(),update(),query.list(),load()都可以转为持久态。在一个session中,对持久对象的改变不会马上对数据库进行变更,必须在transcation结束,也就是commit后才能在数据库中真正的变更。在同步之前的持久对象称为脏对象。

3 托管态(detached),该实例曾经与上下文关联过,但是上下文已经关闭,不在session的缓存中,但是数据库可能还有其对应的记录行。Session的close()会使Session的缓存清空,其所有的实例对象均托管。

transient状态的特征?
    * 在数据库中没有与之匹配的数据
    * 没有纳入session的管理   
persistent状态的特征?
    * persistent状态的对象在数据库中有与之匹配的数据
    * 纳入了session的管理
    * 在清理缓存(脏数据检查)的时候,会和数据库同步  
detached状态的特征?
    * 在数据库中有与之匹配的数据
    * 没有纳入session的管理

4 几种状态的转换。

(1)save方法用于把瞬时态对象保存到数据库中,对象有瞬时态变成持久化态。当对象在持久化态时,其一直位于session的缓存中,对它的任何操作都将在事务提交时同步到数据库中。因此对一个已经持久化的对象调用save或update是没有意义的。

        Transaction tran = session.getTransaction();
        tran.begin();
        session.save(student);
        student.setName("haha");
        tran.commit();

比如上面的代码还会把name属性变为haha。而不需要额外的update或者save方法

(2)update方法用于重新关联托管对象为持久化对象。当对象已经是持久化对象时,调用update就没有多大的意义了。

   Transaction transaction=session.getTransaction();
        Student student= (Student) session.get(Student.class,id);
        student.setName("haha1");
        //session.update(student);
        transaction.commit();

上面的代码会吧student对象的name属性变为haha1,而不需要额外调用update

Hibernate 总是执行 update 语句,不管这个脱管对象在离开 Session 之后有没有更改过,在清理缓存时Hibernate 总是发送一条 update 语句,以确保脱管对象和数据库记录的数据一致,如:

Student stu = new Strudnet();
stu.setCarId(“1234”);
// 打开 Session1, 开启事务
session1.save(stu);
// 提交事务,关闭 Session1
stu.set(“4567”); // 对脱管对象进行更改
// 打开 Session2, 开启事务
session2.update(stu);
// 提交事务,关闭 Session2

注:即使把 session2.update(stu); 这句去掉,提交事务时仍然会执行一条 update() 语句。

如果希望只有脱管对象改变了, Hibernate 才生成 update 语句,可以把映射文件中 <class> 标签的 select-before-update 设为 true, 这种会先发送一条 select 语句取得数据库中的值,判断值是否相同,如果相同就不执行update 语句。不过这种做法有一定的缺点,每次 update 语句之前总是要发送一条多余的 select 语句,影响性能。对于偶尔更改的类,设置才是有效的,对于经常要更改的类这样做是影响效率的。

(3)saveOrUpdate方法兼具save和update方法功能,对于传入对象,其先判断是托管对象还是瞬时对象,然后调用合适方法。

接下来我们在看看get方法和load方法的区别

简单来说,get和load的区别只有两个,第一个如果数据库中没有对应的对象,get方法加载返回的是一个null,load方法返回一个代理对象,如果后面有对这个对象的属性调用,则返回objectnotfoundexception。第二个是load支持延迟加载,而get不支持延迟加载。也就是说load方法调用时不会去进行数据库查询,而是只有用到对象时才会去数据库进行查询,而get是立即进行数据库查询。load是在hibernate的一级缓存中存放一个map对象,map的key就是查询的key。当需要调用这个id值时就去hibernte中查找map的keyi值,而不会去继续拧数据库查询,所以不会报错也不会进行数据库的操作。

load()方法查询数据时会先从session缓存(一级缓存)中查找,如果没有找到则会创建代理类,该代理类仅仅初始化了OID属性,当第一次访问其他属性值时,则会依次从二级缓存-->数据库查找,直到找到数据,最后将所有属性值赋给代理类。而get()方法则会直接按照一级缓存-->二级缓存-->数据库的顺序查找。

  Transaction transaction=session.getTransaction();
        Student student= (Student) session.get(Student.class,999);
        student.setName("haha1");
        transaction.commit();

该代码中studetn返回的是null

  Transaction transaction=session.getTransaction();
        Student student= (Student) session.load(Student.class,999);
        student.setName("haha1");
        transaction.commit();

该代码返回抛出的是objectnotfoundexception。

上面提到了Hibernate的lazy load,即延迟加载,我们再来看看lazyload的相关内容。

Hibernate的lazy loading 采用了一个HibernateSession来管理session,它的逻辑是每进行一次数据库操作,就开新的session,操作完成后立即关闭该session。

lazy可以用在如下标签上

* <class>标签上:可以取值true/false

* <property>标签上,可以取值true/false,这个特性需要类增强

* <set>/<list>等集合上,可以取值为true/false/extra

* <one-to-one>/<many-to-one>等标签上,可以取值false/proxy/no-proxy

例如:

 <class name="com.XXX.test.hibernate.studentTest.Student" table="student1" schema="zz_test" lazy="true">
        <id name="id" type="java.lang.Integer" column="id">
            <generator class="identity" />
        </id>
        <property name="name" type="java.lang.String">
            <column name="name" length="16" />
        </property>
    </class>

如果lazy的属性值为true,那么在使用load方法加载数据时,只有确实用到数据的时候才会发出sql语句;这样有可能减少系统的开销。

另外除了class和property可以设置lazy属性,集合可以设置成extra的。extra是一种比较聪明的懒加载策略,即调用集合的size/contains等方法的时候,hibernate并不会去加载整个集合的数据,而是发出一条聪明的SQL语句,以便获得需要的值,只有在真正需要用到这些集合元素对象数据的时候,才去发出查询语句加载所有对象的数据

在<one-to-one>/<many-to-one>标签上可以配置懒加载策略。可以取值为:false/proxy/no-proxy。proxy是hibernate对单端关联的默认懒加载策略,即只有在调用到其关联对象的方法的时候才真正发出查询语句查询其对象数据,其关联对象是代理类。no-proxy这种懒加载特性需要对类进行增强,使用no-proxy,其关联对象不是代理类。另外注意在class标签上配置的lazy属性不会影响到关联对象

在hibernate 的one-to-many,many-to-one,many-to-many中,为了效率的提高,我们一般都采用lazy机制,但使用spring的getHibernateTemplate().save(Object)时,HibernateTemplate试图每次在execute之前去获得Session,执行完就力争关闭Session 。也就是说Hibernate的Lazy初始化1:n关系时,你必须保证是在同一个Session内部使用这个关系集合,不然Hiernate将抛出Failed to lazily initialize a collection - no session or session was closed的例外。 

最后,我们再来看看Hibernate的缓存机制。

Hibernate层面有两级缓存机制,包括sesion缓存和sessionFactory缓存。

Session缓存,一级缓存. 

SessionFactory的缓存分为内置缓存和外置缓存.内置缓存中存放的是SessionFactory对象的一些集合属性包含的数据(映射元素据及预定义SQL语句等),对于应用程序来说,它是只读的.外置缓存中存放的是数据库数据的副本,其作用和一级缓存类似.二级缓存除了以内存作为存储介质外,还可以选用硬盘等外部存储设备. 

下面是缓存的管理方法

一级缓存的管理: 
evit(Object obj)  将指定的持久化对象从一级缓存中清除,释放对象所占用的内存资源,指定对象从持久化状态变为脱管状态,从而成为游离对象. 
clear()  将一级缓存中的所有持久化对象清除,释放其占用的内存资源 
contains(Object obj) 判断指定的对象是否存在于一级缓存中. 
flush() 刷新一级缓存区的内容,使之与数据库数据保持同步.

二级缓存的管理: 

evict(Class arg0, Serializable arg1)  将某个类的指定ID的持久化对象从二级缓存中清除,释放对象所占用的资源. 

evict(Class arg0)  将指定类的所有持久化对象从二级缓存中清除,释放其占用的内存资源. 

evictCollection(String arg0)  将指定类的所有持久化对象的指定集合从二级缓存中清除,释放其占用的内存资源. 

Hibernate支持二级缓存是通过插件方式来实现的,Hibernate提供了org.hibernate.cache.CacheProvider借口,它充当缓存插件与Hibernate之间的适配器 . 常用的二级缓存插件 
EHCache  org.hibernate.cache.EhCacheProvider 
OSCache  org.hibernate.cache.OSCacheProvider 
SwarmCahe  org.hibernate.cache.SwarmCacheProvider 
JBossCache  org.hibernate.cache.TreeCacheProvider 

下面是一个Ehcache的缓存的简单配置

hibernate.cfg.xml 

 <hibernate-configuration>
    <session-factory>
       <!-- 设置二级缓存插件EHCache的Provider类-->
       <property name="hibernate.cache.provider_class">
          org.hibernate.cache.EhCacheProvider
       </property>
       <!-- 启动"查询缓存" -->
       <property name="hibernate.cache.use_query_cache">
          true
       </property>
    </session-factory>
  </hibernate-configuration>

ehcache.xml 

<ehcache>
  <!-- maxElementsInMemory为缓存对象的最大数目, eternal设置是否永远不过期,timeToIdleSeconds对象处于空闲状态的最多秒数,timeToLiveSeconds对象处于缓存状态的最多秒数 -->
  <diskStore path="java.io.tmpdir"/>
    <defaultCache maxElementsInMemory="10000" eternal="false"  timeToIdleSeconds="300" timeToLiveSeconds="600" overflowToDisk="true"/>
</ehcache>

XXX.hbm.xml

<?xml version="1.0" encoding='UTF-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
                            "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
                            "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping>
   <class>
       <!-- 设置该持久化类的二级缓存并发访问策略 read-only read-write nonstrict-read-write transactional-->
       <cache usage="read-write"/>    
   </class>

</hibernate-mapping>

参考文档:http://www.iteye.com/topic/249465

总结一下,这个章节没有太多的Hibernate实例,只是系统的介绍了些Hibernate的接口和缓存相关的知识。内容比较杂。

但是对于理解Hibernate的机制还是非常重要的。

你可能感兴趣的:(Hibernate学习2--理解Hibernate的的核心接口,实例状态及缓存相关)