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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值