javascript学习指南_java Hibernate框架Session的保存、更新、删除和查询教程

更新时间:2019-12-26    来源:js教程    手机版     字体:

【www.bbyears.com--js教程】

session的save方法

使一个临时对象变为持久化对象。
session的save方法完成以下操作:
1、把customer对象加入到缓存中,使他变为持久化对象
2、选用映射文件指定的标识符生成器为持久化对象分配唯一的OID。Customer.hbm.xml文件中id元素的
子元素指定标识符生成器:



   


在配置文件配置了id自增后,在程序中在setId();是无效的。

3、计划执行一个insert语句,把customer对象当前的属性组装到insert语句中,save方法并不会立即执行sql语句。只有当清理缓存时才会执行insert语句。如果在save方法之后又修改了持久化对象的属性,这会使得Session在清理缓存的时候额外执行Sql 的update语句。以下两段代码尽管都能完成相同功能,第一段代码仅执行了一条insert语句,而右边执行了一条insert语句和一条update语句。第一段代码减少了数据库访问次数,具有更好的性能。

Customer customer = new Customer();
// 先设置customer属性再保存
customer.setName("Tom");
session.save(customer);
tx.commit();

Customer customer = new Customer();
session.save(customer);
// 先保存再设置customer属性
customer.setName("Tom");
tx.commit();

Hibernate通过持久化对象的OID来维持他和数据库相关记录的对应关系。当Customer对象处于持久化状态时,不允许随意修改他的OID。

session的update方法

使一个游离对象转变为持久化对象
session的update方法完成以下操作:
1、把Customer对象重新加入到Session缓存中,使他变为持久化对象
2、计划执行一个update语句。值得注意的是session只在清理缓存的时候执行update
只要通过update方法使游离对象被一个Session关联,即使没有修改Customer对象的任何属性,Session在清理缓存时也会执行由update方法计划的update语句。
如果希望session仅仅当修改了Customer对象的属性时才执行update语句,可以把映射文件中class元素的select-before-update设为true,默认为false;



当update方法关联一个游离对象时,如果在session缓存中已经存在相同的OID的持久化对象,会抛出异常。

session的saveOrUpdate方法

saveOrUpdate方法同时包含了save与update方法,如果传入的参数是临时对象,就调用save方法,如果传入的是游离对象就调用update方法,如果传入的是持久化对象就直接返回。那么Hibernate如何判断对象的状态呢?
下面看看临时对象的判断条件:
1、Java对象的OID取值为null
2、Java对象具有version属性并且取值为null
3、在映射文件中为id元素设置了unsaved-value属性,并且OID取值与unsaved-value属性值匹配
4、在映射文件中为version属性设置了unsaved-value属性,并且OID取值与unsaved-value属性值匹配
5、自定义了Hibernate的Interceptor实现类,并且Interceptor的isUnsaved方法返回true

session的load和get方法

Session的load和get方法都能根据OID从数据库中加载一个持久化对象,区别在于:当数据库中没有与之对应的记录时load方法会抛出ObjectNotFoundException异常,get方法返回null。
get方法会在调用之后立即向数据库发出sql语句(不考虑缓存的情况下),返回持久化对象;而load方法会在调用后返回一个代理对象,该代理对象只保存了实体对象的id,直到使用对象的非主键属性时才会发出sql语句

Session的delete方法

delete方法用于从数据库中删除与Java对象对应的记录,如果传入的参数是持久化对象,session就计划执行一个delete语句,如果传入的是游离对象,先使游离对象被session关联,使他变为持久化对象,然后计划执行一个delete语句。Session只有在清理缓存的时候才会执行delete语句,只有调用session.close才会从session缓存中删除对象。


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


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


1.   Session---单数据加载---load/ get

Load方法根据指定的实体类和id从数据库装载认为存在的一条记录. 应该确保对象确实存在, 否则会抛出ObjectNotFoundException.

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

 

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

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

 
    出于性能考虑,避免无谓的数据库访问,Session在调用数据库查询功能之前,会先在缓存中进行查询。首先在第一级缓存中,通过实体类型和id进行查找,如果第一级缓存查找命中,且数据状态合法,则直接返回。
    之后,Session会在当前 “NonExists”记录中进行查找,如果 “NonExists”记录中存在同样的查询条件,则返回null。“NonExists”记录了当前 Session实例在之前所有查询操作中,未能查询到有效数据的查询条件。如果Session中一个无效的查询条件重复出现,即可迅速做出判断。
    对于load方法而言,如果内部缓存中没有发现有效数据,则查询第二级缓存,如果第二级缓存命中,则返回。
    如果在缓存中未发现有效数据,则发起数据库查询操作(Select SQL).
    如果经过查询未发现对应记录,则将此次查询的信息在 “NonExists”中加以记录,并返回null。
    否则根据映射配置和Select SQL得到的 ResultSet,创建对应的数据对象. 并将其数据对象纳入当前Session实体管理容器(一级缓存)。
    执行Interceptor.onLoad方法(如果有对应的Intercepeor)。
    将数据对象纳入二级缓存。
    如果数据对象实现了LifeCycle接口,则调用数据对象的onLoad方法。
    返回数据对象。

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 itor =(Iterator)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 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 datas = hibernate_session.createQuery(" From Tuser").list();

       for(int i=0;i
           data = datas.get(i);

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

       }

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

    Iterator 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 datas = hibernate_session.createQuery(" From Tuser").list();

       for(int i=0;i
           data = datas.get(i);

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

       }

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

    Iterator 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
           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 datas = hibernate_session.createQuery(" From Tuser").list();

       for(int i=0;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 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
           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 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 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 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 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 datas = hibernate_session.createQuery(hsql).list();

        end =System.currentTimeMillis();

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

       for(int i=0;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 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 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)+"
");

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只在特定的情况下产生作用:

    完全相同的Select SQL重复执行。
    再两次查询之间,此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完成, 后者由数据库完成.

    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排序.
    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+?)。

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

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

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

执行下面的代码:

观察屏幕日志输出:

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

正是由于…的设定,第一次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方法

接受一个实体对象,执行时:

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

14. Session.update方法

可以把对象从Detached状态转换为Persisitent状态.

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

15. Session.saveOrUpdate方法

执行步骤:

    在Session缓存中查找, 找到就直接返回.
    如果有, 执行实体类的Interceptor.isUnsave(), 判断对象是否为未保存.
    未保存(Transient), 则调用save方法保存.
    如果已经保存(Detached), 则调用update时对象与Session关联.

数据批量操作


16. 数据批量操作--保存

保存的数据将纳入缓存.由于二级缓存可以配置最大容量, 但内部缓存却没有容量限制, 所以批量操作中, 应该考虑到控制内部缓存的过度增长而出现OutOfMemeoryError错误.

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

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


17. 数据批量操作--删除--hibernate2

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

由此的问题:

    内存消耗.如果数据量过大, find方法很容易导致OutOfMemeoryError错误. 可以考虑使用iterate方法逐条获取数据在执行删除. Hibernate2.16以后的版本, 提供了基于游标的数据遍历操作(前提是JDBC驱动必须支持游标), 通过游标, 我们可以逐条获取数据, 使得内存比较稳定. Query的scroll方法得到ScrollableResults.
    执行效率. 同样可以调整hibernate.jdbc.batch_size参数.

18. 数据批量操作--删除--hibernate3

引入了bulk delete/update操作, 通过一条独立的SQL完成对数据的批量删除/更新. 方法是, 使用hsql创建Query, 再调用Query的executeUpdate()方法.

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

本文来源:http://www.bbyears.com/wangyezhizuo/83846.html

热门标签

更多>>

本类排行