Ste*_*Kuo 21 java performance hibernate jpa java-ee
我发现检索与其他对象有很多关系的多个对象实例时出现性能问题.我在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<Hobby> hobbies;
@ManyToMany public Set<Country> countriesVisited;
}
Run Code Online (Sandbox Code Playgroud)
当我执行JPA查询以获取名为Bob的所有人员时,数据库中有100个Bobs:
SELECT p FROM Person p WHERE p.name='Bob'
Run Code Online (Sandbox Code Playgroud)
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
...
Run Code Online (Sandbox Code Playgroud)
根据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'
Run Code Online (Sandbox Code Playgroud)
这有效,但是如果有多个LEFT OUTER JOIN似乎存在错误,在这种情况下Hibernate错误地寻找不存在的列:
could not read column value from result set: personId69_2_; Column 'personId69_2_' not found.
Run Code Online (Sandbox Code Playgroud)
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解决方案就是将所有关系都加入.但由于bug行为导致多个多对多关系不可能实现这一点.因此,由于多对多关系,我因左对联而陷入多对一关系和n*r + 1查询.我计划将LEFT OUTER JOIN问题作为Hibernate错误提交,但与此同时我的客户需要一个具有合理性能的应用程序.我目前使用批量提取(BatchSize)的组合,ehcache和自定义内存缓存,但性能仍然很差(它改进了从30到8秒检索5000个对象).底线是太多的SQL查询正在访问数据库.
那么,我的问题是,是否可以在性能敏感的应用程序中使用Hibernate,其中表之间存在多种关系?我很想知道Hibernate如何成功使用地址性能.我应该亲自编写SQL(这有点违背了使用Hibernate的目的)?我应该取消规范化我的数据库架构以减少连接表的数量吗?如果我需要快速查询性能,我应该不使用Hibernate吗?有更快的东西吗?
除了"获取"策略之外,您还可以尝试在hibernate属性中设置批量提取大小,因此它将逐个运行加入查询但是批量运行.
在appContext.xml中:
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
...
<property name="hibernateProperties">
<props>
...
<prop key="hibernate.default_batch_fetch_size">32</prop>
</props>
</property>
</bean>
Run Code Online (Sandbox Code Playgroud)
所以代替:
SELECT ... FROM Hobby WHERE personId=1
SELECT ... FROM Hobby WHERE personId=2
Run Code Online (Sandbox Code Playgroud)
你会得到:
SELECT ... FROM Hobby WHERE personId in (1,2,...,32);
SELECT ... FROM Hobby WHERE personId in (33,34,...,64);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
10344 次 |
| 最近记录: |