`

Hibernate知识点

阅读更多

 

1 什么是Hibernate

    Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。 Hibernate可以应用在任何使用JDBC的场合

   ,既可以在Java的客户端程序使用,也可以在Servlet/JSP的Web应用中使用,最具革命意义的是,Hibernate可以在应用EJB的J2EE架构中取代CMP,完成数据持久化的重任

 

    2 Hibernate的核心类和接口:

   <1>Configuration

负责管理Hibernate的配置信息,这些配置信息都是从配置文件hibernate.cfg.xml或者

Hibernate.properties读取的,当然也可以自定义文件名称,只要在实例化Configuration

的时候指定具体的路径就可以了

   <2>SessionFactory

Configuration的实例会根据当前的配置信息,构造SessionFactory实例。

SessionFactory是线程安全的,一般情况下一个应用中一个数据库共享一个SessionFactory实例。

   <3>Session

一般的持久化方法(CRUD)都是通过Session来调用的,Session是非线程安全的

   <4>Transaction

事务管理

   <5>Query

查询的接口

 

   3 Hibernate的原理及步骤

 

1. 读取并解析配置文件 

//采用默认的hibernate.cfg.xml来启动一个Configuration的实例

例:Configuration configuration=new Configuration().configure();

//如要修改默认配置文件名 当然你可以选择不修改

Configuration configuration=new Configuration().configure(new File("src/xxx.xml"));

2. 读取并解析映射信息,创建SessionFactory

例:SessionFactory  sessionFactory = configuration.buildSessionFactory();

3. 打开Session实例(它是线程不安全的,是共享同一个session,所以要ThreadLocal模式来控制)

例:Session session = sessionFactory.openSession();

4. 创建事务Transaction

例:Transaction transaction = (Transaction) session.beginTransaction();

5. CRUD操作(执行数据操作 CRUD 增删查改)

例: 增加

    session.save(pojo对象);

    增加或者修改

    session.saveOrOpdate(pojo对象)//当修改时候,id不存在将被抛出异常

                     删除

                     session.delete(pojo对象)

    //根据主键加载

                    p1=(Person)session.load(pojo类.class, 主键值);

 

6. 提交事务

例:transaction.commit();

7. 关闭Session实例及SessionFactory

例:   session.close();

              sessionFactory.close();

 

  4.Session的管理

   Session是Hibernate运作的中心,对象的生命周期、事务的管理、数据库的存取,都与 Session息息相关,

   就如同在编写JDBC时需关心Connection的管理,以有效的方法创建、利用与回收Connection,以减少资源的消耗,

   增加系统执行效能一样,有效的Session管理,也是Hibernate应用时需关注的焦点。

    

   Session是由SessionFactory所创建, SessionFactory是线程安全的(Thread-Safe),您可以让多个

   线程同时存取SessionFactory而不会有数据共享的问题,然而Session则不是设计为线程安全的,所以试图让

   多个线程共享一个 Session,将会发生数据共享而发生混乱的问题。  

   在各种Session 管理方案中, ThreadLocal 模式得到了大量使用。ThreadLocal 是Java中一种较为特殊的

   线程绑定机制。通过ThreadLocal存取的数据,总是与当前线程相关,也就是说,JVM 为每个运行的线程,

   绑定了私有的本地实例存取空间,从而为多线程环境常出现的并发访问问题提供了一种隔离机制。

   首先,我们需要知道,SessionFactory负责创建Session,SessionFactory是线程安全的,多个并发线程

   可以同时访问一个SessionFactory 并从中获取Session 实例。而Session并非线程安全,也就是说,如果

   多个线程同时使 用一个Session实例进行数据存取,则将会导致Session 数据存取逻辑混乱

 

 

 

 

 

    5   Hibernate三种查询方式 1 hql 2 qbc《qbe  --- query by example》 3 sql

 

  1.hql    

 

           1.1 冒号法

    Query query=session.createQuery(" from Person p where name=:xxx");//from后面跟的是PO对象名,而不是表名

      query.setString("xxx", "ttt");

      List<Person> list=query.list();

for(Person p:list)

System.out.println(p.getName())

           1.2 问号法

    Query query=session.createQuery(" from Person p where name=?");

query.setString(0, "ttt");

List<Person> list=query.list();

for(Person p:list)

System.out.println(p.getName())

 

    模糊查询 举例

      问号法

     Query query=session.createQuery(" from Person p where name like ?");

      query.setString(0, "%a%");

      冒号法

       Query query=session.createQuery(" from Person p where name like :xxx");

query.setString("xxx", "%w%");

 

 

      2.QBC (  query by Criteria ) 

Criteria接口与DetachedCriteria(二者的区别就是DetachedCriteria可以离线操作,而Criteria不可以)

    

     <1>利用Restrictions进行条件筛选

            方法             說明 

Restrictions.eq 等於 

Restrictions.allEq 使用Map,使用key/value進行多個等於的比對 

Restrictions.gt 大於 > 

Restrictions.ge 大於等於 >= 

Restrictions.lt 小於 < 

Restrictions.le 小於等於 <= 

Restrictions.between 對應SQL的BETWEEN子句 

Restrictions.like 對應SQL的LIKE子句 

Restrictions.in 對應SQL的in子句 

Restrictions.and and關係 

Restrictions.or or關係 

Restrictions.sqlRestriction SQL限定查詢 

 

     

     <2>criteria.addOrder(Order.asc("id"));//排序

     

     举例1

     Criteria criteria = session.createCriteria(Person.class);

     criteria.add(Restrictions.like("name", "ad%"));  //模糊查询,姓名以ad开头,注意要加%号

     criteria.add(Restrictions.between("age", 20, 30));  //年龄在20到30之间

      criteria.addOrder(Order.asc("id"));//

     List<Person> list = criteria.list();

     for (Person p : list) {

 System.out.println(p);

     }

             上面是与查询 ,如果是或查询    查询等于20或者或者年龄为空的记录

      Criteria criteria = session.createCriteria(User.class);

               criteria.add(Restrictions.or( 

                   Restrictions.eq("age", new Integer(20)), 

                   Restrictions.isNull("age") 

               )); 

               List users = criteria.list();

 

 

 

 

            DetachedCriteria 使用  //?离线操作的意义何在?

            DetachedCriteria dc = DetachedCriteria.forClass(Person.class);

dc.add(Restrictions.eq("name","aaa"));

     List<Person> list = dc.getExecutableCriteria(session).list();

     for (Person p : list) {

 System.out.println(p.getId());

     }

 

 

 

 

      3.Sql

           <3.1>采用addScalar方法 一个字段一个字段指定类型

     SQLQuery sQLQuery = session.createSQLQuery("select * from user");  //普通的SQL语句

  

      sQLQuery.addScalar("id",Hibernate.INTEGER);

      sQLQuery.addScalar("name",Hibernate.STRING);

     List<Object[]> list = sQLQuery.list();    //注意,得到的是一个里面放的是object数组的List集合

     for (Object[] object : list) {

System.out.println(Arrays.toString(object));     //在这里可以根据自己的需要,进行封装

     }   

  <3.2>采用addEntity方法   如果想返回的是list里是person对象 

      SQLQuery sQLQuery = session.createSQLQuery("select {person.*} from person"); //注意这里的语法{person.*}

sQLQuery.addEntity("person", Person.class);

sQLQuery.addScalar("id",Hibernate.INTEGER);

      sQLQuery.addScalar("name",Hibernate.STRING);

List<Person> list = sQLQuery.list(); 

for (Person object : list) {

System.out.println(object); 

}

 

 

 

 

 

 

          总结:不管是SQL还是HQL,当你查询出来的不是整个对象,而是其中的某些字段,或者说:当几个表关联查询的时候,取二个表中的某些字段,      

  返回的不是一个封装好的对象的List集合,那么这个List里到底是什么东西呢,这里分为两种情况

           <1>如果只有一个字段,list里每个元素都是Object

  <2>如果多于一个字段,list里每个元素都是Object[], 所以,这时候就需要自己去封装对象了,将object数组中的元素迭代出来,

     然后封装到一个对象里。

 

 

      三种查询方式优缺点

HQL功能最强大,适合各种情况,但是动态条件查询构造起来很不方便 

Criteria最适合动态条件查询,不太适合统计查询,QBE还不够强大,只适合相当简单的查询 

NativeSQL可以实现特定数据库的SQL,但是可移植性就牺牲了 

利用 Query接口的setMaxResults方法 表示一次从list里拿多少条记录  通常用于分页中


 

 6.pojo的3种状态

 

瞬态  持久化  托管(离线)

 

一个PO有三种状态:

1、未被持久化的VO

此时就是一个内存对象VO,由JVM管理生命周期

2、已被持久化的PO,并且在Session生命周期内

此时映射数据库数据,由数据库管理生命周期

3、曾被持久化过,但现在和Session已经detached了,以VO的身份在运行

这种和Session已经detached的PO还能够进入另一个Session,继续进行PO状态管理,此时它就成为PO的第二种状态了。这种PO实际上是跨了Session进行了状态维护的。 在传统的JDO1.x中,PO只有前面两种状态,一个PO一旦脱离PM,就丧失了状态了,不再和数据库数据关联,成为一个纯粹的内存VO,它即使进入一个新的PM,也不能恢复它的状态了

 

POJO<Plain old java Object>, PO<Persistance Object>

         POJO<Plain old java Object>:一种纯粹的java,即private属性及对这个属性的get/set方法,不实现,不继承任何java框架的接口,类。

PO<Persistance Object>:持久对象,,

有时也被称为Data对象,对应数据库中的entity,可以简单认为一个PO对应数据库中的一条记录。

在hibernate持久化框架中与insert/delet操作密切相关。

PO中不应该包含任何对数据库的操作。

可以是由POJO发展而来

POJO(plain old java object)-------->PO(persistance Object)

POJO是沒有状态的,加了状态之后就变成了PO(POJO+xml就是PO);

 

 

 

 

  7 HIbernate主键详解

    1.Assigned

        Assigned方式由程序生成主键值,并且要在save()之前指定否则会抛出异常

 

        特点:主键的生成值完全由用户决定,与底层数据库无关。用户需要维护主键值,

            在调用session.save()之前要指定主键值。

 

        主键由外部程序负责生成,无需Hibernate参与。

    2.Hilo

        Hilo使用高低位算法生成主键,高低位算法使用一个高位值和一个低位值,

        然后把算法得到的两个值拼接起来作为数据库中的唯一主键。

 

        Hilo方式需要额外的数据库表和字段提供高位值来源。

 

        默认请况下使用的表是通过hi/lo 算法实现的主键生成机制,

        需要额外的数据库表保存主键生成历史状态。

    3.hibernate_unique_key

        默认字段叫作next_hi。next_hi必须有一条记录否则会出现错误。

 

        特点:需要额外的数据库表的支持,能保证同一个数据库中主键的唯一性,

        但不能保证多个数据库之间主键的唯一性。Hilo主键生成方式由Hibernate维护,

        所以Hilo方式与底层数据库无关,但不应该手动修改hi/lo算法使用的表的值,

        否则会引起主键重复的异常。

    4.Increment

        Increment方式对主键值采取自动增长的方式生成新的主键值,但要求底层数据库的支持Sequence。

        如Oracle,DB2等。需要在映射文件xxx.hbm.xml中加入Increment标志符的设置。

 

        特点:由Hibernate本身维护,适用于所有的数据库,不适合多进程并发更新数据库,

        适合单一进程访问数据库。不能用于群集环境。

        主键按数值顺序递增。此方式的实现机制为在当前应用实例中维持一个变量,以保存着当前的最大值,

        之后每次需要生成主键的时候将此值加1作为主键。

        这种方式可能产生的问题是:如果当前有多个实例访问同一个数据库,那么由于各个实例各自维护

        主键状态,不同实例可能生成同样的主键,从而造成主键重复异常。因此,如果同一数据库有多个

        实例访问,此方式必须避免使用。

    5.Identity

        Identity当时根据底层数据库,来支持自动增长,不同的数据库用不同的主键增长方式。

 

        特点:与底层数据库有关,要求数据库支持Identity,如MySQl中是auto_increment, SQL Server

        中是Identity,支持的数据库有MySql、SQL Server、DB2、Sybase和HypersonicSQL。

 

        Identity无需Hibernate和用户的干涉,使用较为方便,但不便于在不同的数据库之间移植程序。

        采用数据库提供的主键生成机制。如DB2、SQL Server、MySQL中的主键生成机制。

    6.Sequence

        Sequence需要底层数据库支持Sequence方式,例如Oracle数据库等

 

        特点:需要底层数据库的支持序列,支持序列的数据库有DB2、PostgreSql、Qracle、SAPDb等在不

        同数据库之间移植程序,特别从支持序列的数据库移植到不支持序列的数据库需要修改配置文件

 

        采用数据库提供的sequence 机制生成主键。如Oralce 中的Sequence。

        与hilo 类似,通过hi/lo 算法实现的主键生成机制,只是主键历史状态保存在Sequence中,

        适用于支持Sequence的数据库,如Oracle。

    7.Native

        Native主键生成方式会根据不同的底层数据库自动选择Identity、Sequence、Hilo主键生成方式

 

        特点:根据不同的底层数据库采用不同的主键生成方式。由于Hibernate会根据底层数据库采用

        不同的映射方式,因此便于程序移植,项目中如果用到多个数据库时,可以使用这种方式。

        由Hibernate根据底层数据库自行判断采用identity、hilo、sequence其中一种作为主键生成方式。

    8. uuid.hex

        UUID使用128位UUID算法生成主键,能够保证网络环境下的主键唯一性,也就能够保证在不同

        数据库及不同服务器下主键的唯一性。

 

        特点;能够保证数据库中的主键唯一性,生成的主键占用比较多的存贮空间

 

        由Hibernate基于128 位唯一值产生算法生成16 进制数值(编码后以长度32的字符串表示)作为主键。

    9.Foreign GUID

        Foreign用于一对一关系中。GUID主键生成方式使用了一种特殊算法,保证生成主键的唯一性,

        支持SQL Server和MySQL 

        使用外部表的字段作为主键。

 

        一般而言,利用uuid.hex方式生成主键将提供最好的性能和数据库平台适应性。

    10.uuid.string

        与uuid.hex 类似,只是生成的主键未进行编码(长度16)。在某些数据库中可能出现问题(如PostgreSQL)。

 

 

8 在Hibernate应用中如何处理批量更新和批量删除?---executeUpdate()方法支持

  Hibernate3.0对批量更新和批量删除提供了支持,能够直接执行批量更新或批量删除语句,无需把被更新或删除的对象先加载到内存中。以下是通过Hibernate3.0执行批量更新的程序代码: 

Session session = sessionFactory.openSession(); 

Transaction tx = session.beginTransaction(); 

String hqlUpdate = "update Customer set name = :newName where name = :oldName"; 

int updatedEntities = s.createQuery( hqlUpdate ) 

.setString( "newName", newName ) 

.setString( "oldName", oldName ) 

.executeUpdate(); 

tx.commit(); 

session.close(); 

以下是通过Hibernate3.0执行批量删除的程序代码: 

Session session = sessionFactory.openSession(); 

Transaction tx = session.beginTransaction(); 

String hqlDelete = "delete Customer where name = :oldName"; 

int deletedEntities = s.createQuery( hqlDelete ) 

.setString( "oldName", oldName ) 

.executeUpdate(); 

tx.commit(); 

session.close(); 

 

 

9  Hibernate 映射

    <1>基本映射

        基本属性映射

 

     <property name="PO属性名" column="数据库表列名" type="数据类型" />

 

   <2> 集合映射   知道是如何实现 加一个表利用主键关联

 

  1.List

<list name="list属性名" table="list对应表名">

<key column="list属性所在类的id(主键" not-null="true"></key>

<list-index column="list_order" />

<element type="list元素类型" column="list元素列名"></element>

</list>

 

2.Set

<set name="set属性名" table="set对应表名">

<key column="set属性所在类的id(主键" not-null="true"></key>

<element type="set元素类型" column="set元素列名"></element>

</set>

 

3.Map

<map name="map属性名" table="map对应表名">

<key column="map属性所在类的id(主键)" not-null="true"></key>

<map-key type="map的key的数据类型" column="map的key对应的列名"></map-key>

<element type="map的value的数据类型" column="map的value对应的列名"></element>

</map>

 

    <3>关联映射  重点掌握 一对多   级联保存,更新 以及删除

         单向映射和双向映射的区别:

单向:一方持有另一方的实例或集合  我中有你,你中没有我。也就是说:通过这个表可以查询到另外一个表数据,但是通过另外一个表,查询不到这个表的数据

双向:彼此持有对方的实例或集合    我中有你,你中也有我。也就是说:通过这个表可以查询到另外一个表的数据,通过另外一个表也可以查询到这个表的数据

 

 

 

 

  1对1

       <1> 外键关联实现

     单向: <many-to-one name="对象属性名" column="字段名(可不写则与属性名相同)" unique="true"/>

                      双向: <many-to-one name="对象属性名" column="字段名(可不写则与属性名相同)" unique="true"/>

            <one-to-one name="对象属性名" property-ref="另一对象属性名"/>

 

<2> 主键关联实现   一对一主键关联映射中,默认了cascade属性

     单向:

     <id name="id">

<generator class="foreign">

<param name="property">对象属性名</param>

</generator>

    </id>

     <one-to-one name="对象属性名" constrained="true"/>

 

     双向:

      

      <id name="id">

<generator class="foreign">

<param name="property">对象属性名</param>

</generator>

     </id>

     <one-to-one name="对象属性名" constrained="true"/>

 

           

   

   另一方

   <one-to-one name="对象属性名"/> 

    

1对多

               单向:

 

<set name="set属性名">

<key column="外键列名"/>

<one-to-many class="包名.类名"/>

</set>

 

      双向:

             

               <set name="set属性名">

<key column="外键列名"/>

<one-to-many class="包名.类名"/>

      </set>

 

 

 

      <many-to-one name="对象属性名" column="字段名(可不写则与属性名相同"/>

 

 

多对多  中间表

 

      单向:

       <set name="addresses" table="jointable">

<key column="personId"/>

<many-to-many column="addressId" class="Address"/>

</set>

 

双向  在多对多关联中 下面的两个方向配置字段一致 如果想不一致 则可以使用不一样的中间表名,不一致时候会没有默认值错误

 

                 <set name="set" table="jointable">

<key column="personid"/>

<many-to-many column="wifeid" class="com.Wife"/>

</set>

 

<set name="persons" inverse="true" table="jointable">

<key column="wifeid"/>

<many-to-many column="personid" class="Person"/>

</set>

 

 

 

 

        级联保存,更新 以及删除  在一对多演示   

级联删除分为两种删除

 <1>只把多方(子表)中的关联字段设置为null  当主表为pojo 也就是瞬时状态时

 <2>多方(子表)记录删除掉  当主表为po 也就是持久化状态时

 

<set name="set属性名" inverse="false" cascade="save-update,delete">

<key column="外键列名"/>

<one-to-many class="包名.类名"/>

</set>

 

 

 

      cascade(级联)与inverse(反转)的作用

 

     Cascade:

        用来说明当对主对象进行某种操作时是否对其关联的从对象也作类似的操作,常用的Cascade取值有:

        none,all,save-update,delete,lock,refresh,evict,replicate,persist,merge,

        delete-orphan(one-to-many)。

            一般以many-to-one,many-to-many不设置级联,

            在one-to-one和one-to-many不设置级联,

            在one-to-one和one-to-many中设置级联。

        说明:

            none:表示没有任何操作级联(默认值)

            All:表示所有的操作都级联

            Save-update:表示在save/update/save-update时产生级联

            Delete:表示删除时级联

            若要多个操作都产生级联则:可以用,分开如:save-update,delete

 

        Delete-orphan表示若在从对象中把对应的主对象的值修改为null则删除从对象。通常在one-one中使用。

        一般以many-to-one,many-to-many不设置级联,在one-to-one和one-to-many中设置save-update级联。

 

     Inverse表示:

        “是否放弃维护关联关系”(在Java里面个对象产生关联时,对数据库的影响),

        在one-to-many和many-to-many的集合定义中使用,inverse=“true”表示该对象不维护关联关系;

        该属性的值一般在使用有序集合时设置成false(注意hibernate的缺省值是false).

        One-to-many维护关联关系就是更新外键.many-to-many维护关联关系就是在中间表增减记录

 

 

 

 

 

    <4>继承映射:三种策略

 

  <1>每个类层次映射一个表         (表个数与类个数相同)

<hibernate-mapping>

<class name="test.Sup" table="sup">

<id name="id" column="id" type="java.lang.String">

<generator class="uuid.hex" />

</id>

<property name="name" column="name" type="java.lang.String" />

 

<joined-subclass name="test.Sub1" table="sub1">

      <key column="id" />

<property name="sex" type="java.lang.String" />

</joined-subclass>

 

<joined-subclass name="test.Sub2" table="sub2">

<key column="id" />

<property name="age" type="java.lang.String" />

</joined-subclass>

</class>

</hibernate-mapping>

 

 

<2>每个子类(具体类)映射一个表   (表个数与子类个数相同)

<hibernate-mapping>

 

<class name="test.Sup" table="sup" abstract="true">

<id name="id" column="id" type="java.lang.String">

<generator class="uuid.hex" />

</id>

<property name="name" column="name" type="java.lang.String" />

 

<union-subclass name="test.Sub1" table="sub1" >

<property name="sex" type="java.lang.String" />

</union-subclass>

 

<union-subclass  name="test.Sub2" table="sub2">

<property name="age" type="java.lang.Integer" />

</union-subclass>

</class>

</hibernate-mapping>

 

 

<3>所有类用一个表映射

<hibernate-mapping>

<class name="test.Sup" table="sup" discriminator-value="sup">

<id name="id" column="id" type="java.lang.String">

<generator class="uuid.hex" />

</id>

 

<discriminator column="personType" type="java.lang.String" /><!--在id属性标签后-->

<property name="name" column="name" type="java.lang.String" />

 

<subclass name="test.Sub1" discriminator-value="sub1">

<property name="sex" type="java.lang.String" />

</subclass>

 

<subclass name="test.Sub2" discriminator-value="sub2">

<property name="age" type="java.lang.Integer" />

</subclass>

</class>

</hibernate-mapping>

        三种方法的优缺点:

            1、每一个具体子类映射成单个数据库表,而抽象基类不参与映射。

                优点:

                数据操作实现简单,每个表中都包含自己所需要的具体子类的所有信息,减少了多表关联操作时的性能消耗。

                缺点:

                类的修改会导致相对应的表及其子类所对应表的更改。不支持多态查询。

                应用:

                适合在类层次结构上有一定数量的抽象类的情况下使用。

            2、将整个类层次映射为单个数据库表。

                这对于子类属性不多的情况非常有效。每个子类由识别列(discriminator column)区分。

                优点:

                实现简单,并支持多态。同时数据访问也比较简单,因为数据库表中包含了所有需要的信息。

                缺点:

                增加类层次中的耦合,类层次中任何类的属性的增加都有会导致表的变更。另外,对子类属性的修改错误将会影响到整个类的层次结构。当然也浪费了大量的数据库空间。表中引入区分子类的字段,子类的字段不能创建为空。

            3、继承关系中每个类均映射为一个数据库表

                优点:

                此时,与面向对象的概念是一致的,这种映射实现策略的最大好处就是关系模型完全标准化,关系模型和领域模型完全一致,易于修改基类和增加新的子类。

                缺点:

                数据库中存在大量的表,为细粒度级的数据模型,访问数据时将存在大量的关联表的操作,效率较低。

 

  《5》组件映射 

            在hibernate中,component是某个实体的逻辑组成部分,它与实体的根本区别是没有oid,

         component可以成为是值对象(DDD)

 

           采用component映射的好处:它实现了对象模型的细粒度划分,层次会更分明,复用率会更高

   

   <component name="contact(Javabean属性名)">

<property name="email"/>

<property name="address"/>

<property name="zipCode"/>

<property name="contactTel"/>

</component>

 

   <6> 组合映射 

         通常将复合主键相关的属性,单独放到一个类中

* 此类必须实现序列化接口

* 覆写hashcode和equals方法

 

 

 

   

12.  Hibernate 缓存机制

 

     12.1 Hibernate缓存概述

 

      缓存是介于物理数据源与应用程序之间,是数据库数据在内存中的存放临时copy的容器,

      其作用是为了减少应用程序对物理数据源访问的次数,从而提高了应用的运行性能。

Hibernate在进行读取数据的时候,根据缓存机制在相应的缓存中查询,

      如果在缓存中找到了需要的数据(我们把这称做“缓存命中"),则就直接把命中的数据作为结果加以利用,

      避免了建立数据库查询的性能损耗。

    

    简单介绍完Hibernate的缓存后,再结合Hibernate的获取数据方式来说明缓存的具体使用方式,

    在Hibernate中获取数据常用的方式主要有四种:Session.load、Session.get、Query.list、Query.iterator。 

    

                                                   是否延迟加载

            Session.load-------一级缓存-------二级缓存------------------------数据库

 

 

            Session.get-------一级缓存-------二级缓存------------------------数据库

 

 

   Query.list---------查询缓存-------------------------------------数据库-----------------这里有可能调用Session.load(如果从数据库出来时id集合)

 

                    寻找id集合

   Query.iterator ----------数据库--------session.load

 

 

 

 

 

2、Session.get 

 

在执行Session.get时,和Session.load不同的就是在当从缓存中获取不到时,直接从数据库中获取id对应的值。 

 

 

 

 

     12.2 Hibernate的缓存分为:

     

1、一级缓存(内置缓存)->session级别缓存,是Hibernate 的内置缓存    一级缓存在Hibernate中对应的即为session范围的缓存,也就是当session关闭时缓存即被清除,一级缓存在Hibernate中是不可配置的部分

                      测试一级缓存

       1 在同一个session中发出两次load或者是get查询 

不会发出sql,因为load使用缓存

2 在同一个session中发出两次iterate查询实体对象(查属性)

   2.1 查对象

    会发出查询id的sql,不会发出查询实体对象的sql,因为iterate使用缓存

   2.2查属性

     iterate查询普通属性,一级缓存不会缓存,所以发出sql

一级缓存是缓存实体对象的

3  开启两个session中发出load查询  

   

     会发出查询语句,session间不能共享一级缓存的数据

   因为它会伴随session的生命周期存在和消亡

 

4 在同一个session中先save,在发出load查询save过的数据

  //不会发出sql,因为save是使用缓存的

                             不会发出sql,因为save是使用缓存的

 

      2  二级缓存(应用级缓存):

         二级缓存也称进程级的缓存或SessionFactory级的缓存,二级缓存可以被所有的session共享

                  二级缓存的生命周期和SessionFactory的生命周期一致,SessionFactory可以管理二级缓存 ,二级缓存是缓存实体对象的

 

   什么样的数据适合存放到第二级缓存中?《人的登录信息》

1 很少被修改的数据

2 不是很重要的数据,允许出现偶尔并发的数据

3 不会被并发访问的数据

4 参考数据,指的是供应用参考的常量数据,它的实例数目有限,它的实例会被许多其类的实例引用,实例极少或者从来不会被修改。

不适合存放到第二级缓存的数据?

1 经常被修改的数据

2 财务数据,绝对不允许出现并发

3 与其他应用共享的数据。

 

     

 

              .开启二级缓存的步骤:

 

二级缓存的配置和使用:

* 开启二级缓存,修改hibernate.cfg.xml文件

<property name="hibernate.cache.use_second_level_cache">true</property>

* 指定缓存产品提供商,修改hibernate.cfg.xml文件

<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>

* 指定那些实体类使用二级缓存(两种方法)

* 在映射文件中采用<cache>标签

                                  <cache usage="read-only"/>  

 usage="read-only"是“只读”缓存策略。这个<cache>标签只能放在<class>标签的内部,而且必须处在<id>标签的前面

  这个<cache>标签放在哪些<class>标签下面,就说明会多这些类的对象进行缓存 

* 在hibernate.cfg.xml文件中,采用<class-cache>标签

<class-cache class="com.bjsxt.hibernate.Classes" usage="read-only" />   注意,这个<class-cache>标签必须放在<mapping>标签的后面。

 

               测试二级缓存

    1 开启两个session,分别调用load或者get

                         //不会发出sql,因为开启了二级缓存,session是共享二级缓存的

                     2 开启两个session,分别调用load,在使用SessionFactory清除二级缓存

       //管理二级缓存

SessionFactory factory = HibernateUtils.getSessionFactory();

//factory.evict(Student.class);

factory.evict(Student.class, 1);

 

//会发出查询sql,因为二级缓存中的数据被清除了

                     3 开启两个session 一级缓存和二级缓存的交互

       //仅向二级缓存读数据,而不向二级缓存写数据

session.setCacheMode(CacheMode.GET);

//发出sql语句,因为session设置了CacheMode为GET,所以二级缓存中没有数据

 

//只向二级缓存写数据,而不从二级缓存读数据

session.setCacheMode(CacheMode.PUT);

//会发出查询sql,因为session将CacheMode设置成了PUT

 

 

        3、查询缓存:

       查询缓存的生命周期和session无关

 

query.list----> 数据放到 查询缓存(id)与二级缓存

 

查询缓存是针对普通属性结果集的缓存 

对实体对象的结果集只缓存id

 

如果采用的是select po.property这样的方式那么value为整个结果集,如采用的是from这样的方式那么value为获取的结果集中各po对象的主键ID

查询缓存的生命周期,当前关联的表发生修改,那么查询缓存生命周期结束

 

查询缓存的配置和使用:

* 在hibernate.cfg.xml文件中启用查询缓存,如:

<property name="hibernate.cache.use_query_cache">true</property>

* 在程序中必须手动启用查询缓存,如:

query.setCacheable(true);

                        //查询缓存只对query.list()起作用,query.iterate不起作用,也就是query.iterate不使用

//查询缓存

 

 

                         测试  

 

  1 开启查询缓存,关闭二级缓存 开启一个session,分别调用query.list

      没有发出查询sql,因为启用了查询缓存

                  2  开启查询缓存,关闭二级缓存 开启两个session,分别调用query.list

      不会发出查询sql,因为查询缓存的生命周期和session无关

                           3    开启查询缓存,关闭二级缓存 开启两个session,分别调用query.iterate

       查询缓存只对query.list()起作用,query.iterate不起作用,也就是query.iterate不使用

  4  关闭查询缓存,关闭二级缓存  开启两个session,分别调用query.list查询实体对象

      会发出查询sql,因为list默认每次都会发出查询sql

 

  5 开启查询缓存,关闭二级缓存 开启两个session,分别调用query.list查询实体对象

     会发出n条查询语句,因为开启了查询缓存,关闭了二级缓存,那么查询缓存会缓存实体对象的id

     所以hibernate会根据实体对象的id去查询相应的实体,如果缓存中不存在相应的

     实体那么将发出根据实体id查询的sql语句,否则不会发出sql使用缓存中的数据

                  6 开启查询缓存,开启二级缓存 开启两个session,分别调用query.list查询实体对象

     不会发出查询sql,因为开启了二级缓存和查询缓存,查询缓存缓存了实体对象的id列表

     hibernate会根据实体对象的id列表到二级缓存中取得相应的数据

 

 

13Hibernate 延迟加载(lazy)

 

     重要的概念: 

   1、lazy的概念,指在需要的时候才发出sql

   2、lazy策略只是在session打开期间才是有效的

   3、Hibernate加载本身就是延迟加载,在*.hbm.xml配置文件中<class>里配置lazy="false"将其改

为非延迟加载。

   4、Hibernate属性的延迟加载,在*.hnm.xml配置文件中<property>里配置lazy="false"即可。

 

   注意:类的延迟加载并不影响属性的延迟加载;

 

 

 

  hibernate3.0中lazy有三个值,true,false,proxy,延迟加载

    Lazy的有效期:只有在session打开的时候才有效;session关闭后lazy就没效了。

    lazy策略可以用在:

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

        * <property>标签上,可以取值true/false,这个特性需要类增强  ???  要对类增强器进行类增强org.hibernate.tool.instrument.InstrumentTask

bulid.xml代码 用ant运行 对类进行增强

      <?xml version="1.0" encoding="UTF-8"?>

<project name="hibernateSample" default="instrument" basedir=".">

<property name="lib.dir" value="./lib"/>

<property name="classes.dir" value="./bin"/>

<path id="lib.class.path">

 <fileset dir="${lib.dir}">

 <include name="**/*.jar"/>

 </fileset>

 </path>

<target name="instrument">

    <taskdef name="instrument"

classname="org.hibernate.tool.instrument.InstrumentTask">

 

<classpath path="${classes.dir}"/>

<classpath refid="lib.class.path"/>

    </taskdef>

    <instrument verbose="true">

<fileset dir="${classes.dir}/com">

  <include name="Person.class"/><!-- 此处是要增强的类-->

</fileset>

    </instrument>

 </target>

 

</project>

 

 

 

 

 

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

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

 

 

    类(Class)的延迟加载:

 

  

 

        * 设置<class>标签中的lazy="true",或是保持默认(即不配置lazy属性)

        * 如果lazy的属性值为true,那么在使用load方法加载数据时,只有确实用到数据的时候才会发出sql语句;

            这样有可能减少系统的开销。

 

    集合(collection)的延迟加载:可以取值true,false,extra

        * 保持集合上的lazy的默认值,此时的效果和lazy="extra"是基本一样的。

        * 设置集合上的lazy=extra,此时的效果和lazy属性的默认值是基本一样的。但是推荐使用这个属性值

            ,因为在统计时这种情况显得比较智能。当然延迟

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics