Grails in pillole – come superare i problemi che sorgono con database già fatti

Spread the love
Grails Logo
Grails

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.

Risorse

Grails Framework home page

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.