Scenario
Le applicazioni che scriviamo con Spring boot sono tecnicamente dei microservizi, per quanto abbiamo visto finora gli oggetti che rappresentano la nostra realtà sono non correlati (in verità abbiamo gestito un solo oggetto ma gestirne di più allo stesso modo non fa differenza). Questa situazione però è troppo semplice per essere generalizzabile, in questo capitolo vediamo come si realizza un sistema che usa due oggeti che sono in una relazione uno a molti.
La realtà che andiamo a modellare prevede che ci siano degli hotel ciascuno dei quali ha più stanze. Non scriveremeo il sistema per intero ma soltanto le parti "diverse" rispetto al caso di entità di un solo tipo.
Il modello dei dati
Come dicevamo un oggeto Hotel con alcune proprietà legato a più oggetti Stanza con altre proprietà.
Il database va creato con queste due istruzioni (o con piccoli aggiustamenti dovuti al fatto che si usa h2 o MySQL o altro):
Attenzione al campo chiamato hotel_id
: il nome non è casuale ma
formato dal nome della tabella a cui punta e dal relativo campo chiave legati
da un underscore (solito discorso "Convention over configuration",
FIXME: serve riferimento).
Se si vogliono utilizzare nomi diversi è possibile usare l'annotazione
@JoinColumn
nella classe che rappresenta il "molti" della relazione, Stanza nel nostro caso.
Risulta evidente che l'unica cosa di cui parlare è l'attributo stanze
:
questo attributo non risiede nel database e lo si capisce dalla annotazione
@OneToMany
che specifica tre cose:
intanto che un Hotel è legato a più Stanza, poi
con mappedBy = "hotel"
il fatto che nella classe Stanza è la proprietà hotel che la lega ad un hotel,
fetch = FetchType.LAZY
indica invece che una volta che è stato letto
un Hotel dal database le relative stanze vanno lette soltanto all'occorrenza
(sarebbe a dire se qualcuno usa la proprietà stanze).
Nella classe Stanza c'è soltanto una annotazione di cui parlare:
@ManyToOne
che indica semplicemente che molte istanze della classe Stanza
appartengono allo stesso Hotel, in pratica fa il paio con @OneToMany
presente in Hotel.
Per tutto il resto non cambia nulla, ogni classe dovrà avere il relativo
JpaRepository
scritto come in precedenza e potrenno esserci una o più classi
annotate con @RestController
.
Serializzazione in JSON
Non ce ne occupiamo noi ma Spring boot giusto? Si e no... tutte le cose di default vengono fatte in automatico ma in questo caso sorge un problema: quando deve essere rappresentato con testo JSON un Hotel vengono automaticamente inserite tutte le Stanza relative (il che ci fa comodo) ma ogni Stanza ha a sua volta un Hotel, che ha delle Stanza e via di seguito: si viene a creare una specie di ciclo infinito.
La libreria che usiamo che crea il testo JSON
(formalmente di dice "serializza gli oggetti in JSON") si chiama jackson
ed è possibile indicargli che deve passare da Hotel a Stanza ma non viceversa:
quando stampa una stanza non vogliamo che metta di nuovo l'Hotel.
Di nuovo si usano due annotazioni: @JsonManagedReference
e
@JsonBackReference
.
La prima va inserita in Hotel (da Hotel devo passare a Stanza) mentre la seconda
in Stanza (da Stanza non devo passare ad Hotel).
Due righe in più in totale!
Le annotazioni @JsonManagedReference/JsonBackReference hanno un attributo
value
che può essere omesso (come nell'esempio sopra) ma che diventa
necessario ad esempio quando in una classe si trovano più @JsonBackReference
,
nel caso del nostro esempio l'attributo value va usato nel seguente modo:
Ancora sui nomi dei servizi
Nulla cambia rispetto a ciò che è scritto nel capitolo precedente,
ad esempio per avere le informazioni sull'Hotel il cui id è 75 useremo
la url /hotel/75
ma come ci si regola se voglio la stanza 654
dell'hotel 75? Così: /hotel/75/stanza/654