2012-07-09 17 views
5

Stosunkowo nowy w JPA, więc mam jedno pytanie architektoniczne. Powiedzmy mam tabele pracownika i handlowy z wielu do jednej relacji (tj wielu pracowników pracuje dla jednego działu):JPA - obliczona kolumna jako własność klasy encji?

EMPLOYEE 
    EMPLOYEE_ID 
    EMPLOYEE_NAME 
    DEPARTMENT_ID 

DEPARTMENT 
    DEPARTMENT_ID 
    DEPARTMENT_NAME 

Więc mogę określić odpowiednie podmioty dla pracownika i Zakładu, to nie ma problemu. Jednak w jednym widoku Chciałbym wyświetlić listę działów z liczbą pracowników zatrudnionych w tym dziale, coś takiego:

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

prostu nie jestem pewien, co jest właściwą strategią, aby osiągnąć to za pomocą JPA. .. Nie chcę zawsze pobierać liczby pracowników w dziale Oddział, ponieważ jest tam tylko jeden widok, gdy jest potrzebny.

Wygląda na to, że Hibernate @Formula będzie jednym z możliwych podejść, ale afaik nie jest zgodny ze standardem JPA.

Odpowiedz

4

Możesz utworzyć dowolny obiekt w swoim QL używając "nowej" składni - twoja klasa potrzebuje konstruktora, który pobiera wartości zwrócone przez twoje zapytanie.

Na przykład, z klasą jak DepartmentEmployeeCount, z konstruktora:

public DepartmentEmployeeCount(String departmentName, Integer employeeCount) 

można użyć QL coś takiego:

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

Lub jeśli po prostu wybierając count (*) Państwo może po prostu rzucić wynik zapytania na liczbę.

Alternatywnie, aby zrobić to samo bez klasy DepartmentEmployeeCount, można pominąć nowej, więc:

SELECT D.DEPARTMENT_NAME, count(E.id)  

byłby to powrót do List<Object[]> gdzie każdy element listy był tablicą 2 elementów, departmentName i licznik .

Aby odpowiedzieć na twoje późniejsze pytanie w komentarzach, aby wypełnić wszystkie pola Departamentu plus tymczasowe pole employeeCount, jedną z sugestii byłoby zrobienie 2 zapytań. Byłoby to jeszcze bardziej wydajne niż twoje pierwotne zapytanie (podselekcja dla każdej liczby pracowników).

Więc jedno zapytanie do zapoznania działy

SELECT D from Department D 

daje List<Department>

Następnie 2nd kwerenda powrocie tymczasową tablicę:

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

daje List<Object[]> z DEPARTMENT_ID i liczby w tym.

Następnie należy użyć drugiej listy, aby zaktualizować właściwość liczby przejściowej na pierwszej liście. (Możesz spróbować wybrać mapę, aby ułatwić to wyszukiwanie, ale myślę, że jest to funkcja Hibernuj).

+0

Dziękuję @MattR za odpowiedź. To rozwiązanie może działać dla tego prostego przykładu. Ale co jeśli (a) tabela DEPARTMENT ma wiele innych kolumn oprócz NAME i oczywiście nie chcę ich wszystkich wymienić w konstruktorze i (b) nadal chcę mieć to jako własność jednostki działu (z LAZY Fetch type), aby była dostępna gdzie indziej na wszelki wypadek. – AndreiM

+0

Dobre pytanie, nie próbowałem go, ale * myślę, że * możesz użyć klas jednostek w konstruktorze (abyś nie musiał wybierać wszystkich właściwości encji). Jeśli dostanę szansę, aby to wypróbować, zaktualizuję moją odpowiedź, tzn. Że możesz mieć klasę Pracowników działu (powiedzmy), która miała konstruktor działu Pracownicy (Dział, Integer) i użyć SELECT nowych pracowników działu (D, count (E.id))) - nie do końca to, o co prosisz, ale bliżej ... – MattR

+0

OK Myślałem o tym podczas obiadu, więc zaktualizowałem kilka dodatkowych informacji. Jeśli wypróbujesz którąkolwiek z sugestii lub znajdziesz lepsze rozwiązanie, zgłoś ponownie: – MattR

3

Opcja 1: Zasugerowałem to, ponieważ nie podobała ci się trasa konstruktorska sugerowana przez MattR.Kilka razy wspomniałeś o słowie "widok" i wiem, że mówiłeś o widoku dla użytkownika, ale dlaczego nie skonfigurować widoku w bazie danych, który zawiera obliczone kolumny, a następnie utworzyć jednostkę tylko do odczytu, która odwzorowuje obliczone kolumny?

Opcja 2: W odpowiedzi na Twój komentarz o braku chęci utworzenia widoku. Można utworzyć obiekt kontenera, który przechowuje jednostkę i kolumnę obliczeniową, a następnie podobnie jak sugeruje MattR, należy użyć nowego w swoim wyborze. Coś jak:

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 
} 

wówczas wybierz staje

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

Dzięki czemu można używać DepartmentInfo aby uzyskać właściwości, które są zainteresowane

+0

To jest możliwe, w rzeczy samej. Ale to, czego chcę, to po prostu dodatkowa właściwość dla mojej istniejącej istoty, którą mogę wykorzystać na jednej stronie (i, być może, gdzie indziej). Czy naprawdę warto stworzyć do tego inny widok i inny byt? – AndreiM

+0

To, czego oczekujesz _right now_, to po prostu dodatkowa właściwość, którą twój istniejący byt mógłby użyć na jednej stronie i ewentualnie innej. A w przyszłości ewentualnie 3 więcej z potencjalnie 4 dodatkowe właściwości i tak dalej i tak dalej. W każdym razie dodałem opcję 2 do mojej odpowiedzi. – digitaljoel

+0

Dziękuję, @digitaljoel, do tej pory wygląda na najbardziej optymalne podejście w tym scenariuszu. – AndreiM

1

Można by utworzyć element w swojej jednostce jako. dodatkową kolumnę, a następnie odwołaj ją do aliasu w zapytaniu. Nazwa kolumny w adnotacji @Column musi być zgodna z aliasem.

Załóżmy, że do pierwotnego zapytania można dodać element countEmployees w następujący sposób. dodać również insertable=false i updatable=false więc menedżer jednostka przyzwyczajenie spróbuj umieścić go w wstawieniu lub aktualizacji stwierdzeń:

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 

} 

And zapytanie:

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

Odnosi się to również podczas pracy z wiosennych JPA danych repozytoriach.

+0

Czy możesz zrobić to samo, używając zapytania JPA, a nie natywnego? – Arthur

+0

Ta strategia nie działa. – Partha

Powiązane problemy