Visualizzo problemi di prestazioni con il recupero di più istanze di oggetti che hanno molte relazioni con altri oggetti. Sto usando l'implementazione JPA di Spring e Hibernate con MySQL. Il problema è che durante l'esecuzione di una query JPA, Hibernate non si unisce automaticamente ad altre tabelle. Ciò si traduce in query SQL n * r + 1, dove n è il numero di oggetti da recuperare e r è il numero di relazioni.Hibernate può essere utilizzato in applicazioni sensibili alle prestazioni?
esempio, una persona vive in un indirizzo, ha molti hobby, e ha visitato molti Paesi:
@Entity
public class Person {
@Id public Integer personId;
public String name;
@ManyToOne public Address address;
@ManyToMany public Set<Hobby> hobbies;
@ManyToMany public Set<Country> countriesVisited;
}
Quando si esegue una query JPA per ottenere tutte le persone di nome Bob, e ci sono 100 Bobs nel database:
SELECT p FROM Person p WHERE p.name='Bob'
Hibernate traduce questo a 301 query SQL:
SELECT ... FROM Person WHERE name='Bob'
SELECT ... FROM Address WHERE personId=1
SELECT ... FROM Address WHERE personId=2
...
SELECT ... FROM Hobby WHERE personId=1
SELECT ... FROM Hobby WHERE personId=2
...
SELECT ... FROM Country WHERE personId=1
SELECT ... FROM Country WHERE personId=2
...
In base alle Domande frequenti su Hibernate (here e here), la soluzione deve specificare LEFT JOIN o LEFT OUTER JOIN (for many-to-many) nella query. Così ora la mia domanda si presenta come:
SELECT p, a, h, c FROM Person p
LEFT JOIN p.address a LEFT OUTER JOIN p.hobbies h LEFT OUTER JOIN p.countriesVisited c
WHERE p.name = 'Bob'
Questo funziona, ma sembra che vi sia un bug se c'è più di un LEFT OUTER JOIN in questo caso Hibernate è in modo non corretto alla ricerca di una colonna inesistente:
could not read column value from result set: personId69_2_; Column 'personId69_2_' not found.
Il comportamento del bug sembra essere indirizzato probabilmente da Hibernate Core bug HHH-3636. Sfortunatamente la correzione non fa parte di alcun Hibernate JAR rilasciato. Ho eseguito la mia applicazione contro la build dello snapshot ma il comportamento del bug è ancora presente. Ho anche creato il mio JAR Hibernate Core dall'ultimo codice nel repository e il comportamento del bug è ancora presente. Quindi forse HHH-3636 non affronta questo.
Questa limitazione delle prestazioni di Hibernate è molto frustrante. Se interrogo 1000 oggetti, vengono eseguite 1000 * r + 1 query SQL sul database. Nel mio caso ho 8 relazioni quindi ottengo query SQL 8001, che si traducono in prestazioni orribili. La soluzione ufficiale di Hibernate è quella di lasciare unire tutte le relazioni. Ma questo non è possibile con più di una relazione molti-a-molti a causa del comportamento del bug. Quindi sono bloccato con i join di sinistra per le relazioni many-to-one e le query n * r + 1 a causa delle relazioni many-to-many. Ho intenzione di inviare il problema LEFT OUTER JOIN come un bug di Hibernate, ma nel frattempo il mio cliente ha bisogno di un'app con prestazioni ragionevoli. Attualmente utilizzo una combinazione di batch fetch (BatchSize), ehcache e cache personalizzata in memoria, ma le prestazioni sono ancora piuttosto scadenti (è stato migliorato il recupero di 5000 oggetti da 30 a 8 secondi). La linea di fondo è che troppe query SQL stanno colpendo il database.
Quindi, le mie domande, è possibile utilizzare Hibernate in applicazioni sensibili alle prestazioni in cui le tabelle hanno più relazioni tra loro? Mi piacerebbe sapere quanto successo ha Hibernate che usa le prestazioni dell'indirizzo. Dovrei scrivere a mano SQL (che in qualche modo sconfigge lo scopo di usare Hibernate)? Devo de-normalizzare il mio schema di database per ridurre il numero di tabelle unite? Non dovrei usare Hibernate se ho bisogno di prestazioni di query veloci? C'è qualcosa più veloce?
Abbiamo rilasciato Batoo APP cioè ~ 15 volte più veloce Iberna e implementa lo Spec JPA% 100. La principale motivazione del progetto era che tutte e tre le implementazioni JPA sono state semplicemente messe a punto. Avendo realizzato JPA potrebbe essere molto più veloce, abbiamo sviluppato Batoo JPA. Fai un tentativo http://batoo.jp –