一尘不染

Hibernate可以用于对性能敏感的应用程序吗?

hibernate

我在检索与其他对象有许多关系的对象的多个实例时遇到性能问题。我正在将Spring和Hibernate的JPA实现与MySQL配合使用。问题是执行JPA查询时,Hibernate不会自动联接到其他表。这将导致n * r + 1个SQL查询,其中n是要检索的对象数,r是关系数。

例如,某人住在某个地址,有很多爱好,并且访问过许多国家:

@Entity
public class Person {
    @Id public Integer personId;    
    public String name;    
    @ManyToOne public Address address;    
    @ManyToMany public Set<Hobby> hobbies;    
    @ManyToMany public Set<Country> countriesVisited;
}

当我执行JPA查询以获取所有名为Bob的个人时,数据库中有100个Bobs:

SELECT p FROM Person p WHERE p.name='Bob'

Hibernate将此转换为301 SQL查询:

SELECT ... FROM Person WHERE name='Bob'
SELECT ... FROM Address WHERE personId=1
SELECT ... FROM Address WHERE personId=2
...
SELECT ... FROM Hobby WHERE personId=1
SELECT ... FROM Hobby WHERE personId=2
...
SELECT ... FROM Country WHERE personId=1
SELECT ... FROM Country WHERE personId=2
...

根据Hibernate
FAQ(此处此处),解决方案是在查询中指定LEFT
JOIN或LEFT OUTER
JOIN(用于多对多)
。所以现在我的查询看起来像:

SELECT p, a, h, c FROM Person p
LEFT JOIN p.address a LEFT OUTER JOIN p.hobbies h LEFT OUTER JOIN p.countriesVisited c
WHERE p.name = 'Bob'

这可以工作,但是如果有多个LEFT OUTER JOIN,则似乎存在错误,在这种情况下,Hibernate错误地寻找了不存在的列:

could not read column value from result set: personId69_2_; Column 'personId69_2_' not found.

该错误行为似乎由Hibernate
Core错误HHH-3636解决
。不幸的是,该修复程序不是任何已发布的Hibernate
JAR的一部分。我已经针对快照构建运行了我的应用程序,但是错误行为仍然存在。我还从存储库中的最新代码构建了自己的Hibernate Core
JAR,并且该错误行为仍然存在。因此,也许HHH-3636无法解决此问题。

Hibernate的性能限制非常令人沮丧。如果我查询1000个对象,则会对数据库进行1000 * r +
1个SQL查询。就我而言,我有8个关系,因此我得到8001个SQL查询,这导致了可怕的性能。Hibernate的官方解决方案是保留所有关系。但是由于错误的行为,对于多于许多的关系这是不可能的。因此,由于多对多关系,我对多对一关系和n * r + 1查询使用左联接。我计划将“ LEFT OUTER
JOIN”问题作为Hibernate错误提交,但与此同时,我的客户需要一个性能合理的应用程序。我目前使用批处理提取(BatchSize)的组合,ehcache和自定义内存缓存,但性能仍然很差(将30个秒内的5000个对象检索到8秒的性能得到了改善)。最重要的是,有太多的SQL查询正在访问数据库。

因此,我的问题是,可以在表之间具有多个关系的对性能敏感的应用程序中使用Hibernate吗?我很想听听Hibernate如何成功使用地址性能。我应该手写SQL(这在某种程度上违背了使用Hibernate的目的)吗?我应该对数据库架构进行非规范化以减少联接表的数量吗?如果我需要快速的查询性能,是否应该不使用Hibernate?有更快的东西吗?


阅读 201

收藏
2020-06-20

共1个答案

一尘不染

如果您阅读了链接到的所有常见问题解答,请参阅我对其他问题的回答:

遵循最佳做法指南!确保在Hibernate2中所有映射和映射都指定lazy =“ true”(这是Hibernate3中的新默认设置)。使用HQL
LEFT JOIN FETCH指定在初始SQL SELECT中需要检索的关联。

避免n + 1选择问题的第二种方法是在Hibernate3中使用 fetch =“ subselect”

如果仍然不确定,请参考Hibernate文档和Hibernate in Action。

请参阅有关提高性能的提示。如果对联接不小心,则会导致笛卡尔积问题。

2020-06-20