`

缓存 hibernate

 
阅读更多
1.   Session---单数据加载---load/ get
Load方法根据指定的实体类和id从数据库装载认为存在的一条记录. 应该确保对象确实存在, 否则会抛出ObjectNotFoundException.

Load方法可返回实体的代理类实例, 可充分利用内部缓存和二级缓存中的现有数据.



get方法根据指定的实体类和id从数据库查询并装载一条记录.数据不存在将得到null.

get方法返回的永远是实体类. 只在内部缓存中进行数据查找, 如果没有数据就调用SQL完成数据读取.



1.出于性能考虑,避免无谓的数据库访问,Session在调用数据库查询功能之前,会先在缓存中进行查询。首先在第一级缓存中,通过实体类型和id进行查找,如果第一级缓存查找命中,且数据状态合法,则直接返回。
2.之后,Session会在当前 “NonExists”记录中进行查找,如果 “NonExists”记录中存在同样的查询条件,则返回null。“NonExists”记录了当前 Session实例在之前所有查询操作中,未能查询到有效数据的查询条件。如果Session中一个无效的查询条件重复出现,即可迅速做出判断。
3.对于load方法而言,如果内部缓存中没有发现有效数据,则查询第二级缓存,如果第二级缓存命中,则返回。
4.如果在缓存中未发现有效数据,则发起数据库查询操作(Select SQL).
5.如果经过查询未发现对应记录,则将此次查询的信息在 “NonExists”中加以记录,并返回null。
6.否则根据映射配置和Select SQL得到的 ResultSet,创建对应的数据对象. 并将其数据对象纳入当前Session实体管理容器(一级缓存)。
7.执行Interceptor.onLoad方法(如果有对应的Intercepeor)。
8.将数据对象纳入二级缓存。
9.如果数据对象实现了LifeCycle接口,则调用数据对象的onLoad方法。
10.返回数据对象。
2.   Session---批量数据查询---find/iterate|createQuery().list/iterate
Hibernate2的find/iterate分别返回list和Iterator, Hibernate3中,上述方法已经从Session接口中废除,统一由Query接口提供。Hibernate3将Session的createQuery()方法得到的Query对象调用list/iterate方法实现相同的功能. 从实现体制而言,这两个版本之间并没有什么差异。



find/ list方法通过一条select sql实现查询; 而iterate则执行1+ N次查询, 它首先执行select sql获取满足条件的id, 再根据每个id获取对应的记录.

find方法将执行Select HQL从数据库中获得所有符合条件的记录并构造相应的实体对象,实体对象构建完毕之后,就将其纳入缓存。为之后的iterate方法提供了现成的可用数据。

这样,之后iterate方法执行时,它首先执行一条Select SQL以获得所有符合查询条件的数据id,随即,iterate方法首先再本地缓存中根据id查找对应的实体对象是否存在(类似Session.load方法),如果缓存中已经存在对应的数据,则直接以此数据对象作为查询结果,如果没找到,再执行相应的Select语句获得对应的库表记录(iterate方法如果执行了数据库读取操作并构建了完整的数据对象,也会将其查询结果纳入缓存)。



根据java面向对象的继承层次,Object是所有类的父类,所以使用面对对象的持久化框架Hibernate可以执行下述操作:

    public static void testHibernateOO() {

       Session s = HibernateSession3.getSession();

       Iterator<Object> itor =(Iterator<Object>)s.createQuery("FROM java.lang.Object").list();

       while(itor.hasNext()){

           Object rowData =  itor.next();

           System.out.println(rowData.getClass()+":");

       }

    }

3.   Session批量数据查询与缓存利用的示例
如果执行下面的代码:

Tuser data =null;

Iterator<Tuser> dataItor = hibernate_session.createQuery(" From Tuser").iterate();

       while(dataItor.hasNext()){

           data = dataItor.next();

           System.out.println("iterate:" + data.getName() + ":" + data.getEmail());

       }



Hibernate: select tuser0_.id as col_0_0_ from T_user tuser0_

Hibernate: select tuser0_.id as id2_0_, tuser0_.name as name2_0_, tuser0_.email as email2_0_ from T_

user tuser0_ where tuser0_.id=?

iterate:AiSee11652:[1w@11, 1w@11, as@tsts.com]

Hibernate: select tuser0_.id as id2_0_, tuser0_.name as name2_0_, tuser0_.email as email2_0_ from T_

user tuser0_ where tuser0_.id=?

iterate:Washing:[aisee@163.com]



如果执行下面的代码:

Tuser data =null;

       List<Tuser> datas = hibernate_session.createQuery(" From Tuser").list();

       for(int i=0;i<datas.size();i++){

           data = datas.get(i);

           System.out.println("list:" + data.getName() + ":" + data.getEmail());

       }

       System.out.println("\r\nUse iterate");

    Iterator<Tuser> dataItor = hibernate_session.createQuery(" From Tuser").iterate();

       while(dataItor.hasNext()){

           data = dataItor.next();

           System.out.println("iterate:" + data.getName() + ":" + data.getEmail());

       }



Hibernate: select tuser0_.id as id2_, tuser0_.name as name2_, tuser0_.email as email2_ from T_user t

user0_

list:AiSee11652:[1w@11, 1w@11, as@tsts.com]

list:Washing:[aisee@163.com]



Use iterate

Hibernate: select tuser0_.id as col_0_0_ from T_user tuser0_

iterate:AiSee11652:[1w@11, 1w@11, as@tsts.com]

iterate:Washing:[aisee@163.com]

如果执行下面的代码:



       Tuser data =null;

       List<Tuser> datas = hibernate_session.createQuery(" From Tuser").list();

       for(int i=0;i<datas.size();i++){

           data = datas.get(i);

           System.out.println("list:" + data.getName() + ":" + data.getEmail());

       }

       System.out.println("\r\nUse iterate");

    Iterator<Tuser> dataItor = hibernate_session.createQuery(" From Tuser"). iterate();

       while(dataItor.hasNext()){

           data = dataItor.next();

           System.out.println("iterate:" + data.getName() + ":" + data.getEmail());

       }

      

       System.out.println("\r\nUse list AGAIN");

       datas = hibernate_session.createQuery(" From Tuser").list();

       for(int i=0;i<datas.size();i++){

           data = datas.get(i);

           System.out.println("list:" + data.getName() + ":" + data.getEmail());

       }

Hibernate: select tuser0_.id as id2_, tuser0_.name as name2_, tuser0_.email as email2_ from T_user t

user0_

list:AiSee11652:[1w@11, 1w@11, as@tsts.com]

list:Washing:[aisee@163.com]



Use iterate

Hibernate: select tuser0_.id as col_0_0_ from T_user tuser0_

iterate:AiSee11652:[1w@11, 1w@11, as@tsts.com]

iterate:Washing:[aisee@163.com]



Use list AGAIN

Hibernate: select tuser0_.id as id2_, tuser0_.name as name2_, tuser0_.email as email2_ from T_user t

user0_

list:AiSee11652:[1w@11, 1w@11, as@tsts.com]

list:Washing:[aisee@163.com]



Use list

Hibernate: select tuser0_.id as id2_, tuser0_.name as name2_, tuser0_.email as email2_ from T_user t

user0_

list:AiSee11652:[1w@11, 1w@11, as@tsts.com]

list:Washing:[aisee@163.com]

如果执行:

Tuser data =null;

       System.out.println("\r\nUse list");

       List<Tuser> datas = hibernate_session.createQuery(" From Tuser").list();

       for(int i=0;i<datas.size();i++){

           data = datas.get(i);

           System.out.println("list:" + data.getName() + ":" + data.getEmail());

       }

       hibernate_session.close();

       hibernate_session = MySessionFactory.getSession();

      

        System.out.println("\r\nUse iterate");

    Iterator<Tuser> dataItor = hibernate_session.createQuery(" From Tuser").iterate();

       while(dataItor.hasNext()){

           data = dataItor.next();

           System.out.println("iterate:" + data.getName() + ":" + data.getEmail());

       }

      

       System.out.println("\r\nUse list AGAIN");

       datas = hibernate_session.createQuery(" From Tuser").list();

       for(int i=0;i<datas.size();i++){

           data = datas.get(i);

           System.out.println("list:" + data.getName() + ":" + data.getEmail());

       }



Use list

Hibernate: select tuser0_.id as id2_, tuser0_.name as name2_, tuser0_.email as email2_ from T_user t

user0_

list:AiSee11652:[1w@11, 1w@11, as@tsts.com]

list:Washing:[aisee@163.com]



Use iterate

Hibernate: select tuser0_.id as col_0_0_ from T_user tuser0_

Hibernate: select tuser0_.id as id2_0_, tuser0_.name as name2_0_, tuser0_.email as email2_0_ from T_

user tuser0_ where tuser0_.id=?

iterate:AiSee11652:[1w@11, 1w@11, as@tsts.com]

Hibernate: select tuser0_.id as id2_0_, tuser0_.name as name2_0_, tuser0_.email as email2_0_ from T_

user tuser0_ where tuser0_.id=?

iterate:Washing:[aisee@163.com]



Use list AGAIN

Hibernate: select tuser0_.id as id2_, tuser0_.name as name2_, tuser0_.email as email2_ from T_user t

user0_

list:AiSee11652:[1w@11, 1w@11, as@tsts.com]

list:Washing:[aisee@163.com]

如果执行下面的代码:

System.out.println("\r\nUse iterate");

Iterator<Tuser> dataItor = hibernate_session.createQuery(" From Tuser").iterate();

       while(dataItor.hasNext()){

           data = dataItor.next();

           System.out.println("iterate:" + data.getName() + ":" + data.getEmail());

       }

      

       System.out.println("\r\nUse iterate AGAIN");

       dataItor = hibernate_session.createQuery(" From Tuser").iterate();

       while(dataItor.hasNext()){

           data = dataItor.next();

           System.out.println("iterate:" + data.getName() + ":" + data.getEmail());

       }

Use iterate

Hibernate: select tuser0_.id as col_0_0_ from T_user tuser0_

Hibernate: select tuser0_.id as id2_0_, tuser0_.name as name2_0_, tuser0_.email as email2_0_ from T_

user tuser0_ where tuser0_.id=?

iterate:AiSee11652:[1w@11, 1w@11, as@tsts.com]

Hibernate: select tuser0_.id as id2_0_, tuser0_.name as name2_0_, tuser0_.email as email2_0_ from T_

user tuser0_ where tuser0_.id=?

iterate:Washing:[aisee@163.com]



Use iterate AGAIN

Hibernate: select tuser0_.id as col_0_0_ from T_user tuser0_

iterate:AiSee11652:[1w@11, 1w@11, as@tsts.com]

iterate:Washing:[aisee@163.com]

如果执行下面的代码:

System.out.println("\r\nUse iterate");

Iterator<Tuser> dataItor = hibernate_session.createQuery(" From Tuser").iterate();

       while(dataItor.hasNext()){

           data = dataItor.next();

           System.out.println("iterate:" + data.getName() + ":" + data.getEmail());

       }

      

       hibernate_session.close();

       hibernate_session = MySessionFactory.getSession();

       System.out.println("\r\nUse iterate AGAIN");

       dataItor = hibernate_session.createQuery(" From Tuser").iterate();

       while(dataItor.hasNext()){

           data = dataItor.next();

           System.out.println("iterate:" + data.getName() + ":" + data.getEmail());

       }

Use iterate

Hibernate: select tuser0_.id as col_0_0_ from T_user tuser0_

Hibernate: select tuser0_.id as id2_0_, tuser0_.name as name2_0_, tuser0_.email as email2_0_ from T_

user tuser0_ where tuser0_.id=?

iterate:AiSee11652:[1w@11, 1w@11, as@tsts.com]

Hibernate: select tuser0_.id as id2_0_, tuser0_.name as name2_0_, tuser0_.email as email2_0_ from T_

user tuser0_ where tuser0_.id=?

iterate:Washing:[aisee@163.com]



Use iterate AGAIN

Hibernate: select tuser0_.id as col_0_0_ from T_user tuser0_

Hibernate: select tuser0_.id as id2_0_, tuser0_.name as name2_0_, tuser0_.email as email2_0_ from T_

user tuser0_ where tuser0_.id=?

iterate:AiSee11652:[1w@11, 1w@11, as@tsts.com]

Hibernate: select tuser0_.id as id2_0_, tuser0_.name as name2_0_, tuser0_.email as email2_0_ from T_

user tuser0_ where tuser0_.id=?

iterate:Washing:[aisee@163.com]



find方法(hibernate2)/Query的list(hibernate3)实际上不利用缓存,它对缓存只写不读。而iterate方法(hibernate2)/Query的iterate(hibernate3)则可以充分发挥缓存带来的优势,如果目标数据只读或者读取相对较为频繁,通过这种机制可以大大减少性能上的损耗。对于批量数据, hibernate可以自动延迟加载:



把数据库数据扩大后:

String hsql = " From Tuser as t where t.id<501";

       Tuser data =null;

      

       long end =0;        

       System.out.println("\r\nUse iterate");   

        long begin =System.currentTimeMillis();

       Iterator<Tuser> dataItor = hibernate_session.createQuery(hsql).iterate();

        end =System.currentTimeMillis();

       System.out.println("Time be userd:"+(end-begin));

       while(dataItor.hasNext()){

           data = dataItor.next();

           System.out.println("iterate:" + data.getName() + ":" + data.getEmail());

       }     



Use iterate

Hibernate: select tuser0_.id as col_0_0_ from T_user tuser0_ where tuser0_.id<501

Time be userd:15

Hibernate: select tuser0_.id as id0_0_, tuser0_.name as name0_0_, tuser0_.email as email0_0_ from T_

user tuser0_ where tuser0_.id=?

iterate:AiSee11652:[1w@11, 1w@11, as@tsts.com]

Hibernate: select tuser0_.id as id0_0_, tuser0_.name as name0_0_, tuser0_.email as email0_0_ from T_

user tuser0_ where tuser0_.id=?

iterate:Washing:[aisee@163.com]

Hibernate: select tuser0_.id as id0_0_, tuser0_.name as name0_0_, tuser0_.email as email0_0_ from T_

user tuser0_ where tuser0_.id=?

iterate:Washing:[aisee@163.com]

....

执行:

String hsql = " From Tuser as t where t.id<100001";

       Tuser data =null;

      

       System.out.println("\r\nUse list");

       long end =0;

        long begin =System.currentTimeMillis();

       

       System.out.println("\r\nUse iterate");   

        begin =System.currentTimeMillis();

       Iterator<Tuser> dataItor = hibernate_session.createQuery(hsql).iterate();

       while(dataItor.hasNext()){

           data = dataItor.next();

           //System.out.println("iterate:" + data.getName() + ":" + data.getEmail());

       }

        end =System.currentTimeMillis();

       System.out.println("Time be userd:"+(end-begin));

      

       System.out.println("\r\nUse iterate AGAIN");    

        begin =System.currentTimeMillis();

       dataItor = hibernate_session.createQuery(hsql).iterate();

       while(dataItor.hasNext()){

           data = dataItor.next();

           //System.out.println("iterate:" + data.getName() + ":" + data.getEmail());

       }

        end =System.currentTimeMillis();

       System.out.println("Time be userd:"+(end-begin));



Use list



Use iterate

Hibernate: select tuser0_.id as col_0_0_ from T_user tuser0_ where tuser0_.id<100001

Time be userd:4078



Use iterate AGAIN

Hibernate: select tuser0_.id as col_0_0_ from T_user tuser0_ where tuser0_.id<100001

Time be userd:3797



如果是用list, 不会利用任何缓存,更不会延迟加载:

System.out.println("\r\nUse list");

       long end =0;

        long begin =System.currentTimeMillis();

       List<Tuser> datas = hibernate_session.createQuery(hsql).list();

        end =System.currentTimeMillis();

       System.out.println("Time be userd:"+(end-begin));

       for(int i=0;i<datas.size();i++){

           data = datas.get(i);

           System.out.println("list:" + data.getName() + ":" + data.getEmail());

       }

Use list

Hibernate: select tuser0_.id as id0_, tuser0_.name as name0_, tuser0_.email as email0_ from T_user t

user0_ where tuser0_.id<501

Time be userd: 78

list:AiSee11652:[1w@11, 1w@11, as@tsts.com]

list:Washing:[aisee@163.com]

list:Washing:[aisee@163.com]

list:Washing:[aisee@163.com]

list:Washing:[aisee@163.com]

list:Washing:[aisee@163.com]

如果数据量较大, 结合iterate方法和Session/SessionFactory的evict方法逐条对记录进行处理,并将数据对象强制从缓存中移除, 将内存消耗保持再可以接受的范围之内。查询50万条:

System.out.println("\r\nUse iterate");   

        begin =System.currentTimeMillis();

       Iterator<Tuser> dataItor = hibernate_session.createQuery(hsql).iterate();

      

       while(dataItor.hasNext()){

           data = dataItor.next();

           data.getName();

           //System.out.println("iterate:" + data.getName() + ":" + data.getEmail());

           hibernate_session.evict(data);

           factory.evict(Tuser.class,data.getId());

       }

        end =System.currentTimeMillis();

       System.out.println("Time be userd:"+(end-begin));

Use iterate

Time be userd:2113969

直接查询并保存在缓存:

System.out.println("\r\nUse iterate");   

        begin =System.currentTimeMillis();

       Iterator<Tuser> dataItor = hibernate_session.createQuery(hsql).iterate();

      

       while(dataItor.hasNext()){

           data = dataItor.next();

           data.getName();

           //out.println("iterate:" + data.getName() + ":" + data.getEmail());

       //  hibernate_session.evict(data);

       //  factory.evict(Tuser.class,data.getId());

       }

        end =System.currentTimeMillis();

       System.out.println("Time be userd:"+(end-begin));

       out.println("Time be userd:"+(end-begin)+"<br />");

Use iterate

ERROR: 2007-01-13 20:55:28,687: StandardWrapperValve[line:253}: Servlet.service() for servlet jsp th

rew exception

java.lang.OutOfMemoryError: Java heap space



所以数据量过大, 应该避免使用find(list), 而使用iterate(iterate),并且手动清除hibernate的一,二级缓存。

4.   Query Cache(阑尾)
Query Cache中保存了之前查询操作执行过的Select SQL,以及由此查询产生的查询结果集 (包括查询对象的类型和id)。

之后发生查询请求的时候,Hibernate会首先根据查询的 SQL从Query Cache中检索,如果此SQL曾经执行过,则取出对应这个SQL的检索结果集,再根据这个结果集中的对象类型及其id,从缓存中取出对应的实体对象返回。

Query Cache中缓存的SQL及其结果集并非永远存在,当Hibernate发现此SQL对应的库表发生了变动(Update/Delete/Insert),会自动将Query Cache中对应表的 SQL缓存废除。因此,Query Cache只在特定的情况下产生作用:

1.完全相同的Select SQL重复执行。
2.再两次查询之间,此Select SQL对应的库表没有发生过改变。
由于以上两个条件的严格限制,Query Cache再实际应用中的意义并没有我们想象中的那么重大,因此,Hibernate在默认情况下也关闭了这个特性。

为了启用Query Cache,我们必须在Hibernate配置文件(hibernate.cfg.xml)中打开配置hibernate.cache.use_query_cache为ture,之后我们必须在Query 的查询执行之前, 调用Query的setQureyCache(true); 而且后面的查询想利用该缓存也需要调用Query的setQureyCache(true). 这样find(list)就可以利用Query Cache缓存了.

鉴于配置和使用比较麻烦, 所以用得不多.

5.   Hibernate2的find/iterate的批量查询条件
find(String) throws HibernateException;

find(String, Object, Type) throws HibernateException;

find(String, Object[], Type[]) throws HibernateException;



iterate(String)

iterate(String, Object, Type)

iterate(String, Object[], Type[])



其中的字符串参数表示一个可带查询参数的HQL查询字符串, 其中的每个 ? 代表一个参数, 而对应的Object[]参数表示每个?的值, 而Type[]数组代表每个参数的数值类型. 取自net.sf.hibernate.type.Type类的实现. net.sf.hibernate.type包下面有很多XXXType的实现类, 也可以使用net.sf.hibernate.Hibernate类的NullableType类型静态常量属性(表示Type类型的一个实现. 比如Hibernate.STRING表示一个字符串类型).



AddUserForm inData = (AddUserForm) form;

hsql =" From webapp.hibernate.pojo.UserPoJo As user2 Where user2.tel=? and user2.user=?";

s.find(hsql,new Object[]{inData.getTel(), inData.getUser()},

                                   new Type[]{new net.sf.hibernate.type.StringType(), new net.sf.hibernate.type.StringType()});



/* //或者:



s.find( hsql,

new Object[]{ inData.getTel(), inData.getUser() } ,

          new Type[]{ Hibernate. STRING, Hibernate.STRING } );



*/

6.   Hibernate2/3的Criteria批量查询条件
org.hibernate.criterion. Restrictions 及其final子类org.hibernate.criterion.Expression提供了从Criteria对象中增加查询条件的实现.

Hibernate3的HQL的查询条件可以用Expression的相关方法.



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

    criteria.add(Expression.eq("name", "Erica"));



数据查询与检索是Hibernate中的一个亮点。相对其他ORM实现而言,Hibernate提供了灵活多样的查询机制。其中包括:1. Criteria Query, 2. Hibernate Query Language (HQL), 3. SQL



Criteria Query通过面向对象化的设计,将数据查询条件封装为一个对象。简单来讲,Criteria Query可以看作是传统SQL的对象化表示,如:

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

criteria.add(Expression.eq("name","Erica"));

criteria.add(Expression.eq("sex",new Integer(1)));

这里的criteria 实例实际上是SQL “Select * from t_user where name=’Erica’ and sex=1”的封装(我们可以打开Hibernate 的show_sql 选项,以观察Hibernate在运行期生成的SQL语句)。

Hibernate 在运行期会根据Criteria 中指定的查询条件(也就是上面代码中通过criteria.add方法添加的查询表达式)生成相应的SQL语句。

这种方式的特点是比较符合Java 程序员的编码习惯,并且具备清晰的可读性。正因为此,不少ORM实现中都提供了类似的实现机制(如Apache OJB)。

对于Hibernate的初学者,特别是对SQL了解有限的程序员而言,Criteria Query无疑是上手的极佳途径,相对HQL,Criteria Query提供了更易于理解的查询手段,借助IDE的Coding Assist机制,Criteria的使用几乎不用太多的学习。

7.   Criteria 查询表达式, Criterion
org.hibernate.criterion. Expression extends Restrictions,  Restrictions的众多方法对应了SQL的限制条件, 自然也被Expression类继承. (特别地拥有and, or, sql等方法.) 那些方法返回一个Criterion接口的实现, 而这正式Criteria的add方法的参数类型. Criteria 本身只是一个查询容器,具体的查询条件需要通过Criteria.add方法添加到Criteria实例中。

Expression 对象具体描述了查询条件。Expression提供了对应的查询限定机制,包括:

Expression.eq 对应SQL“field = value”表达式。  如Expression.eq("name","Erica")

Expression.allEq 参数为一个Map对象,其中包含了多个属性-值对应关系。相当于多个Expression.eq关系的叠加。

Expression.gt 对应SQL中的 “field > value ” 表达式

Expression.ge 对应SQL中的 “field >= value” 表达式

Expression.lt 对应SQL中的 “field < value” 表达式

Expression.le 对应SQL中的 “field <= value” 表达式

Expression.between 对应SQL中的 “between” 表达式

如下面的表达式表示年龄(age)位于13到50区间内。

Expression.between("age",newInteger(13),new Integer(50));

Expression.like 对应SQL中的 “field like value” 表达式

Expression.in 对应SQL中的 ”field in …” 表达式

Expression.eqProperty 用于比较两个属性之间的值,对应SQL中的“field= field”。如:Expression.eqProperty("TUser.groupID","TGroup.id");

Expression.gtProperty 用于比较两个属性之间的值,对应SQL中的“field> field”。

Expression.geProperty 用于比较两个属性之间的值,对应SQL中的“field>= field”。

Expression.ltProperty 用于比较两个属性之间的值,对应SQL中的“field< field”。

Expression.leProperty 用于比较两个属性之间的值,对应SQL中的“field<= field”。

Expression.and and关系组合。

如:

Expression.and(Expression.eq("name","Erica"),Expression.eq("sex",new Integer(1)));

Expression.or or关系组合。如:

Expression.or(Expression.eq("name","Erica"),Expression.eq("name","Emma"));

Expression.sql 作为补充,本方法提供了原生SQL语法的支持。我们可以通过这个方法直接通过SQL语句限定查询条件。

下面的代码返回所有名称以“Erica”起始的记录:

Expression.sql(“lower({alias}.name) like lower(?)”,"Erica%",Hibernate.STRING);

其中的“{alias}”将由Hibernate在运行期使用当前关联的POJO别名替换。

注意Expression 各方法中的属性名参数(如Express.eq中的第一个参数),这里所谓属性名是POJO中对应实际库表字段的属性名(大小写敏感),而非库表中的实际字段名称。

Criteria,Query ,排序, 分页
8.   局部属性的级连数据排序
两种排序方式: 1.  Sort  2. order-by, 前者通过JVM完成, 后者由数据库完成.

1.sort :可排序Set在Hibernate中对应为net.sf.hibernate.collection. SortedSet类, 实现了java.util.SortedSet .  在set元素中可配置sort属性(sort='natural', 指定采用Java默认排序机制, 通过调用数据类型的compareTo方法. 可以自定义java.util.Comparator接口的实现, 来作为sort的属性值, 而实现自定义的排序算法. Map类型与Set基本一致, 但Bag和List不支持sort排序.
2.order-by:  在元素中增加order-by属性(比如order-by="address desc" )可以实现数据库排序. 该特性利用了JDK1.4+ 中的LinkedHashSet以及LinkedHashMap, 由此必须在环境JDK1.4以上才可成功. Set, Map, Bag支持, List不支持该特性.
9.   Criteria/Query分页, Criteria排序
限定返回的记录范围

通过Criteria(或者Query)的setFirstResult, setMaxResults 方法可以限制一次查询返回的记录范围:

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

//限定查询返回检索结果中,从第一百条结果开始的20条记录

criteria.setFirstResult(99); //Base 0

criteria.setMaxResults(20);

对查询结果进行排序

//查询所有groupId=2的记录  //并分别按照姓名(顺序)和groupId(逆序)排序

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

criteria.add(Expression.eq("groupId",new Integer(2)));

criteria.addOrder(Order.asc("name"));

criteria.addOrder(Order.desc("groupId"));

Criteria作为一种对象化的查询封装模式,不过由于Hibernate在实现过程中将精力更加集中在HQL查询语言上,因此Criteria的功能实现还没做到尽善尽美(这点上,OJB的Criteria 实现倒是值得借鉴),因此,在实际开发中,建议还是采用Hibernate 官方推荐的查询封装模式:HQL。



Hibernate Query Language (HQL)

Criteria提供了更加符合面向对象编程模式的查询封装模式。不过,HQL(Hibernate Query Language)提供了更加强大的功能,在官方开发手册中,也将HQL作为推荐的查询模式。相对Criteria,HQL提供了更接近传统SQL语句的查询语法,也提供了更全面的特性。最简单的一个例子:

String hql = "from org.hibernate.sample.TUser";

Query query = session.createQuery(hql);

List userList = query.list();

上面的代码将取出TUser的所有对应记录。如果我们需要取出名为“Erica”的用户的记录,类似SQL,我们可以通过SQL 语句加以限定:

String hql = "from org.hibernate.sample.TUser as user where user.name='Erica'";

Query query = session.createQuery(hql);

List userList = query.list();

其中我们新引入了两个子句“as”和“where”,as子句为类名创建了一个别名,而where子句指定了限定条件。

HQL子句本身大小写无关,但是其中出现的类名和属性名必须注意大小写区分。

关于HQL,Hibernate 官方开发手册中已经提供了极其详尽的说明和示例,详见本文HQL部分或者Hibernate官方开发手册。

延迟加载(Lazy Loading)
所谓延迟加载,就是在需要数据的时候,才真正执行数据加载操作, 避免性能浪费。

Hibernate 2中的延迟加载实现主要针对:实体对象和集合(Collectio)。Hibernate 3同时提供了属性的延迟加载功能。hibernate2默认lazy=false; 而hibernate3默认为true.

10. 实体对象的延迟加载
在关于Session.get/load方法的描述中,我们曾经提到,通过load方法我们可以指定可以返回目标实体对象的代理。

正常情况下,一个非延迟加载运行时,Hibernate已经从库表中取出了对应的记录,并构造了一个完整的TUser对象。

通过class的lazy="true"属性, 使用了延迟加载机制之后, user对象属性均为null,此时并没有任何Hibernate并没有执行数据库查询操作。原因就在于Hibernate的代理机制。Hibernate中引入了CGLib作为代理机制实现的基础。CGLib可以在运行期动态生成Java Class。这里的代理机制,其基本实现原理就是通过由CGLib构造一个包含目标对象所有属性和方法的动态对象(相当于动态构造目标对象的一个子类)返回,并以之作为中介,为目标对象提供更多的特性。

从上面的内存快照可以看到,真正的TUser对象位于代理类的属性中。当我们调用user.getName方法时,调用的实际上是代理类的getName( )方法, 它会首先检查CGLIB$CALLBACK_0.target中是否存在目标对象。如果存在,则调用目标对象的getName方法返回,如果目标对象为空,则发起数据库查询指令,读取记录、构建目标对象并将其投入"代理类的.target"。

这样,用过一个中间代理,实现了数据延迟加载功能,只有当客户程序真正调用实体类的取值方法时,Hibernate才会执行数据库查询操作。

11. 集合类型的延迟加载
Hibernate延迟加载机制中,关于集合的延迟加载特性意义最为重大,也时实际应用中相当重要的一个环节。

回到开篇提到的一个例子:

如,之前示例中TUser对象在加载的时候,在非“延迟加载”的情况下,会同时读取其所关联的多个地址(adress)对象,对于确实需要对address进行操作的应用逻辑而言,关联数据的自动加载机制的确非常有效。

但是,如果我们只是想要获得user的年龄(age)属性,而不关心user的地址(address)信息,那么自动加载address的特性就显得多余,并且造成了极大得性能浪费。为了获得user的性别属性,我们可能还要同时从数据库中读取数条无用的地址数据,这导致了大量无谓的系统开销。

对于我们这里TUser对象的加载过程,如果要做到集合的延迟加载,也就意味着,加载TUser对象时只针对其本身的属性,而当我们需要获取TUser对象所关联的address信息时(如执行user.getAddresses时),才真正从数据库中加载address数据并返回。

我们将前面一对多关系中的lazy属性修改为true,即指定了关联对象采用延迟加载:

尝试执行以下代码:

运行时抛出异常:

如果我们稍做调整,将session.close放在代码末尾,则不会发生这样的问题。

这意味着,只有我们实际加载user关联的address时,Hibernate才试图通过session从数据库中加载实际的数据集,而由于我们读取address之前已经关闭了session,所以出现了以上的错误。

这里有个问题,如果我们采用了延迟加载机制,但希望在一些情况下,实现非延迟加载时的功能,也就是说,我们希望在Session关闭后,依然允许操作user的addresses属性。如,为了向View层提供数据,我们必须提供一个完整的User对象,包含其所关联的sddress信息,而这个User对象必须在Session关闭之后仍然可以使用。

Hibernate.initialize方法可以强制Hibernate立即加载关联对象集:

为了实现透明化的延迟加载机制,Hibernate进行了大量努力。其中包括JDK Collection接口的独立实现。

如果我们尝试用HashSet强制转化Hibernate返回的Set型对象:

就会在运行期得到一个java.lang.ClassCastException,实际上,此时返回的是一个Hibernate的特定Set实现“net.sf.hibernate.collection.Set”,而非传统意义上的JDK Set实现。

这也正是我们为什么在编写POJO时,必须用JDK Collection Interface (如Set,Map),而非特定的JDK Collection实现类(如HashSet、HashMap)的原因(如private Set addresses;而非private HashSet addresses)。

回到前面TUser类的定义:

我们通过Set接口,声明了一个addresses属性,并创建了一个HashSet作为addresses的初始实例,以便我们创建TUser实例后,就可以为其添加关联的addrsee对象:

此时,这里的addresses属性是一个HashSet对象,其中包含了一个address对象的引用。

前面的 “脏数据检查”部分中,我们讨论过针对无关联实体的保存。那么,在现在的情况下,当前调用session.save(user)时,Hibernate如何处理其关联的Address对象集?

通过Eclipse的Debug视图,我们可以看到session.save方法执行前后user对象发生的变化。

可以看到,user对象在通过Hibernate处理之后已经发生了变化。

首先,由于insert操作,Hibernate获得数据库产生的id值(在我们的例子中,采用native方式的主键生成机制),并填充到user对象的id属性。这个变化比较容易理解。

另一方面,Hibernate使用了自己的Collection实现“net.sf. hibernate.collection.Set”对user中的HashSet型addresses属性进行了替换,并用数据对其进行填充,保证新的addresses与原有的adresses包含同样的实体元素。

再来看下面的代码:

根据之前的讨论我们知道,当代码执行到(1)处时,addresses数据集尚未读入,我们得到的addSet对象实际上只是一个未包含任何数据的net.sf.hibernate.collection.Set实例。

代码运行至(2),真正的数据读取操作才开始执行。观察一下net..sf..hibernate.collection.Set.iterator方法我们可以看到:

直到此时,真正的数据加载(read( )方法)才开始执行。

Read方法将首先在缓存中查找是否有符合条件的数据索引。

注意这里数据索引的概念,Hibernate在对集合类型进行缓存时,分两部分保存,首先是这个集合中所有实体的id列表(也就是所谓的数据索引,对于这里的例子,数据索引中包含了所有userid=1的adress对象的id清单),其次是各个实体对象。

如果没有发现对应的数据索引,则执行一条Select SQL(对于本例就是select…fromt_address where user_id=?)获得所有符合条件的记录,接着构造实体对象和数据索引后返回。实体对象和数据索引也同时被分别纳入缓存。

另一方面,如果发现了对应的数据索引,则从这个数据索引中取出所有id列表,并根据id列表依次从缓存中查询对应的数据,则执行相应的Select SQL获得对应的address记录(对于本例就是select…from t_adress wahere id+?)。

这里引出了另外一个性能关注点,即关联对象的缓存策略。

如果我们为某个集合类设定了缓存,如:

注意这里的<cache usage= “read-only”/>只会使得Hibernate对数据索引进行缓存,也就是说,这里的配置实际上只是缓存了集合中的数据索引,而并不包括这个集合中的各个实体元素。

执行下面的代码:

观察屏幕日志输出:

看到,第二次获取关联的addresses集合的时候,执行了3次Select SQL。

正是由于<set…><cache usage= “read-only”/>…</set>的设定,第一次addresses集合被加载之后,数据索引已经被放入缓存。

第二次再加载addresses集合的时候,Hibernate再缓存中发现了这个数据索引,于是从索引里面取出当前所有的id(此时数据库中有3条符合的记录,所以共获得3个id),r然后依次根据这3个id在缓存中查找对应的实体对象,但是没有找到,于是发起了数据库查询,由Select SQL根据id从t_address表中读取记录。

我们看到,由于缓存中数据索引的存在,似乎SQL执行的次数更多了,这导致第二次借助缓存的数据查询比第一次性能开销更大。

导致这个问题出现的原因何在?

这是由于我们至为集合类型配置了缓存,这样Hibernate只会缓存数据索引,而不会将集合中的实体元素同时也纳入缓存。

我们必须为集合类型中的实体对象也指定缓存策略,如:

此时,Hibernate才会对集合中的实体也进行缓存。

再次运行之前的代码,得到以下日志输出:

可以看到,第二次查询没有执行任何SQL即宣告完成,所有的数据都来自缓存,这无意对性能的提升有着及其重要的意义。

上面我们探讨了 net.sf. hibernate.collection.Set.iterate方法,同样,观察 net.sf.hibernate.collection.Set.size/isEmpty方法或者其他hibernate collection中的同类型方法实现,我们可以看到同样的处理方式。

通过自定义Collection类型实现数据延迟加载的原理也就再于此。

这样,通过自定义Collection实现,Hibernate就可以在Collection层从容的实现延迟加载特性。只有程序真正读取这个Collection的内容时,才激发底层数据库操作,这为系统的性能提供了更加灵活的调整手段。

12. 属性的延迟加载
在前面的内容中,我们讨论了关于实体,及其关联集合对象的延迟加载机制。这些机制为改进持久层性能提供了一个重要渠道。

根据我们已有的经验来看,上面这两种延迟加载模式,实质上都是面向数据实体。我们可以决定是否即刻加载某个实体,或者某个实体集合。

如果需要对实体的某个部分(如某个属性)应用延迟加载策略,我们应如何入手?在基础篇中,我们曾经探讨了有关实体粒度设计的主题。通过对同一库表建立不同粒度的实体映射关系,我们可以变通的实现库表的部分加载,不过,这并非我们这里所说的延迟加载,另一方面,这样需要付出大量的额外工作。

另外,我们也可以在HQL中通过Select子句限定加载的属性列表。不过,随之而来HQL语句的琐碎语法实在令人厌倦。

在Hibernate 2中,为了避免实体加载可能带来的性能浪费,我们只能采取以上两种策略。Hibernate团队显然也意识到了这个问题, Hibernate3中通过property节点的lazy属性可以为特定属性指定延迟加载.但还需要借助类增强器对二进制class文件进行强化处理.(buildtime bytecode instrumentation).

数据保存
13. Session.save方法
接受一个实体对象,执行时:

1.在Session内部缓存中寻找对象, 如果数据已经保存(Persistent状态), 则直接返回.即使状态已经变化, 也将在脏数据检查中判定, 并执行相应update操作.
2.如果实体类实现了lifecyle接口, 则调用待保存对象的onSave方法.
3.如果实体类实现了Validatable接口, 则调用其validate()方法.
4.如果有, 调用拦截器(Interceptor)的onSave方法.
5.构造Insert SQL并执行. 可能会设置对象的id值.
6.将对象放入内部缓存. (不纳入二级缓存, 因为Hibernate认为在事务的剩余部分被修改的几率很高).
7.如果存在级连关系, 则递归处理.
14. Session.update方法
可以把对象从Detached状态转换为Persisitent状态.

1.根据实体对象的id在Session缓存中查找, 如果发现, 则认为对象已经处于Persistent状态, 直接返回.(说明Persistent状态的数据调用update不起作用.)
2.初始化对象的状态信息, 并纳入缓存.  本身并不发送Update SQL完成更新操作, 但是Session.flush方法将执行Update SQL.(Transaction.commit在提交数据库事务之前调用了Session.flush)
15. Session.saveOrUpdate方法
执行步骤:

1.在Session缓存中查找, 找到就直接返回.
2.如果有, 执行实体类的Interceptor.isUnsave(), 判断对象是否为未保存.
3.未保存(Transient), 则调用save方法保存.
4.如果已经保存(Detached), 则调用update时对象与Session关联.
数据批量操作
16. 数据批量操作--保存
保存的数据将纳入缓存.由于二级缓存可以配置最大容量, 但内部缓存却没有容量限制, 所以批量操作中, 应该考虑到控制内部缓存的过度增长而出现OutOfMemeoryError错误.

可以在数据保存过程中周期性的对Session调用flush和clear方法, 确保Session的容量不至于太大.

也可以设置hibernate.jdbc.batch_size参数, 指到批量操作中每次提交SQL的数量. (MySql Driver不支持BatchUpdate).

17. 数据批量操作--删除--hibernate2
轻量级的ORM都面临一个问题, 数据库的改变需要在缓存中同步. 所以表面上的一个HQL删除, 往往执行的是1+N次操作. 第一次是查询, 第二次是逐条删除.

由此的问题:

1.内存消耗.如果数据量过大, find方法很容易导致OutOfMemeoryError错误. 可以考虑使用iterate方法逐条获取数据在执行删除. Hibernate2.16以后的版本, 提供了基于游标的数据遍历操作(前提是JDBC驱动必须支持游标), 通过游标, 我们可以逐条获取数据, 使得内存比较稳定. Query的scroll方法得到ScrollableResults.
2.执行效率. 同样可以调整hibernate.jdbc.batch_size参数.
18. 数据批量操作--删除--hibernate3
引入了bulk delete/update操作, 通过一条独立的SQL完成对数据的批量删除/更新. 方法是, 使用hsql创建Query, 再调用Query的executeUpdate()方法.

但是无法解决缓存同步问题(包括一级缓存和二级缓存):

分享到:
评论

相关推荐

    47-二级缓存 Hibernate-Shiro-MyBatis

    在IT行业中,二级缓存是一种优化数据库访问性能的重要技术,特别是在使用ORM框架(如Hibernate)、权限管理框架(如Shiro)以及SQL映射框架(如MyBatis)时。二级缓存可以存储经常访问的数据,避免频繁地从数据库中...

    hibernate二级缓存

    基于给定的文件信息,我将从 Hibernate 二级缓存的角度生成相关知识点。 标题:Hibernate 二级缓存 描述:Hibernate 缓存机制是介于应用程序和物理数据源之间,用于降低应用程序对物理数据源访问的频次,从而提高...

    深入理解Hibernate缓存

    ### 深入理解Hibernate缓存 #### 一、Hibernate缓存概述 Hibernate作为一款流行的Java持久层框架,为开发者提供了高效且灵活的数据访问能力。其中,缓存机制是Hibernate性能优化的重要组成部分之一。理解Hibernate...

    HIbernate缓存

    ### Hibernate缓存详解 #### 一、概述 Hibernate作为一款流行的Java持久层框架,提供了多种方式来优化数据访问性能,其中缓存机制是一项关键的技术。本文将深入探讨Hibernate的缓存机制,特别是针对二级缓存的配置...

    hibernate一级缓存和二级缓存的区别与联系

    Hibernate 是一个流行的对象关系映射(ORM)框架,它提供了数据缓存机制以优化数据库访问性能。缓存机制分为一级缓存和二级缓存,两者都有助于减少对物理数据库的直接访问,从而提高应用程序的运行效率。 一级缓存...

    Hibernate二级缓存

    Hibernate二级缓存是一种提高应用程序性能的技术,它将数据存储在SessionFactory级别的缓存中,使得数据可以在不同的Session之间共享。这与一级缓存(Session级别)不同,一级缓存仅存在于单个Session生命周期内,当...

    Hibernate一级缓存、二级缓存以及查询缓存实例

    在Java的持久化框架Hibernate中,缓存机制是优化数据库操作性能的重要手段。本文将深入探讨Hibernate的一级缓存、二级缓存以及查询缓存,通过具体的实例来阐述它们的工作原理和使用方法。 首先,我们从一级缓存开始...

    hibernate一级缓存、二级缓存和查询缓存

    **hibernate一级缓存、二级缓存和查询缓存** 在Java的持久化框架Hibernate中,缓存机制是提高应用程序性能的关键要素。缓存能够减少数据库的访问次数,提高数据读取速度,并且在一定程度上降低了系统的负载。本文将...

    hibernate基础教程

    Hibernate中的缓存 Hibernate中使用了一级缓存和二级缓存的机制来提高程序的性能. 一 为什么要使用缓存? 缓存是一块存储区域,可能是一块内存,也可能是一块硬盘.缓存...

    hibernate一级和二级缓存配置与详解

    一级缓存是Hibernate默认提供的缓存,它是Session级别的,每个Hibernate Session都有一个私有的、本地的一级缓存。当我们在Session中对对象进行 CRUD(创建、读取、更新、删除)操作时,这些对象会被自动放入一级...

    Hibernate缓存技术研究

    ### Hibernate缓存技术研究 #### 一、引言 Hibernate是一种强大的对象-关系映射(Object-Relational Mapping,简称ORM)工具,主要用于Java环境下的应用程序。它能够将应用程序中的对象模型映射到关系型数据库的表...

    springboot整合Ehcache组件,轻量级缓存管理

    EhCache是一个纯Java的进程内缓存框架,具有快速、上手简单等特点,是Hibernate中默认的缓存提供方。 2、Hibernate缓存 Hibernate三级缓存机制简介: 一级缓存:基于Session级别分配一块缓存空间,缓存访问的对象...

    Hibernate缓存,性能优化

    在IT领域,尤其是在Java开发中,Hibernate作为一款流行的开源对象关系映射(ORM)框架,其缓存机制是实现高性能数据库交互的关键技术之一。本文将深入探讨Hibernate缓存的原理、类型及其对性能优化的影响。 ### ...

    Hibernate缓存.doc

    Hibernate缓存.docHibernate缓存.doc

    Spring集成的Hibernate配置二级缓存

    以EhCache为例,我们需要在项目中引入ehcache-core或ehcache的依赖,并在Hibernate配置文件(hibernate.cfg.xml或persistence.xml)中启用二级缓存,添加如下配置: ```xml &lt;property name="hibernate.cache.use_...

    hibernate5.1二级缓存包

    二级缓存是相对于一级缓存(Session 缓存)而言的,一级缓存是每个 Hibernate Session 内部的缓存,而二级缓存则是在 SessionFactory 级别的,可以被多个 Session 共享。它能够存储对象的状态,避免频繁的数据库交互...

    Hibernate缓存深入详解

    【Hibernate缓存深入详解】 在Java的持久化框架Hibernate中,缓存机制是提升系统性能的关键因素。它位于Hibernate应用和数据库之间,减少了对数据库的直接访问,从而提高了应用程序的运行速度。缓存中存储的是...

    Hibernate中大量数据的更新

    Hibernate 的一级缓存是指 Session 对象中缓存的所有对象。在批量更新时,如果不及时清除一级缓存,可能会导致内存溢出异常。因此,在批量更新时,需要定期调用 `session.flush()` 和 `session.clear()` 来清除一级...

    hibernate 二级缓存详解

    Hibernate 二级缓存是针对SessionFactory级别的全局缓存,与一级缓存(Session级别)不同,一级缓存只在单个Session生命周期内有效。二级缓存则允许不同Session之间共享数据,提高了数据访问效率,减少了对数据库的...

    hibernate缓存机制

    Hibernate缓存机制是提高应用程序性能的关键技术之一,它通过存储数据副本减少对物理数据库的访问。缓存可以分为两层:第一级缓存和第二级缓存。 **第一级缓存**是内置在Session中的,它是不可卸载的,也称为...

Global site tag (gtag.js) - Google Analytics