我有以下代码从Oracle数据库的单个表中执行简单查询。
entityManager.createQuery(
"SELECT a FROM " + Person.class.getSimpleName()
+ " a WHERE lower(a.firstName) = '" + firstName + "'")
.getSingleResult();
Hibernate生成以下sql:
select
*
from
( select
person0_.id as id75_,
person0_.FIRSTNAME as FIRSTNAME75_,
person0_.LASTNAME as LASTNAME75_
from
PERSONS person0_
where
lower(person0_.FIRSTNAME)='john' )
where
rownum <= ?
我们的DBA建议,出于性能原因,此查询应该更简单。如何使hibernate简化查询 像这样:
select ID, FIRSTNAME, LASTNAME from PERSONS
where lower(FIRSTNAEM) = 'john' and rownum <= 1
由于
答案 0 :(得分:8)
我刚刚查看了explain plan
与您类似的查询,两个查询的计划完全相同,所以我不确定您的DBA建议的性能原因。
使用select * from ( ... ) where rownum = 1
包装查询会引入一个STOPKEY,在一行之后停止内部查询。 Oracle知道您实际上并不想从子查询中获取所有结果,而只是获取第一行。
如果不修改hibernate源代码本身,就不可能改变Hibernate生成的查询。
注意,当您尝试引入ORDER BY
子句时,为什么需要这种嵌套的原因显而易见:
select ID, FIRSTNAME, LASTNAME
from PERSONS
where lower(FIRSTNAME) = 'john'
and rownum <= 1
order by LASTNAME
产生不同的结果
select * from (
select ID, FIRSTNAME, LASTNAME
from PERSONS
where lower(FIRSTNAME) = 'john'
order by LASTNAME)
where rownum <= 1
在where rownum
....
order by clause
编辑:
这里的参考是解释计划的输出,对于两个查询都是完全相同的:
---------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 112 | 2 (0)| 00:00:01 |
|* 1 | COUNT STOPKEY | | | | | |
|* 2 | TABLE ACCESS FULL| TABLE_NAME | 1 | 112 | 2 (0)| 00:00:01 |
---------------------------------------------------------------------------------
可以通过在lower(FIRST_NAME)
上放置一个功能索引来提高性能,但两个查询都会使用完全相同的。
答案 1 :(得分:2)
我强烈建议您使用查询参数:
Query query = entityManager.createQuery("SELECT a FROM "
+ Person.class.getSimpleName()
+ " a WHERE lower(a.firstName) = :name");
query.setParameter("name", firstName);
return query.getSingleResult();
这有两个重要原因:
考虑
select * from (...) where rownum <= ?
包装:这根本不需要任何性能。你可以忽略它。
答案 2 :(得分:0)
令我感到惊讶的是,没有人建议重写为本机Query而不是坚持使用Hibernate生成的内容。我遇到了同样的问题,尽管我的问题与性能无关,但是查询的结果从来都不相同,但实际上查询很难理解。
出于短期和长期原因,最好的方法是控制查询,随时随地使用本机查询。 只需按照DBA的建议编写查询,然后将其作为JPQL NamedNativeQuery放入即可,每个人都很高兴。享受吧!
答案 3 :(得分:-2)
简而言之 - 你没有。几乎没有足够的理由来调整Hibernate生成的查询,并且我有一种怀疑,即如果不修改源代码就可以实现。
我能想到的唯一简化是拨打getResultList().get(0)
而不是getSingleResult()
。它会简化生成的SQL,但会导致性能下降,而不是改进,因为您将从DB中获取所有匹配的行。
然而,您可以稍微改进一下查询。
您知道要查询的类的简单名称。没有必要先从实体获取它并连接,只需使用
SELECT a FROM Person a...
查询的连接参数不是一个好习惯。它使您的查询容易受到SQL注入攻击。最好写一下:
...WHERE lower(a.firstName) = :firstName");
query.setParameter("firstName", firstName);