给出以下hibernate查询:
String sql = "select distinct changeset " +
"from Changeset changeset " +
"join fetch changeset.changeEntries as changeEntry " +
"join fetch changeEntry.repositoryEntity as repositoryEntity " +
"join fetch repositoryEntity.project as project " +
"join fetch changeset.author as changesetAuthor " +
"where project.id = :projectID ";
为什么会导致N + 1问题?
我希望这会生成以下单个SQL语句(或类似的东西)
select *
from Changeset
inner join changeEntry on changeset.id = changeEntry.changeset_id
inner join repositoryEntity on changeEntry.repositoryentity_id = repositoryentity.id
inner join project on repositoryentity.project_id = project.id
where project.id = ?
相反,我看到许多选择语句被触发。
此处的数据模型如下所示:
alt text http://img29.imageshack.us/img29/4123/uml.png
我想在一次访问数据库时从Select语句返回完整的对象图,这就是我在hibernate查询中明确使用“fetch”的原因。
Hibernate日志语句如下:
Hibernate: select distinct changeset0_.id as id2_0_, changeentr1_.id as id1_1_, repository2_.id as id9_2_, project3_.id as id6_3_, user4_.id as id7_4_, changeset0_.author_id as author5_2_0_, changeset0_.createDate as createDate2_0_, changeset0_.message as message2_0_, changeset0_.revision as revision2_0_, changeentr1_.changeType as changeType1_1_, changeentr1_.changeset_id as changeset4_1_1_, changeentr1_.diff as diff1_1_, changeentr1_.repositoryEntity_id as reposito5_1_1_, changeentr1_.repositoryEntityVersion_id as reposito6_1_1_, changeentr1_.sourceChangeEntry_id as sourceCh7_1_1_, changeentr1_.changeset_id as changeset4_0__, changeentr1_.id as id0__, repository2_.project_id as connecti6_9_2_, repository2_.name as name9_2_, repository2_.parent_id as parent7_9_2_, repository2_.path as path9_2_, repository2_.state as state9_2_, repository2_.type as type9_2_, project3_.projectName as connecti2_6_3_, project3_.driverName as driverName6_3_, project3_.isAnonymous as isAnonym4_6_3_, project3_.lastUpdatedRevision as lastUpda5_6_3_, project3_.password as password6_3_, project3_.url as url6_3_, project3_.username as username6_3_, user4_.username as username7_4_, user4_.email as email7_4_, user4_.name as name7_4_, user4_.password as password7_4_, user4_.principles as principles7_4_, user4_.userType as userType7_4_ from Changeset changeset0_ inner join ChangeEntry changeentr1_ on changeset0_.id=changeentr1_.changeset_id inner join RepositoryEntity repository2_ on changeentr1_.repositoryEntity_id=repository2_.id inner join project project3_ on repository2_.project_id=project3_.id inner join users user4_ on changeset0_.author_id=user4_.id where project3_.id=? order by changeset0_.revision desc
Hibernate: select repository0_.id as id10_9_, repository0_.changeEntry_id as changeEn2_10_9_, repository0_.repositoryEntity_id as reposito3_10_9_, changeentr1_.id as id1_0_, changeentr1_.changeType as changeType1_0_, changeentr1_.changeset_id as changeset4_1_0_, changeentr1_.diff as diff1_0_, changeentr1_.repositoryEntity_id as reposito5_1_0_, changeentr1_.repositoryEntityVersion_id as reposito6_1_0_, changeentr1_.sourceChangeEntry_id as sourceCh7_1_0_, changeset2_.id as id2_1_, changeset2_.author_id as author5_2_1_, changeset2_.createDate as createDate2_1_, changeset2_.message as message2_1_, changeset2_.revision as revision2_1_, user3_.id as id7_2_, user3_.username as username7_2_, user3_.email as email7_2_, user3_.name as name7_2_, user3_.password as password7_2_, user3_.principles as principles7_2_, user3_.userType as userType7_2_, repository4_.id as id9_3_, repository4_.project_id as connecti6_9_3_, repository4_.name as name9_3_, repository4_.parent_id as parent7_9_3_, repository4_.path as path9_3_, repository4_.state as state9_3_, repository4_.type as type9_3_, project5_.id as id6_4_, project5_.projectName as connecti2_6_4_, project5_.driverName as driverName6_4_, project5_.isAnonymous as isAnonym4_6_4_, project5_.lastUpdatedRevision as lastUpda5_6_4_, project5_.password as password6_4_, project5_.url as url6_4_, project5_.username as username6_4_, repository6_.id as id9_5_, repository6_.project_id as connecti6_9_5_, repository6_.name as name9_5_, repository6_.parent_id as parent7_9_5_, repository6_.path as path9_5_, repository6_.state as state9_5_, repository6_.type as type9_5_, repository7_.id as id10_6_, repository7_.changeEntry_id as changeEn2_10_6_, repository7_.repositoryEntity_id as reposito3_10_6_, repository8_.id as id9_7_, repository8_.project_id as connecti6_9_7_, repository8_.name as name9_7_, repository8_.parent_id as parent7_9_7_, repository8_.path as path9_7_, repository8_.state as state9_7_, repository8_.type as type9_7_, changeentr9_.id as id1_8_, changeentr9_.changeType as changeType1_8_, changeentr9_.changeset_id as changeset4_1_8_, changeentr9_.diff as diff1_8_, changeentr9_.repositoryEntity_id as reposito5_1_8_, changeentr9_.repositoryEntityVersion_id as reposito6_1_8_, changeentr9_.sourceChangeEntry_id as sourceCh7_1_8_ from RepositoryEntityVersion repository0_ left outer join ChangeEntry changeentr1_ on repository0_.changeEntry_id=changeentr1_.id left outer join Changeset changeset2_ on changeentr1_.changeset_id=changeset2_.id left outer join users user3_ on changeset2_.author_id=user3_.id left outer join RepositoryEntity repository4_ on changeentr1_.repositoryEntity_id=repository4_.id left outer join project project5_ on repository4_.project_id=project5_.id left outer join RepositoryEntity repository6_ on repository4_.parent_id=repository6_.id left outer join RepositoryEntityVersion repository7_ on changeentr1_.repositoryEntityVersion_id=repository7_.id left outer join RepositoryEntity repository8_ on repository7_.repositoryEntity_id=repository8_.id left outer join ChangeEntry changeentr9_ on changeentr1_.sourceChangeEntry_id=changeentr9_.id where repository0_.id=?
第二个重复多次 - 对于17个对象的结果集,第二个语句执行521次。
我怀疑这是RepositoryEntity对象中父/子关系的结果。出于这个选择的目的,我实际上只需要获取父对象。
有什么建议吗?
答案 0 :(得分:0)
除非您将集合映射为延迟加载,否则当您获取对象时,无论其他HQL如何,它都会生成多个选择。将连接映射更改为延迟加载。另外,除非connectionDetails永远不能为空,否则我建议您将最后一个连接更改为左连接。
答案 1 :(得分:0)
您发布的第一个SQL是您期望的那个(不包括“预期”SQL中缺少的inner join users
- 但它存在于您的HQL中,这是正确的。)
第二个SQL是(为简洁起见而简化):
select *
from RepositoryEntityVersion repository0_
left outer join ChangeEntry changeentr1_ on repository0_.changeEntry_id=changeentr1_.id
left outer join Changeset changeset2_ on changeentr1_.changeset_id=changeset2_.id
left outer join users user3_ on changeset2_.author_id=user3_.id
left outer join RepositoryEntity repository4_ on changeentr1_.repositoryEntity_id=repository4_.id
left outer join project project5_ on repository4_.project_id=project5_.id
left outer join RepositoryEntity repository6_ on repository4_.parent_id=repository6_.id
left outer join RepositoryEntityVersion repository7_ on changeentr1_.repositoryEntityVersion_id=repository7_.id
left outer join RepositoryEntity repository8_ on repository7_.repositoryEntity_id=repository8_.id
left outer join ChangeEntry changeentr9_ on changeentr1_.sourceChangeEntry_id=changeentr9_.id
where repository0_.id=?
这里的基表是RepositoryEntityVersion
,它不在你的图表上;我猜它在RepositoryEntity
上被映射为一对多?我进一步猜测它被映射为急切的提取,这就是你的问题所在。
您需要将其映射为惰性或在join fetch
的查询中明确提及它。然而,后者可能是不合需要的,因为可能涉及的数据量和(可能)返回重复的根实体实例。 distinct
并不总是有帮助;看看你发布的SQL,你会看到它被应用于所有表格中返回的所有列,从而使它变得毫无意义。