2017-06-20 3 views
0

Ich brauche Hilfe beim Generieren von Abfrage in JPA. Lassen Sie uns sagen, ich habe diese TabelleJPA: wie komplizierte Zählung ohne N + 1 Problem zu definieren

create table PERSON (
    person_id number(3,0) not null primary key, 
    last_name varchar2(15) not null, 
    first_name varchar2(15) not null, 
    department varchar2(15) not null 
); 

create table PHONE (
    phone_id number(3,0) not null primary key, 
    person_id number(3,0) not null, 
    phone_type varchar2(10) not null, 
    CONSTRAINT fk_person_id FOREIGN KEY (person_id) 
    REFERENCES person (person_id) 
); 

Ich möchte eine Abfrage wie definieren:

select p1.person_id, p1.last_name, p1.first_name, 
     (select count(1) from phone p2 
     where p2.person_id = p1.person_id) as phone_count 
from person p1; 

Meine JPA Klasse wie unten definiert:

@Entity 
@Table(name="PERSON") 
@Transactional(readOnly=true) 
public class Person { 
    @Id 
    @Column(name = "PERSON_ID") 
    private Integer personId; 

    @Column(name = "LAST_NAME") 
    private String lastName; 

    @Column(name = "FIRST_NAME") 
    private String firstName; 

    @Column(name = "DEPARTMENT") 
    private String department; 

    @OneToMany 
    @LazyCollection(LazyCollectionOption.FALSE) 
    @Fetch(FetchMode.SELECT) // or @Fetch(FetchMode.JOIN) 
    @JoinColumn(name = "PERSON_ID", referencedColumnName="PERSON_ID") 
    private Set<Phone> phones; 

    .... getters and setters... 
} 


@Entity 
@Table(name="PHONE") 
@Transactional(readOnly=true) 
public class Person { 
    @Id 
    @Column(name = "PHONE_ID") 
    private Integer phoneId; 

    @Column(name = "PERSON_ID") 
    private Integer personId; 

    @Column(name = "PHONE_TYPE") 
    private String phoneType; 

    .... getters and setters ... 
} 

Da ich dynamische where-Klausel haben, dh last_name, first_name, department combination können nach Bedarf übergeben werden. Ich habe eine Funktion:

personRepository.findAll(spec, pageable); 

, die mir die Dynamik zu lösen hilft, wo Klausel und dann habe ich die Telefonzahl über getPhones.size get(). Es funktioniert wie erwartet. Es erstellt jedoch N + 1 Problem. Wenn ich mehr als 500 Personen habe, führt es eine Schleife für jede Person ein und ruft eine Abfrage in der Telefontabelle auf. Meine Frage ist, wie man N + 1 Problem in diesem Fall vermeidet.

Ich denke, auch native Abfrage zu verwenden, und ich weiß nicht, wie die dynamische Where-Klausel funktioniert (mein echtes Programm hat mehr als 15 Spalten zu filtern). Hilfe wird geschätzt.

+0

In der Regel für normale Abfrage, ich werde einfach vorschlagen, '' Person Person p links Join fetch p.phones' hinzuzufügen. Nicht vertraut mit der Verwendung von 'Spezifikation' obwohl. Eine schnelle Überprüfung scheint nahe zu legen, dass 'Root' es erlaubt,' fetch' zu definieren, was wiederum zu einem Join-Fetch führt, wofür Sie die Spezifikation erstellen können. –

+0

Der @Fetch (FetchMode.JOIN) führt den linken Join aus Ich nehme an, aber das tut es nicht. Es gab mir immer noch N + 1 Abfrage. –

+0

Ich bin fast nie auf eifrige Fetch-Definition in Entitäten angewiesen, weil 1. es nicht immer funktioniert, 2. Ich habe keine Möglichkeit zu kontrollieren. Ich würde eher steuern Abrufverhalten in der Abfrage statt –

Antwort

0

Die äquivalente würde JPA-Abfrage sein:

SELECT 
    p.personId, p.lastName, p.firstName, 
    (SELECT COUNT(ph) FROM p.phones ph) 
WHERE ... 

Das gibt Tupeln, dh es als verwenden:

TypedQuery<Object[]> q = em.createQuery(_QUERY_FROM_ABOVE_, Object[].class); 
List<Object[]> results = q.getResultList(); 
// results.get(0)[0] -> Integer personId 
// results.get(0)[1] -> String lastName 
// results.get(0)[2] -> String firstName 
// results.get(0)[3] -> Long COUNT 

Sie sogar für die gesamte Person fragen kann, zusammen mit seiner Zählung Telefone:

SELECT p, (SELECT COUNT(ph) FROM p.phones ph) 
WHERE ... 

Mit Code ähnlich dem obigen, results.get(0)[0] ist das gesamte Person Objekt, results.get(0)[1] ist die Zählung als 10.

+0

oder vielleicht "Tupel" –

+0

Vielen Dank für die Antwort. Diese Lösung funktioniert für die statische Where-Klausel. In meinem Szenario wird die WHERE-Klausel basierend auf dem übergebenen Filter generiert. Ich habe eine Lösung für dieses Problem gefunden, indem ich eine View-Tabelle mit den counts erstellt habe. Von JPA kann ich normale Abfrage findAll (spec, pageable) in der Ansichtstabelle ausführen. –