Sto scrivendo una query JPQL (con Hibernate come mio provider JPA) per recuperare un'entità Company
e diverse sue associazioni. Questo funziona bene con le mie associazioni ManyToMany "semplici", in questo modo:Partecipazione al recupero nested con JPQL e Hibernate
@Entity
@Table(name = "company")
@NamedQueries({
@NamedQuery(
name = "Company.profile.view.byId",
query = "SELECT c " +
"FROM Company AS c " +
"INNER JOIN FETCH c.city AS city " + <-- @ManyToOne
"LEFT JOIN FETCH c.acknowledgements " + <-- @ManyToMany
"LEFT JOIN FETCH c.industries " + <-- @ManyToMany
"WHERE c.id = :companyId"
)
})
public class Company { ... }
Hibernate crea una singola query per recuperare quanto sopra, che è buono. Tuttavia, la mia entità Company
ha anche un'associazione molti a molti con i dati memorizzati nella tabella intermedia, quindi perché questa è mappata come associazioni @OneToMany
e @ManyToOne
tra tre entità.
società < - CompanyService -> Servizio
Questi sono i tre soggetti che ho nel mio codice. Pertanto, un'istanza Company
ha una raccolta di entità CompanyService
, ciascuna delle quali ha una relazione con un'istanza Service
. Spero che abbia senso - altrimenti controlla il codice sorgente alla fine della domanda.
Ora desidero recuperare i servizi per una determinata azienda modificando la query precedente. Ho letto in anticipo che JPA non consente join fetch annidati o alias pari per join, ma che alcuni provider JPA lo supportano, quindi ho tentato la fortuna con Hibernate. Ho provato a modificare la query in quanto tale:
@Entity
@Table(name = "company")
@NamedQueries({
@NamedQuery(
name = "Company.profile.view.byId",
query = "SELECT c " +
"FROM Company AS c " +
"INNER JOIN FETCH c.city AS city " +
"LEFT JOIN FETCH c.acknowledgements " +
"LEFT JOIN FETCH c.industries " +
"LEFT JOIN FETCH c.companyServices AS companyService " +
"LEFT JOIN FETCH companyService.service AS service " +
"WHERE c.id = :companyId"
)
})
public class Company { ... }
Ora, invece di creare una singola query, Hibernate crea le seguenti interrogazioni:
#1
select ...
from company company0_
inner join City city1_ on company0_.postal_code = city1_.postal_code
[...]
left outer join company_service companyser6_ on company0_.id = companyser6_.company_id
left outer join service service7_ on companyser6_.service_id = service7_.id
where company0_.id = ?
#2
select ...
from company company0_
inner join City city1_ on company0_.postal_code = city1_.postal_code
where company0_.id = ?
#3
select service0_.id as id1_14_0_, service0_.default_description as default_2_14_0_, service0_.name as name3_14_0_
from service service0_
where service0_.id = ?
#4
select service0_.id as id1_14_0_, service0_.default_description as default_2_14_0_, service0_.name as name3_14_0_
from service service0_
where service0_.id = ?
Query # 1 ho lasciato fuori il irrilevante join come questi sono OK. Sembra selezionare tutti i dati di cui ho bisogno, inclusi i servizi e i dati delle entità intermedie (CompanyService
).
Query # 2 Questa query recupera semplicemente l'azienda dal database e la sua City
. L'associazione cittadina è stata recuperata con impazienza, ma la query viene comunque generata anche se la cambio in pigro. Onestamente, non so a cosa serva questa domanda.
Query # 3 + Query # 4 Queste query sono alla ricerca fino Service
istanze basata su ID, presumibilmente sulla base di ID di servizio recuperati in Query # 1. Non vedo la necessità di questa query, perché questi dati sono stati già recuperati in Query n. 1 (proprio come i dati di Query n. 2 erano già stati scaricati in Query n. 1). Inoltre, questo approccio ovviamente non si adatta bene se una società ha molti servizi.
La cosa strana è che sembra che la query n. 1 faccia ciò che voglio, o almeno recuperi i dati di cui ho bisogno. Solo non so perché Hibernate crea la query n. 2, n. 3 e n. Quindi ho le seguenti domande:
- Perché Hibernate crea query n. 2, n. 3 e n. 4? E posso evitarlo?
- Hibernate supporta il recupero di associazioni nidificate anche se JPA non lo fa? Se è così, come potrei farlo nel mio caso?
- Questo comportamento è normale, oppure è perché quello che sto cercando di fare non è supportato e quindi ottengo risultati strani?Ciò sembrerebbe strano, perché la query n. 1 sembra perfetta
Qualsiasi suggerimento di errori o soluzioni alternative per realizzare ciò che voglio sarebbe molto apprezzato. Di seguito è riportato il mio codice (getter e setter esclusi). Grazie mille in anticipo!
entità impresa
@Entity
@Table(name = "company")
@NamedQueries({
@NamedQuery(
name = "Company.profile.view.byId",
query = "SELECT c " +
"FROM Company AS c " +
"INNER JOIN FETCH c.city AS city " +
"LEFT JOIN FETCH c.acknowledgements " +
"LEFT JOIN FETCH c.industries " +
"LEFT JOIN FETCH c.companyServices AS companyService " +
"LEFT JOIN FETCH companyService.service AS service " +
"WHERE c.id = :companyId"
)
})
public class Company {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column
private int id;
// ...
@ManyToOne(fetch = FetchType.EAGER, targetEntity = City.class, optional = false)
@JoinColumn(name = "postal_code")
private City city;
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "company_acknowledgement", joinColumns = @JoinColumn(name = "company_id"), inverseJoinColumns = @JoinColumn(name = "acknowledgement_id"))
private Set<Acknowledgement> acknowledgements;
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "company_industry", joinColumns = @JoinColumn(name = "company_id"), inverseJoinColumns = @JoinColumn(name = "industry_id"))
private Set<Industry> industries;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "company")
private Set<CompanyService> companyServices;
}
entità CompanyService
@Entity
@Table(name = "company_service")
@IdClass(CompanyServicePK.class)
public class CompanyService implements Serializable {
@Id
@ManyToOne(targetEntity = Company.class)
@JoinColumn(name = "company_id")
private Company company;
@Id
@ManyToOne(targetEntity = Service.class)
@JoinColumn(name = "service_id")
private Service service;
@Column
private String description;
}
entità Servizio
@Entity
@Table(name = "service")
public class Service {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column
private int id;
@Column(length = 50, nullable = false)
private String name;
@Column(name = "default_description", nullable = false)
private String defaultDescription;
}
dati Recuperare
public Company fetchTestCompany() {
TypedQuery<Company> query = this.getEntityManager().createNamedQuery("Company.profile.view.byId", Company.class);
query.setParameter("companyId", 123);
return query.getSingleResult();
}
Grazie.Quello che mi chiedo è il motivo per cui le ultime tre domande vengono persino eseguite, quando il primo è tutto Hibernate deve fare quello che gli ho chiesto di fare. Ma forse hai ragione che non è supportato - ma se così fosse, allora è strano che assembla correttamente # 1 per fare esattamente quello che mi aspettavo. Strano! :-) – Andy0708
Strano che sia, ma sfortunatamente non posso aiutare di più, e queste sono solo le mie ipotesi. Vorrei vedere un commento da qualcuno che ha familiarità con il funzionamento interno di Hibernate. –