用hibernate的性能:插入很快,可查询为什么非常慢?????

我才开始用HIBERNATE,按照它的例子测试了一下性能,有点疑问!
我用的例子就是那个最最简单的cat的例子,只有一个cat的class,再没有其他
任何相关的类:
public class Cat {
private String id;
private String name;
private char sex;
private float weight;
......//get/set
}
然后我用hibernate操作这个cat类,写了一个插入DB的方法和查询的方法:
public void createCats(){
for(int i=0;i<1000;i++){
Cat princess = new Cat();
princess.setName("Princess"+i);
princess.setSex('F');
princess.setWeight(7.4f);
session.save(princess);
}
}
另一个查询的方法:
public void selectFemaleCats(){
String queryString = "select cat from Cat as cat where cat.sex = :sex";
Query query = session.createQuery(queryString);
query.setCharacter("sex", 'F');
for (Iterator it = query.iterate(); it.hasNext();) {
Cat cat = (Cat) it.next();
}
}
然后在另外一个方法里调用这两个方法:
public void test(){
long startTime=System.currentTimeMillis();
initHibernate();
System.out.println("-------------spare time 0:"+(System.currentTimeMillis()-startTime));

// Create some Cats
beginTransaction();
createCats();
endTransaction(true);

System.out.println("-------------spare time 1:"+(System.currentTimeMillis()-startTime));
// Select all Cats
beginTransaction();
selectFemaleCats();
endTransaction(false);
System.out.println("-------------spare time 2:"+(System.currentTimeMillis()-startTime));
}
其中,beginTransaction/endTransaction这两个方法很简单:
private void beginTransaction()
throws HibernateException {

session = sessionFactory.openSession();
transaction = session.beginTransaction();
}

private void endTransaction(boolean commit)
throws HibernateException {

if (commit) {
transaction.commit();
} else {
// Don't commit the transaction, can be faster for read-only operations
transaction.rollback();
}
session.close();
}
我运行这个如此简单的程序,看了一下它的效率:
[03-8-16 20:09:34:047 CST] 1cdb7652 SystemOut O -------------spare time 0:751
[03-8-16 20:09:39:345 CST] 1cdb7652 SystemOut O -------------spare time 1:6049
[03-8-16 20:10:25:752 CST] 1cdb7652 SystemOut O -------------spare time 2:52456
这个时间是我多次刷新页面以后一个大致稳定下来的时间,也就是说,
插入1000条数据,用HIBERNATE大致用6秒左右,查询大致在50秒左右!!!!!!!!
而我改成直接使用Statement(不是PreparedStatement),
插入1000条的时间一般是13秒,比用hibernate慢了一倍,但是,
查询的速度却保持在20秒左右,比用hibernate查询要快一倍多
请问,这是为什么呢?????????

iterate()当然慢了,它是先查询出id,然后每次next()的时候
再查询出数据。用list()试试!

我还是那句老话,不要随便做这种性能测试,没有严格的测试环境,精心设计的测试用例,是得到任何有意义的结论的。

你的这个测试例子老实说,编译都通不过!我很怀疑你怎么测试的。

 for(int i=0;i<1000;i++){
Cat princess = new Cat();

 for (Iterator it = query.iterate(); it.hasNext();) {
Cat cat = (Cat) it.next();
}

你自己看看,这样的对象重复定义的错误,你如何能够编译通过,更不要说运行了。


问一下Robbin,

我很想测一下用JDBC,Hibernate,CMP的性能,如CRUD, 但通过你的详尽贴子也知道有测试trap, 的确不是一个时间差就可以测的出来的,不过不知你了不了解Benchmarks等关于测试方面的,或者有什么好的测试工具推荐一下,我还是准备试比较合理的测一测它们的性能,先谢了。

光是测时间是远远不够的,像JVM的CPU占用率,JVM的内存堆栈都需要测量,进行综合评定,我没有什么测试工具,不过我知道那些大公司内部有自己开发的专用测试软件。

其实这样的专业性能测试,不要说测试软件本身了,就是测试结果的评测报告都是需要花很多银子购买的。像Java程序这种不是直接在OS平台上运行,涉及到JVM本身的很多性能调节的测试,非专业公司是不能够做的。自己随便写几个程序,跑一下,比较一下运行时间长短就去议论软件的性能是非常有害处的。

JDBC,Hibernate和CMP的性能问题其实不需要测试,如果你能够足够了解它们的运行的原理和细节,性能是昭然若揭的,何必还做什么测试呢?只有性能接近,运行原理一样的软件才需要进行性能测试。

JDBC如果精心编写,程序员本身是JDBC高手,那么性能一定是最好的,任何ORM都比不上,就像汇编高手用汇编写的程序一定比任何高级语言性能都好的道理一样;

Hibernate是JDBC的轻量级封装,准确来说是PreparedStatement的封装,性能有稍微的损失,但是由于大多数程序员用JDBC的水平达不到那么高,就像上面的测试程序中之所以create的测试JDBC要慢一倍的原因就在于程序员不知道应该用Batch,所以写出来的JDBC程序比不上Hibernate

CMP其实就不用多说了,客户端的一次业务方法调用要经过多次RMI调用,好几层代理才能达到Bean里面我们编写的业务代码,再加上繁重的容器方法调用,性能不在一个数量级上。

专业的测试,不是一两人可以做到的。

在这里,有谁可以对某个framework的整体性能做专业测试,设计出专业的测试环境和专业的测试用例?

每个人关心的角度是不一样的,一个人不可能对一个东西方方面面的性能都关心。如:楼主只是关心,在他那样处理时两种方案的运行时间的长短。

这样的测试至少对个人是有好处,对各种解决方案在自己的使用能力内能达到什么样的性能,自己有个谱先。所以没有所谓不专业就没有意义之说。

再,被别人仍版砖的时候,至少也能知道怎么才可以让它运行的更好。

to robbin

我贴这段程序的目的不是要质疑HIBERNATE的性能!!!!!!!!

因为我才开始用它,在学习它,写了这样一个查询,发现比
直接用STATEMENT慢的多,所以我觉得我用HIBERNATE肯定用的
不对,所以把代码贴出来,想问问大家,正确的方式是什么
也就是,我想知道,我怎样写查询才对。

yehs220
你好,你说:
iterate()当然慢了,它是先查询出id,然后每次next()的时候
再查询出数据。用list()试试!

能稍微详细点吗,我才开始学HIBERNATE。
能给出代码来吗?
应该用哪几句代码来替换我的程序

for (Iterator it = query.iterate(); it.hasNext();)
改为
for (Iterator it = query.list().iterate(); it.hasNext();)

看看hibernate api doc,Query.iterate()和Query.list()有什么区别。

>> 专业的测试,不是一两人可以做到的。

在这里,有谁可以对某个framework的整体性能做专业测试,设计出专业的测试环境和专业的测试用例?

每个人关心的角度是不一样的,一个人不可能对一个东西方方面面的性能都关心。如:楼主只是关心,在他那样处理时两种方案的运行时间的长短。

这样的测试至少对个人是有好处,对各种解决方案在自己的使用能力内能达到什么样的性能,自己有个谱先。所以没有所谓不专业就没有意义之说。

再,被别人仍版砖的时候,至少也能知道怎么才可以让它运行的更好。<<

性能测试受到的影响因素太多了,任何一个微小的因素的影响都会导致测试结果偏离真实性,这样的测试对个人有何好处而言?这样的测试能让你心里有个什么谱?

>>因为我才开始用它,在学习它,写了这样一个查询,发现比
直接用STATEMENT慢的多,所以我觉得我用HIBERNATE肯定用的
不对,所以把代码贴出来,想问问大家,正确的方式是什么
也就是,我想知道,我怎样写查询才对。<<

可是你贴的测试代码有编译错误阿,例子都运行不了,你让我怎么相信你的测试结果?而且你也没有把完整的测试过程详细的描述出来,比如说用JDBC时候的测试例子呢?用什么数据库,表有哪几个字段,表中准备的测试记录有多少?你的Hibernate的配置文件,初始化代码等等等等,每个地方都可能影响到测试的真实性。

初步判断,我同意yehas的意见,估计你的测试表中的记录数比较少,大概不会上千,那么在如此少的记录数的情况下,用iterator会表现出明显的速度劣势。因为它是每next1次,从数据库中取1条,而list是query的时候1次性把所有记录都取出来。但如果结果集非常庞大,比如说有几万条的话,用iterator的速度就应该比list快多了。

所以我认为你的测试数据量太少,样本数量明显不够,得不到真实的结果。

另外从你的测试代码来看,还有很多其它问题,每个问题都可能导致测试结果偏离真实性,比如说你光测试查询时间,但是并没有把结果集取出来,这样的过程也应该算在测试中;还有查询操作不需要启动Transaction,你启动了Transaction,速度会降低的,诸如此类。

》》》性能测试受到的影响因素太多了,任何一个微小的因素的影响都会导致测试结果偏离真实性,

呵呵,没有任何的实物可以是精确没有偏差的。任何的测试,其结果都是相对的。能发现自己的问题就是收获。

照这个思路,吃饭可能被噎死,那是不是就不要吃饭了?
走路可能天上掉个彗星把你砸死,那是不是就不要出门了?
不出门,呆在房里,还可能被基地开飞机撞死,那是不是大家直接都自杀算了?

to robbin
还有查询操作不需要启动Transaction,你启动了Transaction,速度会降低的

我曾经在hibernate forum的一个贴子中看到Gavin说:即使是查询也要放在一个事务中,理由有
1事务读并不比非事务读慢
2不会读到脏数据(当然要你的隔离级别足够高),而非事务读则可能

对于1我不是太相信,但2我觉得是对的。

你可以看看hibernate文档上的例子,所有读操作也放在事务中。

>>照这个思路,吃饭可能被噎死,那是不是就不要吃饭了?
走路可能天上掉个彗星把你砸死,那是不是就不要出门了?
不出门,呆在房里,还可能被基地开飞机撞死,那是不是大家直接都自杀算了?<<

你用不着偷换概念,难怪前面有人会说useless,full of attack and boast.

to robbin

我的代码绝对没问题,我贴的不完全,所以你觉得编译不通过
另外,我的这个cat表里有目前有56万条记录。
我可以把我的测试程序贴出来,如果你有时间,希望能编译运行一下:
============================================================
使用hibernate的程序
============================================================
Cat.java
/*
create table CAT (cat_id char(32) not null primary key,name varchar(16) not null,sex char(1),weight decimal(18,2))
*/
package hibernate.examples;

public class Cat {
private String id;
private String name;
private char sex;
private float weight;

public Cat() { }

public String getId() { return id; }
public void setId(String id) { this.id = id; }

public String getName() { return name; }
public void setName(String name) { this.name = name; }

public char getSex() { return sex; }
public void setSex(char sex) { this.sex = sex; }

public float getWeight() { return weight; }
public void setWeight(float weight) { this.weight = weight; }
}
============================Test.java======================
package hibernate.examples;

import net.sf.hibernate.*;
import net.sf.hibernate.cfg.*;

import java.util.*;

public class Test {
private SessionFactory sessionFactory;
private Session session;
private Transaction transaction;

public void runTest()
{

try {
// Initialize Hibernate (Configuration and SessionFactory)
long startTime=System.currentTimeMillis();
initHibernate();
System.out.println("-------------spare time 0:"+(System.currentTimeMillis()-startTime));

// Create some Cats
beginTransaction();
createCats();
endTransaction(true);

System.out.println("-------------spare time 1:"+(System.currentTimeMillis()-startTime));
// Select all Cats
beginTransaction();
selectAllCats();
endTransaction(false);

// Select female Cats
beginTransaction();
selectFemaleCats();
endTransaction(false);

System.out.println("-------------spare time 2:"+(System.currentTimeMillis()-startTime));
} catch (HibernateException e) {
e.printStackTrace(System.out);
}

}

public void createCats()
throws HibernateException {

//System.out.print("<h3>Creating Cats:</h3>");
//System.out.println("CREATING 'Princess'...<br/>");
for(int i=0;i<1000;i++){
Cat princess = new Cat();
princess.setName("Princess"+i);
princess.setSex('F');
princess.setWeight(7.4f);
session.save(princess);

//System.out.println("CREATING 'Max'...<br/>");
Cat max = new Cat();
max.setName("Max"+i);
max.setSex('M');
max.setWeight(8.1f);
session.save(max);

//System.out.println("CREATING 'Sophie'...<br/>");
Cat sophie = new Cat();
sophie.setName("Sophie"+i);
sophie.setSex('F');
sophie.setWeight(4.1f);
session.save(sophie);
}
}

public void selectAllCats()
throws HibernateException {

//System.out.print("<h3>Retrieving all Cats:</h3>");
String queryString = "select cat from Cat as cat";
Query query = session.createQuery(queryString);
for (Iterator it = query.iterate(); it.hasNext();) {
Cat cat = (Cat) it.next();
//System.out.println("CAT: " + cat.getName() + " (" + cat.getSex() + ", " + cat.getWeight() + ")<br/>");
}
}

public void selectFemaleCats()
throws HibernateException {

//System.out.print("<h3>Retrieving female Cats:</h3>");
String queryString = "select cat from Cat as cat where cat.sex = :sex";
Query query = session.createQuery(queryString);
query.setCharacter("sex", 'F');
for (Iterator it = query.iterate(); it.hasNext();) {
Cat cat = (Cat) it.next();
//System.out.println("CAT: " + cat.getName() + " (" + cat.getSex() + ", " + cat.getWeight() + ")<br/>");
}
}

// Helper Methods
private void initHibernate()
throws HibernateException {

// Load Configuration and build SessionFactory
sessionFactory = new Configuration().configure().buildSessionFactory();
}

private void beginTransaction()
throws HibernateException {

session = sessionFactory.openSession();
transaction = session.beginTransaction();
}

private void endTransaction(boolean commit)
throws HibernateException {

if (commit) {
transaction.commit();
} else {
// Don't commit the transaction, can be faster for read-only operations
transaction.rollback();
}
session.close();
}
}
=============================================================
test.jsp。我用这个jsp文件调用Test.java来做测试
=============================================================
<HTML>
<HEAD>
<%@ page
language="java"
contentType="text/html; charset=GB2312"
pageEncoding="GB2312"
import="java.io.*,hibernate.examples.*,oldconn.*"
%>
<META http-equiv="Content-Type" content="text/html; charset=GB2312">
<META name="GENERATOR" content="IBM WebSphere Studio">
<TITLE>test.jsp</TITLE>
</HEAD>
<BODY>
<P>Place test.jsp's content here.</P>
<%
Test obj=new Test();
obj.runTest();
%>
</BODY>
</HTML>
===========================================================
我的测试环境是:
PIII 1G / 512M 内存的一个笔记本电脑
Websphere Studio Application Developerment V5.0.1(WSAD5)
DB2 V7.2
数据库连接池我用的是WEBSPHERE的,通过JNDI访问的。