Hibernate性能问题解决- -
古怪的Hibernate性能问题:
从Session.find方法开始一路跟踪,很快就发现AbstractEntityPersister.setPropertyValues方法耗用了最多的时间。该方法代码如下:
public void setPropertyValues(Object object, Object[] values) throws HibernateException {
try{
if (optimizer!=null) {
optimizer.setPropertyValues(object, values); // 1
return;
}
}
catch (Throwable t) {
throw new PropertyAccessException(
t,
ReflectHelper.PROPERTY_ACCESS_EXCEPTION,
true,
mappedClass,
ReflectHelper.getPropertyName(t, optimizer)
);
}
for (int j=0; j<getHydrateSpan(); j++) getSetters()[j].set(object, values[j]); // 2
}
其中“2”的地方效率相当低,给一个实体对象的25个属性赋值大约需要半秒到一秒。把这里的循环拆开跟踪,发现因为password属性的getter/setter需要对密码加密/解密,并且每一次加密/解密时都重新初始化加密器(Cipher对象),造成了巨大的损耗。把初始化代码抽取出来,再添加一些缓存,性能提升一倍左右,但仍然很慢,比在Sun JDK下运行慢一个数量级左右。
再观察之下,发现某些对象从数据库中取出时执行了“1”的代码,而效率低下的部分都执行到了“2”。也就是说,
Party类的persister没有得到optimizer,所以只能通过反射给属性赋值。之所以在Sun JDK下执行仍然效率不减,估计是因为Sun JDK的反射机制做得比较好,IBM的做得比较烂。再观察Hibernate输出的日志,发现有一句说“
Party的
setXxxx方法是
private,所以将optimizer禁用”。于是将这个方法改为
public,再运行,速度飞快。问题圆满解决。最后解决问题的过程看起来就像是我把一个不相干的
private方法改成
public,然后效率就突然提升,不明就里的人恐怕会觉得有点神奇。

最后,有一个问题:在我看来,如果要给一个实体的属性设上值,唯一的办法就是用反射,通过Method对象去调用。但optimizer显然有另一种高效得多的实现,值得去研究一下。