hibernate的问题

各位老大,我测了一下hibernate和直接用jdbc取mysql的数据

目的是:从表的10000条记录处取100条,看时间

1.用jdbc取数据
Class.forName("org.gjt.mm.mysql.Driver");
String url="jdbc:mysql://10.0.18.93/test";
String user = "root";
String password = "hello";
Connection conn = DriverManager.getConnection(url, user, password);
Statement stmt = conn.createStatement();
long begintime=System.currentTimeMillis();
ResultSet rs = stmt.executeQuery("select * from post limit 10000,100");
long endtime=System.currentTimeMillis();
System.out.println("query cost time="+(endtime-begintime));

结果:query cost time=235

2.用hibernate
sql = "select post from my.test.Post as post ";
long begintime = System.currentTimeMillis();
System.out.println("Begin time is " + begintime);
query = session.createQuery(sql);
query.setFirstResult(10000);
query.setMaxResults(100);
List catList = query.list();
long middletime = System.currentTimeMillis();
System.out.println("Query cost time is " +
(middletime - begintime));

结果:Query cost time is 8391


使用hibernate的全部代码


public class HibernateMain {
private static SessionFactory sessionFactory;
private static Session session;
private static Transaction transaction;

public static void main(String[] args) {
HibernateMain hm = new HibernateMain();

try {
hm.initHibernate();
session = sessionFactory.openSession();
//取出贴子条数
String sql = "select count(*) from com.alibaba.bizclub.dbo.Post as post ";
Query query = session.createQuery(sql);
Integer count = (Integer) query.iterate().next();
//取出贴子内容
sql = "select post from my.test.Post as post ";
long begintime = System.currentTimeMillis();
System.out.println("Begin time is " + begintime);
query = session.createQuery(sql);
query.setFirstResult(10000);
query.setMaxResults(100);
List catList = query.list();
long middletime = System.currentTimeMillis();
System.out.println("Query cost time is " +
(middletime - begintime));

for (Iterator it = catList.iterator(); it.hasNext();) {
Post post = (Post) it.next();
}

long endtime = System.currentTimeMillis();
System.out.println("End cost time is " + (endtime - begintime));
} catch (HibernateException ee) {
ee.printStackTrace();
}

}

private void initHibernate() throws HibernateException {
sessionFactory = new Configuration().configure().buildSessionFactory();
}
}

把这一句放到后面去或注释掉会更准确 System.out.println("Begin time is " + begintime);

因为 System IO 也要花时间。

1 测试方法太粗糙

2 ResultSet rs = stmt.executeQuery("select * from post limit 10000,100");
这并没有实际取出数据

stmt.executeQuery("select * from post limit 10000,100");

能取到数据的

我看过了这里另一个关于性能的贴子,有两点
1.mysql的jdbc驱动有问题,所以极快?

2.hibernate是不是默认不打开jcs的,所以性能一般,如何打开jcs?

stmt.executeQuery()只是返回一个ResultSet,
你需要遍历这个ResultSet并取出每个字段。

jcs看看hibernate的文档

To: Chinahero

你的测试例子写的不对,所以这样的测试出来的结果有极大的误导性。

对了,如果你对自己的测试有信心的话,证实了JDBC的速度远远超过Hibernate,可以给Gavin King发信,赢取100美元的奖金。

我就是觉的奇怪啊,为什么会这样,我错在哪里?

我想这样测一下,肯定是有问题的,能不能说一下我的问题

我的目的是从mysql的一个post表中取10000-10100条记录

问题在哪呀

虚心请假各位老大

你需要遍历ResultSet并取出每个字段。

多次测试取平均,偏差太大的测试要剔除...
还有很多技巧,我也不是很清楚

比较准确地用statement的方法如下,有兴趣你可以试一试
以oracle数据库为例

Statement stmt = conn.createStatement();
long begintime=System.currentTimeMillis();
ResultSet rs = stmt.executeQuery("select * from post where rownum < 10000");
Post post = null;
ArrayList list= new ArrayList ();
int i =0;
while(rs.next && i<100){
post = new Post();
post.set...(rs.get...());
post.set...(rs.get...());
list.add(post);
i++;
}
long endtime=System.currentTimeMillis();
System.out.println("query cost time="+(endtime-begintime));


不知道my-sql有没有rownum这种伪列
还有一点就是JDBC并不是在rs.get时才取数据的,而是在stmt.executeQuery()取的,当让,大部分JDBC的API可以通过
stmt.setPreFetch...()来设定stmt.executeQuery()第一次获得的纪录条数。这个stmt不是java.sql.Statement接口,而是具体的实现类。


>还有一点就是JDBC并不是在rs.get时才取数据的,而是在stmt.executeQuery()取的<

我也不是很清楚在stmt.executeQuery()时是否会取出数据,
但至少不会取出所有数据(当rows>fetchSize时)

你的测试里面至少有3个明显的错误:

一、生成的SQL语句都不同

JDBC用的sql是MySQL数据库专用的sql语句,是在数据库里面分页,驱动只从数据库里面取100条记录出来;而Hibernate的sql是通用sql,你看看我前面一个Hibernate性能测试的帖子,分析过MySQL驱动的缺陷问题,在Hibernate List方式下,MySQL驱动会把10100条记录全部取出。简单的心算一下,两者就有101倍的速度差距了,怎么比阿?

要做性能对比,最起码的一点是两者产生的sql语句必须一样,否则还比什么?表面上在做JDBC vs Hibernate的测试,实际上在做 两个不同SQL语句之间的性能测试。

Hibernate是一个通用持久层框架,它不可能把MySQL专用的sql语句包含进来,Hibernate2.1beta版的HQL引入了native sql,不知道能不能在HQL里面用MySQL的专用limit分页,你可以试试。

这是造成测试结果差异的主要原因,当然还有其它的错误。

二、结果集遍历速度

你的测试只测了从数据库取结果集的速度,而没有测试结果集的遍历速度。看看我前面那个帖子,详细分析了这个问题。JDBC的结果集的获取由于驱动的缺陷,要比Hibernate List快一个以上的数量级,但是JDBC结果集的遍历速度可比Hibernate List方式差远了。 你可以分别测试一下结果集获取速度和结果集遍历速度,我前面曾经测试过, Hibernate List方式下的结果集遍历,30万条记录的遍历速度还不到100ms,而JDBC遍历30万条记录的结果集需要10几秒,两者相差何止两个数量级?

三、 load Class的IO问题

如果用Java主程序来测试,那么必须连续测试多次,抛弃第一次结果,后面的结果取平均。为什么要连续测试?为什么要抛弃第一次结果?

因为Java load Class是一种按需加载,不到必须使用的时候,不会去load class,当你启动测试程序,JVM被启动,依次加载用到的Class,这包括MySQL的JDBC驱动,包括rt.jar,以及其它用到的Class。

在JDBC测试的两个计时过程中,当第一次测试被运行的时候,有关Statement, ResultSet等等一些Class被加载到JVM的内存里面去,这个加载需要时间,如果被加载的Class预先没有被读入内存,甚至需要硬盘IO过程。

而Hibernate用到的类库,比JDBC多了差不多5MB左右,那意味着第一次测试过程中,Hibernate要多load 5MB的数据到内存,不计算Class的验证过程,光是5MB的数据的硬盘IO,你算算要多多少时间?

所以要连续测试,第二次测试的时候,所有的Class已经被load完毕,就排除了load Class带来的测试偏差。


同意robbin

>它不可能把MySQL专用的sql语句包含进来,Hibernate2.1beta版的HQL引入了native sql<

只要在hibernate.properties中设置了
hibernate.dialect net.sf.hibernate.dialect.MySQLDialect
分页时就会采用limit

native是指session.createSQLQuery(queryString)时在queryString中
采用limit,rownum之类的native sql

hibernate 的list()方法执行后,返回的是数据对象Java Object,而你的JDBC返回的是ResultSet。

你应该在JDBC测试程序后加上Java Object的组装。再测试看看,估计new对象时要花费一些时间。