2013-03-15 92 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 <= ? 

包裝:費用沒有性能可言。你可以忽略它。