2013-03-15 101 views
7

我有以下代码从一个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

首先,我将在FIRSTNAME字段上创建一个索引。 – 2013-03-15 10:14:39

+1

@StefanBe:当where子句使用函数时,该索引将被忽略。你需要一个功能索引... – beny23 2013-03-15 10:15:26

+0

啊好的。不知道 – 2013-03-15 10:16:54

回答

-2

总之 - 你没有。几乎从来没有足够的理由来调整Hibernate生成的查询,我有seroius怀疑它可能没有修改源代码。

我能想到的唯一简化就是致电getResultList().get(0)而不是getSingleResult()。它会简化生成的SQL,但会导致性能下降,而不是提高,因为您将从数据库中获取所有匹配的行。

但是,您可以稍微改进查询。

你知道你正在查询的类的简单名称。有没有必要首先从实体得到它并连接,只需使用

SELECT a FROM Person a...

串联参数查询是不是一个好的做法。它使您的查询容易受到SQL注入攻击。这是更好地写:

...WHERE lower(a.firstName) = :firstName"); 
query.setParameter("firstName", firstName); 
+0

我认为调用'getResultList()。get(0)'是一个坏主意,因为您只会返回来自数据库的所有行仅用于您的代码,只使用第一行。让Oracle只返回一行会更好。 – beny23 2013-03-15 10:19:36

+0

@ beny23是的,当然,它会恶化性能,但它会简化查询一下。我现在在答案中已经更加明确了,但它并不十分清楚。 – kostja 2013-03-15 10:23:08

+0

如果getResultList()需要在where子句中使用唯一字段返回1行,会怎么样? – 2013-03-15 10:47:02

8

我只是看着explain plan为与你相似的查询和计划正是针对这两个查询相同,因此我不确定你的DBA建议的性能原因。

select * from (...) where rownum = 1包装查询引入了一个STOPKEY,它在一行之后停止内部查询。 Oracle知道你实际上并不想从子查询中获得所有结果,然后只取第一行。

如果不修改休眠源代码本身,更改由Hibernate生成的查询是不可能的。作为order by clause前应用where rownum

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 

注意,为什么嵌套是必要的原因,当你试图引入ORDER BY条款变得明显...

编辑:

仅供参考,这里的解释计划的输出,这是完全一样的两个查询:

--------------------------------------------------------------------------------- 
| 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

+1更好的答案IMO – kostja 2013-03-15 10:30:37

2

我强烈建议,您使用的查询参数:

Query query = entityManager.createQuery("SELECT a FROM " 
    + Person.class.getSimpleName() 
    + " a WHERE lower(a.firstName) = :name"); 
query.setParameter("name", firstName); 
return query.getSingleResult(); 

这有两个重要原因:

  • 您防止SQL注入
  • 您允许SQL-server缓存已解析的查询,从而改进后续执行的性能

考虑

select * from (...) where rownum <= ? 

包装:费用没有性能可言。你可以忽略它。