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

我发现检索多个与其他对象有很多关系的对象实例时出现性能问题。 我在MySQL中使用Spring和Hibernate的JPA实现。 问题是,在执行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 hobbies; @ManyToMany public Set 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 bug HHH-3636似乎可以解决bug行为。 不幸的是,修复程序不是任何已发布的Hibernate JAR的一部分。 我已经针对快照构建运行了我的应用程序,但bug行为仍然存在。 我还从存储库中的最新代码构建了自己的Hibernate Core JAR,并且仍然存在bug行为。 所以也许HHH-3636没有解决这个问题。

这种Hibernate性能限制非常令人沮丧。 如果我查询1000个对象,则对数据库进行1000 * r + 1个SQL查询。 在我的情况下,我有8个关系,所以我得到8001 SQL查询,这导致可怕的性能。 官方的Hibernate解决方案是将所有关系加入。 但由于错误行为导致多个多对多关系不可能实现这一点。 因此,由于多对多关系,我一直坚持使用左连接进行多对一关系和n * r + 1查询。 我计划将LEFT OUTER JOIN问题作为Hibernate错误提交,但与此同时我的客户需要一个具有合理性能的应用程序。 我目前使用批量提取(BatchSize),ehcache和自定义内存缓存的组合,但性能仍然很差(它改进了从30秒到8秒检索5000个对象)。 底线是太多的SQL查询正在访问数据库。

那么,我的问题是,是否可以在性能敏感的应用程序中使用Hibernate,其中表之间存在多种关系? 我很想知道Hibernate如何成功使用地址性能。 我应该亲自编写SQL(这有点违背了使用Hibernate的目的)? 我应该对我的数据库模式进行去规范化以减少连接表的数量吗? 如果我需要快速查询性能,我应该不使用Hibernate吗? 有更快的东西吗?

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

遵循最佳实践指南! 确保all和mappings在Hibernate2中指定lazy =“true”(这是Hibernate3中的新默认值)。 使用HQL LEFT JOIN FETCH指定在初始SQL SELECT中需要检索哪些关联。

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

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

请参阅有关提高性能的提示。 如果您对连接不小心,最终会遇到笛卡尔积问题。

除了“获取”策略之外,您还可以尝试在hibernate属性中设置批量提取大小,因此它将逐个运行加入查询但是批量运行。

在appContext.xml中:

  ...   ... 32    

所以代替:

 SELECT ... FROM Hobby WHERE personId=1 SELECT ... FROM Hobby WHERE personId=2 

你会得到:

 SELECT ... FROM Hobby WHERE personId in (1,2,...,32); SELECT ... FROM Hobby WHERE personId in (33,34,...,64); 

您是否尝试过对集合的“加入” 获取策略 ?

如果您需要Hibernate的function并且此function有问题,您有两个选择:a)提交错误请求并使用解决方法(缓慢的性能或手写的sql),直到修复错误,这需要一段时间b)提交错误请求以及修正和测试。 (当然你可以使用bugfix并跳过bugrequest和测试部分)。