Hibernate为每个查询生成不同的SQL

时间:2012-02-26 10:04:51

标签: java sql performance hibernate memory-leaks

我刚刚在探查器下测试了我的应用程序,发现sql字符串占用了大约30%的内存!这很奇怪。

有很多这样的字符串存储在应用程序内存中。这是由hibernate生成的SQL查询,请注意不同的数字和尾随下划线:

select avatardata0_.Id as Id4305_0_,...... where avatardata0_.Id=? for update
select avatardata0_.Id as Id4347_0_,...... where avatardata0_.Id=? for update

这是我无法理解的部分。为什么hibernate必须为每个查询生成具有不同标识符的不同sql字符串,例如“ Id4305_0 _ ”?为什么不能为所有相同的查询使用一个查询字符串?这是绕过查询缓存的某种技巧吗?

如果有人能形容我为什么会这样,以及如何避免这种资源浪费,我将不胜感激。

更新

确定。我找到了。假设内存泄漏我错了,这是我的错。 Hibernate按预期工作。

我的应用程序在10个线程中创建了121(!)SessionFactories,它们生成了大约2300个SingleTableEntityPersisters实例。每个SingleTableEntityPersister生成大约15个具有不同标识符的SQL查询。 Hibernate被迫生成大约345,000个不同的SQL查询。一切都很好,没什么奇怪的。)

2 个答案:

答案 0 :(得分:1)

hibernate生成的查询字符串背后有一个逻辑。其主要目的是为表和列名称获取唯一的别名。

从您的查询中

select avatardata0_.Id as Id4305_0_,...... where avatardata0_.Id=?

  

avatardata0_ ==> avatardata是表的别名,并附加0_以表明它是查询中的第一个表。因此,如果它是查询中的第二个表(或实体),它应该显示为avatardata1_。它对列别名使用相同的逻辑。

因此,这样可以避免所有可能的冲突。

您正在查看这些查询,因为您已打开show_sql标记配置。 这是用于调试查询。一旦您的应用程序开始工作,您应该将其关闭。

详细了解API文档here

我不太了解内存消耗部分,但是在关闭上面的标志后重复测试,看看是否有任何改进。

答案 1 :(得分:1)

假设您使用的是sql server,您可能需要检查'?'的参数类型声明,确保声明每次都产生相同的固定长度声明。

动态长度参数将导致每个查询的单独执行计划。这可能会占用大量资源。我们看到的是相同的过程,被sql server解释为另一个查询,呈现一个单独的执行计划。

因此,

exec myprocedure @p1 varchar(3)='foo' 

exec myprocedure @p1 varchar(6)='foobar' 

会导致不同的计划。简单地说,@ p1的声明大小不同。

有很多关于这种行为的知识。如果上述内容适用于您,我建议您阅读“参数嗅探”。