Utilizzare meno colonne nella query SQL tramite Proiezioni di ibernazione sull'entità con relazione ManyToOne
-
06-07-2019 - |
Domanda
Sto cercando di creare un SQL più piccolo, per evitare " selezionare * da A " che viene creato per impostazione predefinita per i criteri di ibernazione.
Se utilizzo campi semplici (nessuna relazione), tramite " Transformers " ;, posso riuscire a disporre di questo SQL:
select description, weight from Dog;
Ciao, ho questa Entità:
@Entity
public class Dog
{
Long id;
String description;
Double weight;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "person_id", nullable = false)
Person owner;
}
@Entity
public class Person
{
Long id;
String name;
Double height;
Date birthDate;
}
Il mio obiettivo è quello di avere questo:
select description, weight, owner.name from Dog
L'ho provato con Criteria (e subcriteria):
Criteria dogCriteria = sess.createCriteria(Dog.class);
ProjectionList proList = Projections.projectionList();
proList.add(Projections.property("description"), description);
proList.add(Projections.property("weight"), weigth);
dogCriteria.setProjection(proList);
Criteria personCriteria = dogCriteria.createCriteria("owner");
ProjectionList ownerProList = Projections.projectionList();
ownerProList.add(Projections.property("name"), description);
dogCriteria.setProjection(ownerProList); //After this line, debugger shows that the
//projection on dogCriteria gets overriden
//and the query fails, because "name" is
//not a field of Dog entity.
Come dovrei usare Proiezioni, per ottenere un SQL più piccolo, meno colonne? Grazie in anticipo.
Soluzione
Prima di tutto,
select description, weight, owner.name from Dog
non è SQL valido. Dovrebbe essere qualcosa di simile
select description, weight, Person.name
from Dog join Person on Dog.person_id = Person.id
, invece. Secondo, perché? Sebbene sia possibile fare ciò che vuoi (vedi sotto), è estremamente dettagliato farlo tramite l'API Criteria e non ottieni nulla da mostrare per questo. I risparmi sul trasferimento di dati per un paio di colonne sono trascurabili a meno che tali colonne non siano enormi BLOB o non si selezionino centinaia di migliaia di record. In entrambi i casi ci sono modi migliori per affrontare questo problema.
In ogni caso, per fare ciò che desideri per i criteri, devi unirti alla tabella collegata (Persona) tramite alias e specificare la proiezione sui criteri principali usando detto alias:
Criteria criteria = session.createCriteria(Dog.class, "dog")
.createAlias("owner", "own")
.setProjection( Projections.projectionList()
.add(Projections.property("dog.description"))
.add(Projections.property("dog.weight"))
.add(Projections.property("own.name"))
);
C'è una descrizione e un esempio di quanto sopra in Documentazione sulle proiezioni di criteri . Tenere presente che, una volta eseguito, i criteri sopra riportati restituiscono un elenco di matrici di oggetti. Dovrai specificare un ResultTransformer per convertire i risultati in oggetti reali.
Altri suggerimenti
Non l'ho ancora provato da solo, ma penso che puoi anche usare un altro costruttore nella tua Entità (Pojo) e passare lì le colonne. Vedi https://www.thoughts-on-java.org/hibernate- best practice / capitolo "1.2 Pojo" per un'istruzione dettagliata. Anche per me non è ancora chiaro se questo funziona anche per le relazioni ManyToOne. Proverò.