
In questo articolo parlerò dei problemi che si incontrano nel refactoring di una applicazione. Più precisamente di quanto espresso dal titolo quindi:
Come gestire l’ORM (Object Relationship Mapping) su tabelle già definite.
Devo riscrivere un’applicazione PHP in Groovy on Grails ma il database non deve essere toccato come specifica di progetto.
Le pratiche di convention over configuration di molti framweork MVC (come Groovy on Grails, Laravel o Django) permettono di creare il database semplicemente dalla definizione delle classi, senza scrivere alcuna istruzione DDL.
Tuttavia se il Cliente non vuole che si modifichi il database, abbiamo una serie di strumenti messi a disposizone dal layer di gestione della mappatura tra oggetti e tabelle adottato da Grails (Hibernate) che ci permettono di uscire dallo standard per adeguarci all’esistente.
Importante: se non si vuole sovrascrivere il database, nel file grails-app/conf/dataSource.groovy
va impostato il parametro dbCreate
come “update
“, non come “create-drop
” (mi raccomando).
Se c’è già un campo chiave primaria
Ogni tabella ha come standard la colonna ID che di solito è un number(10) (sto usando Oracle). Se nella definizione della classe aggiungiamo la proprietà id, Hibernate mapperà il campo id nella colonna ID senza protestare.
Il campo version
Hibernate aggiunge per ogni tabella anche il campo version per implementare la strategia di “optimistic locking”. Cioè si vuole gestire una storia del record per cui viene di fatto creata una chiave composita che si riferisce ad un particolare record attraverso la fornitura sia dell’id che del numero di versione. In realtà in molte applicazioni a cui ho lavorato questo tipo di meccanismo è delegato al DBMS, nel quale (soprattutto nei database Oracle che ho gestito) viene scritto un trigger che crea un “giornale” della tabella con dentro tutte le oprazioni fatte sul record: dal primo inserimento, alle succesive modifiche fino all’eventuale cancellazione fisica. Per cui, in questo contesto ho ritenuto sovrabbondante l’introduzione dell’oprimistic locking e ho optato per la sua soppressione.
Per evitare di gestire una proprietà version
si deve dichiare che non la si vuole nel dictionary mapping
dentro alla dichiarazione del domain
(il corrispondente Grails del model
nel paradiga MVC):
package myapp class Utente ... static mapping = { version false }
Il campo version
non verrà aggiunto alla tabella.
Il nome della tabella non segue uno standard
I nomi delle classi domain e nelle tabelle prassi abbastanza diffusa sceglierli in inglese, al singolare; però io ho una tabella Utenti per cui faccio un compromesso:
package myapp class Utente ... static mapping = { table "utenti" version false }
Specificando nel dictionary statico mapping
il campo table
posso scegliere di associare il nome della tabella del database “intoccabile”.
Relazioni uno a molti
Come esempio ho un tabella articoli che è figlia di una tabella capitoli: ad un capitolo possono essere associati più articoli. Ho di fatto una serie di articoli che appartengono ad un capitolo per cui si usa la variabile belongsTo
per specifcare questa relazione:
package myapp class Articolo { Integer id Float prezzo String descrizione Integer attivo static belongsTo = [capitolo: Capitolo]
In questa dichiarazione, il nome a sinistra del : è il nome della relazione (capitolo
in minuscolo), e il nome a destra è il nome dell’oggetto padre (Capitolo
).
Il nome delle chiavi esterne non segue uno standard
O, meglio, ne segue uno di suo che non è quello di Grails. Nella tabella Articoli per esempio ho una chiave esterna per realizzare la relazione con i Capitoli: idcapitolo
che si riferisce all’id della tabella Capitoli.
Seguendo il suo standard, Hibernate crea il nome <tabella>Id
per dichiarare le variabili “chiave”. Sfortunamtamente all’epoca nel database hanno seguito la convenzione inversa IDCAPITOLO
(con l’id prima del nome tabella).
Mai paura: si dichiara questa deviazione dallo standard nella creazione del domain:
package myapp class Articolo { Integer id Float prezzo String descrizione Integer attivo static belongsTo = [capitolo: Capitolo] static mapping = { table "articoli" capitolo column: "idcapitolo" // <----- version false }
Si faccia attenzione che per mappare il nome della colonna della tabella database ho utilizzato il nome della relazione definita in belongsTo
: non capitoloId (che non è definito da nessuna parte) ma capitolo.
Una tabella ha una chiave primaria composita
Come ultimo caso c’è quello di una tabella con una doppia chiave composta dall’unione di due campi idutente
e iddipartimento
(dovendo gestire il fatto che un utente può lavorare anche in dipartimenti diversi). È una strategia per gestire una relazione molti-a-molti. Di suo Hibernate vorrebbe che io aggiungessi una chiave artificiale ID ma la consegna data me lo impedisce, posso dichiarare la chiave composita e sopprimere la direttiva di creazione dell’ID:
package myapp class UtenteDipartimento implements Serializable { Integer attivo Date dataInizio Date dataFine static belongsTo = [utente: Utente, dipartimento: Dipartimento] static mapping = { table "utenti_dipartimenti" dataInizio column: "data_inizio" dataFine column: "data_fine" // relazioni utente column: "idutente" dipartimento column: "iddipartimento" id composite: ['utente', 'dipartimento'] version false }
nel quale sorgente riassumo un po’ tutte le cose dette nell’articolo.
Attenzione: se voglio creare una chiave composita, la classe deve implementare l’interfaccia Serializable
.
Commenti recenti