2012-07-09 57 views
5

JPA比較新,所以我有一種建築問題。 比方說,我有表EMPLOYEE和DEPARTMENT與多對一的關係(即很多員工一個部門工作):JPA - 計算列作爲實體類屬性?

EMPLOYEE 
    EMPLOYEE_ID 
    EMPLOYEE_NAME 
    DEPARTMENT_ID 

DEPARTMENT 
    DEPARTMENT_ID 
    DEPARTMENT_NAME 

所以我可以定義爲員工和部門適當的實體,沒有任何問題。然而,在一個視圖中,我想這樣的與該部門工作的員工人數,東西顯示部門的列表:

SELECT D.DEPARTMENT_NAME, 
     (SELECT COUNT(*) FROM EMPLOYEE E WHERE E.DEPARTMENT_ID = D.DEPARTMENT_ID) NUMBER_OF_EMPLOYEES 
FROM DEPARTMENT D 

我只是不知道什麼是實現這個使用JPA是正確的策略。 .. 我不想總是爲部門實體提取員工數量,因爲只有一個視圖是需要的。

它看起來像Hibernate的@Formula將是一種可能的方法,但afaik它不符合JPA標準。

回答

4

您可以使用「new」語法在您的QL中創建任何對象 - 您的類只需要一個構造函數,它接受查詢返回的值。

例如,對於像DepartmentEmployeeCount一類,它有一個構造函數:

public DepartmentEmployeeCount(String departmentName, Integer employeeCount) 

你可以使用QL類似:

SELECT NEW DepartmentEmployeeCount(D.DEPARTMENT_NAME, count(E.id)) from Department D left join D.employees E GROUP BY D.DEPARTMENT_NAME 

或者,如果你只是選擇了COUNT(*)你可以簡單地將查詢結果轉換爲數字。

另外,做同樣沒有DepartmentEmployeeCount類,你可以離開了新的,所以:

SELECT D.DEPARTMENT_NAME, count(E.id)  

這將返回一個List<Object[]>其中每個列表項是2個元素的數組,DEPARTMENTNAME和計數。

要回答你的問題,後來在評論中,來填充處加一個瞬態EMPLOYEECOUNT場的各個領域,一個建議是做2個查詢。這仍然比您的原始查詢(每個員工數量的子查詢)更高效。

所以一個查詢閱讀部門

SELECT D from Department D 

給你一個List<Department>

然後,第二個查詢,返回臨時數組:

SELECT D.DEPARTMENT_ID, count(E.id) from Department D left join D.employees E GROUP BY D.DEPARTMENT_ID 

給你一個List<Object[]>與DEPARTMENT_ID和計數在裏面。

然後使用第二個列表更新你的第一個列表上的瞬時計數屬性。 (你可以嘗試選擇一個地圖來使這個查找更容易,但我認爲這是一個休眠功能)。

+0

謝謝@MattR的回答。此解決方案可能適用於此簡單示例。但是,如果(a)DEPARTMENT表除NAME之外還有許多其他列,我顯然不想在構造函數中列出所有這些列,並且(b)我仍然希望將其作爲Department實體的屬性(使用LAZY獲取類型)可以在其他地方訪問以防萬一。 – AndreiM 2012-07-10 00:08:19

+0

好問題,我沒有嘗試過,但我認爲*你可以在構造函數中使用實體類(所以你不需要選擇所有的實體屬性)。如果我有機會嘗試它,我會更新我的答案......也就是說您可以有一個DepartmentEmployees類(比如說),它具有一個構造函數DepartmentEmployees(Department,Integer)並使用SELECT new DepartmentEmployees(D,count(E.id )) - 不完全是你要求的,但更接近... – MattR 2012-07-10 00:22:05

+0

好吧,我正在考慮這個午餐,所以更新了一些額外的信息。如果您嘗試任何建議或找到更好的解決方案,請回報 – MattR 2012-07-10 04:46:48

3

選項1:我建議這個,因爲你不喜歡MattR建議的構造函數路線。你多次提到「視圖」這個詞,而且我知道你在向用戶討論視圖,但爲什麼不在數據庫中設置包含計算列的視圖,然後創建映射到計算的只讀實體列?

選項2:迴應您對不想創建視圖的評論。你可以創建一個包含實體和計算列的容器對象,就像MattR所說的那樣,你在你的選擇中使用了一個新對象。喜歡的東西:

public class DepartmentInfo { 
    private Department department; 

    // this might have to be long or something 
    // just see what construct JPA tries to call 
    private int employeeCount; 

    public DepartmentInfo(Department d, int count) { 
     department = d; 
     employeeCount = count; 
    } 
    // getters and setters here 
} 

那麼你的選擇變得

SELECT new my.package.DepartmentInfo(D, 
     (SELECT COUNT(*) FROM EMPLOYEE E WHERE E.DEPARTMENT_ID = D.DEPARTMENT_ID)) 
FROM DEPARTMENT D 

這樣,您可以使用DepartmentInfo讓你感興趣的性質

+0

這確實是可能的。但是我想要的只是我現有實體的一個額外屬性,我可以在一個頁面上使用(也可能在其他地方)。真的值得創建另一個視圖和另一個實體嗎? – AndreiM 2012-07-10 15:11:38

+0

你想要什麼_right now_只是你現有的實體在一個頁面上使用的一個額外的屬性,可能是另一個。並且在將來可能有3個以上可能還有4個額外的屬性等等。無論如何,我已將選項2添加到我的答案中。 – digitaljoel 2012-07-10 18:05:45

+0

謝謝@digitaljoel,迄今爲止,看起來像這種情況下最優化的方法。 – AndreiM 2012-07-10 21:00:35

1

你可以在你的實體創建成員。一個額外的列,然後在查詢中使用別名引用它。 @Column註釋中的列名必須與別名匹配。

說,對於您的原始查詢,您可以添加countEmployees成員,如下所示。同時添加insertable=falseupdatable=false所以實體管理器不會嘗試它包括在插入或更新語句:

public class Department { 

    @Column(name="DEPARTMENT_ID") 
    Long departmentId; 

    @Column(name="DEPARTMENT_NAME") 
    String departmentName; 

    @Column(name="countEmployees", insertable=false, updatable=false) 
    Long countEmployees; 

    //accessors omitted 

} 

,且查詢:

SELECT D.DEPARTMENT_NAME, 
(SELECT COUNT(*) FROM EMPLOYEE E WHERE E.DEPARTMENT_ID = D.DEPARTMENT_ID) AS countEmployees 
FROM DEPARTMENT D 

與Spring JPA的數據存儲庫工作時同樣如此。

+0

你可以使用JPA查詢來做同樣的事情嗎,不是原生的嗎? – Arthur 2016-07-17 14:29:24

+0

此策略不起作用。 – Partha 2018-02-05 18:03:03