Domanda

Sto utilizzando l'implementazione di JPA di Hibernate e vedo scarse prestazioni poiché vengono eseguite più query SQL per ogni entità recuperata. Se utilizzo una query JPA unita, viene generata una sola query SQL, ma non viene trovata alcuna relazione nulla valida.

Esempio, considera questo semplice schema. Una persona vive a un indirizzo ed è impiegata da un'azienda. Sia l'indirizzo che il datore di lavoro sono facoltativi e possono quindi essere nulli.

@Entity
public  class Person {
    public name;

    @ManyToOne
    @Column(nullable=true)
    public Address address

    @ManyToOne
    @Column(nullable=true)
    public Company employer
}

@Entity
public  class Address {
    address attributes ...
}

@Entity
public  class Company {
    company attributes ...
}

Non mostrato sopra è che ogni entità JPA ha una sorta di ID (chiave):

@Id
public Integer id;

Il problema che vedo è che una singola query JPA su Person provoca più query SQL sul database. Ad esempio, la seguente query JPA:

select p from Person p where ...

risulta nella query SQL:

select ... from Person where ...

e anche la seguente coppia di query SQL per ciascuna persona recuperata:

select ... from Address a where a.id=xxx
select ... from Company c where c.id=yyy

Questo ha un impatto enorme sulle prestazioni. Se il set di risultati della query è 1000 persone, genera 1 + 1000 + 1000 = 2001 query SQL.

Quindi ho provato a ottimizzare la query JPA costringendola a unirsi:

select p from Person p join p.address a join p.employer e where ...

o

select p, a, e from Person p join p.address a join p.employer e where ...

Ciò si traduce in una singola query SQL con un sacco di join. Il problema è se l'indirizzo o il datore di lavoro è nullo, quindi la query unita non la troverà.

Quindi rimango con la query senza join che è lenta o che la query con join rapido che non recupera le righe annullerà le relazioni. Qui mi sta sfuggendo qualcosa. Sicuramente c'è un modo per eseguire query veloci e complete.

È stato utile?

Soluzione

La mia ipotesi è che avresti bisogno di un join sinistro, ovvero,

SELECT p FROM Person p LEFT JOIN p.address a LEFT JOIN p.employer e WHERE...

Vedi questo post sul blog per un esempio

Nota che in realtà non l'ho provato con JPA, ma funziona bene in HQL, che è la base dello standard JPA in molti modi.

Il motivo per cui non funziona con i join semplici è che l'impostazione predefinita è un join interno.

Altri suggerimenti

Prova a impostare una dimensione batch (@BatchSize) sulle entità Indirizzo e Società. Non li caricherà in un join (è quello che stai cercando?), Ma ne caricherà un sacco ogni volta che uno viene caricato. Il batchsize indica quanti dovrebbe caricare quando scopre di averne bisogno.

Se hai una dimensione batch di 1 (impostazione predefinita) e carica 10 persone. Quindi scorrere su di essi, leggendo il loro indirizzo e gli elementi dell'azienda, quindi ibernazione eseguirà una query per le 10 persone, quindi ogni volta che avrà bisogno dell'indirizzo o dell'azienda per una di quelle persone, farà una query per l'indirizzo di quella persona.

Se hai impostato una dimensione batch di 7 sull'entità Indirizzo, quando leggi il primo indirizzo, vedrà che ci sono più di 7 Indirizzi che sono attualmente sottoposti a proxy e andranno a prenderti 7 degli indirizzi.

Se hai sia Indirizzo che Azienda con un BatchSize di 7 e stai iterando attraverso 10 persone, questo comporterà 5 query, piuttosto che i 21 anni che otterresti al momento. Ancora non l'1 che ti darà un join. Tuttavia, l'unione sarebbe più lenta nei casi in cui desideri solo gli oggetti Persona e non toccherai le entità Indirizzo / Azienda incorporate in essi (supponiamo che tu voglia solo ottenere un elenco degli ID persona o contare quanti sono maschio / femmina)

Dai un'occhiata a: http://hibernate.org/hib_docs/v3/reference/en/ html / performance.html

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top