zoukankan      html  css  js  c++  java
  • 五、hibernate的Session操作, 查询过滤, 缓存利用, 批量处理

    一.                                                         Session操作, 查询过滤, 缓存利用, 批量处理

    数据查询,装载

    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()方法.

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

  • 相关阅读:
    Jmeter之写入测试结果到Excel文件
    jmeter之BeanShell 断言
    jmeter-csv数据文件设置之线程共享模式
    LoadRunner生成随机位数的字符串
    linux命令操作
    LoadRunner函数
    vi常用操作
    LoadRunner常见问题
    面试题库
    jmeter持续集成化(二)---jenkins+ant+jmeter
  • 原文地址:https://www.cnblogs.com/linzheng/p/1924854.html
Copyright © 2011-2022 走看看