Modello dati
Entità JPA, relazioni, schema DB reale e vincoli di integrità
- 1. Entità principali (14)
- 2. Diagramma ER
- 3.
semiorto_analisinel dettaglio - 4.
semiorto_contenel dettaglio - 5.
semiorto_lotto - 6. Anagrafiche (specie, varietà, produttore)
- 7. La tabella mappa specie × mese × tipologia
- 8. Modello utenti/permessi/menu
- 9. Foreign keys e vincoli
- 10. Charset e aspetti fisici
1. Entità principali (14)
Tutte le entità sono in it.dss.model.vo. Ogni entità è un POJO Hibernate annotato con @Entity e @Table(name = "..."). Le relazioni usano @ManyToOne(LAZY) + @OneToMany(mappedBy, LAZY).
| Entità Java | Tabella DB | Record (live) | Funzione |
|---|---|---|---|
SemiortoAnalisi | semiorto_analisi | 61.998 | Il cuore: una prova di germinabilità eseguita su un lotto |
SemiortoConte | semiorto_conte | 109.204 | Letture giornaliere delle ripetizioni di una prova (A, B, C, D) |
SemiortoLotto | semiorto_lotto | 11.138 | Un lotto di seme ricevuto in laboratorio |
SemiortoSpecie | semiorto_specie | 160 | Specie vegetale (Pomodoro, Peperone, …) |
SemiortoVarieta | semiorto_varieta | 1.332 | Varietà di una specie |
SemiortoProduttore | semiorto_produttore | 331 | Fornitore / produttore del seme |
SemiortoTecnico | semiorto_tecnico | 4 | Persona fisica che esegue le analisi |
SemiortoTipologiaSeme | semiorto_tipologia_seme | 9 | Portaseme, Natura, Selezionato, Calibrato, Film coated, Pillolato, Trattato, Multiseme, Vigorizzato |
SemiortoAnalisiRipetizione | semiorto_analisi_ripetizione | 12 | I 12 mesi dell'anno (nome fuorviante, non sono tipi di prova) |
SemiortoMapSpecieAnalisiRipetizioneTipologiaSeme | semiorto_map_... | ~200 | Matrice di fattibilità: quale mese si può fare analisi su quale specie+tipologia |
Utente | utente | 5 | Account |
Permesso | permesso | 3 | Ruoli: Amministrazione, Laboratorio, Magazzino |
Permessi | permessi | — | Join table utente ↔ permesso (M:N) |
Menu | menu | 20 | Voci menu alberate (con id_padre) |
PermessoMenu | permesso_menu | ~40 | Quale ruolo vede quale voce menu |
SemiortoErrori | semiorto_errori | — | Log errori (non usato nei flussi principali) |
2. Diagramma ER
┌─ AUTH ──────────────────────────────────────────────────────────────┐ │ │ │ utente ──*permessi*── permesso ──*permesso_menu*── menu │ │ │ │ (auto-ref) │ │ id, username, password(sha), └── id_padre │ │ email, nome, cognome, attivo, │ │ cambiaPassword │ └─────────────────────────────────────────────────────────────────────┘ ┌─ DOMINIO GERMINABILITÀ ─────────────────────────────────────────────────┐ │ │ │ semiorto_specie ─┬── semiorto_varieta ──── semiorto_lotto │ │ 1 : N │ 1 : N │ │ │ │ │ N : 1 │ │ │ ▼ │ │ │ semiorto_produttore │ │ │ │ │ ▼ (M:N via map) │ │ semiorto_map_specie_analisi_ripetizione_tipologia_seme │ │ ▲ ▲ │ │ │ │ │ │ semiorto_analisi_ semiorto_tipologia_ │ │ ripetizione (mesi) seme │ │ │ │ │ │ semiorto_lotto ──── semiorto_analisi ───── semiorto_conte │ │ 1 : N 1 : N-4 (A/B/C/D) │ │ │ │ │ │ N : 1 │ │ ▼ │ │ semiorto_tecnico │ │ │ │ semiorto_tipologia_seme (N:1) │ └─────────────────────────────────────────────────────────────────────────┘
3. semiorto_analisi — schema completo
Questa tabella è il cuore di tutto. Contiene 61.998 record con tantissime colonne. Qui il DDL originale:
CREATE TABLE `semiorto_analisi` (
`id` int(11) NOT NULL AUTO_INCREMENT,
-- FK
`id_lotto` int(11) DEFAULT NULL,
`id_tecnico` int(11) DEFAULT NULL,
`id_tipologia_seme` int(11) DEFAULT NULL,
-- identificazione
`ft` varchar(45) DEFAULT NULL, -- numero fattura tecnica: "3561/20"
`lotto` varchar(20) DEFAULT NULL, -- copia del codice lotto (denormalizzato)
-- parametri prova
`prova` enum('PETRI','TORBA','CONTENITORI ALVEOLATI') DEFAULT NULL,
`tipologiaAnalisi` int(11) DEFAULT NULL, -- 1/2/3 = numero repliche programmate
`tipologiaSeme` enum('NATURA','SELEZIONATO','CALIBRATO',
'FILM COATED','PILLOLATO','TRATTATO') DEFAULT NULL,
-- copia denormalizzata del nome
`calibratura` varchar(45) DEFAULT NULL,
`pillole` int(11) DEFAULT NULL,
-- date
`data` date DEFAULT NULL, -- data chiusura analisi (NULL=in corso)
`dataArrivoLaboratorio` date DEFAULT NULL,
`dataInizio` date DEFAULT NULL,
-- parametri ambientali
`ur` int(11) DEFAULT NULL, -- umidità relativa %
`totaleGiorniDiProva` int(11) DEFAULT NULL,
-- riferimento analisi esterna (es. produttore)
`rifNumero` varchar(45) DEFAULT NULL,
`rifData` date DEFAULT NULL,
`rifGerminabilita` int(11) DEFAULT NULL,
`rifProvaLaboratorio` varchar(45) DEFAULT NULL,
`germinabilitaProvaLaboratorio` int(11) DEFAULT NULL,
-- esito
`scostamento` enum('REGOLARE','IRREGOLARE') DEFAULT NULL,
`risultato` enum('POSITIVO','NEGATIVO') DEFAULT NULL,
`vigore` enum('SCARSO','MEDIO','OTTIMO') DEFAULT NULL,
`germinabilita` int(11) DEFAULT NULL, -- VALORE FINALE calcolato
-- purezza
`semePuro` double DEFAULT '100',
`materialeInerte` double DEFAULT '0',
`altriSemi` double DEFAULT NULL,
`altriSemiString` varchar(512) DEFAULT NULL,
`osservazioniPurezza` varchar(512) DEFAULT NULL,
-- misc
`categoria` enum('STANDARD','COMMERCIALE') DEFAULT NULL,
`tecnico` varchar(60) DEFAULT NULL, -- copia denormalizzata del nome tecnico
`osservazioni` varchar(512) DEFAULT NULL,
`cancella` varchar(128) DEFAULT NULL, -- NOT NULL = analisi cancellata (soft delete)
PRIMARY KEY (`id`),
KEY (id_lotto, id_tecnico, id_tipologia_seme),
CONSTRAINT FOREIGN KEY (id_lotto) REFERENCES semiorto_lotto ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT FOREIGN KEY (id_tecnico) REFERENCES semiorto_tecnico ON DELETE CASCADE,
CONSTRAINT FOREIGN KEY (id_tipologia_seme) REFERENCES semiorto_tipologia_seme ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
I campi lotto, tipologiaSeme (ENUM), tecnico (varchar) sono COPIE del nome referenziato. Questo serve a mantenere lo storico in caso venga modificato il record originale. In TopSeed invece manteniamo solo le FK e risolviamo al bisogno.
4. semiorto_conte — schema
CREATE TABLE `semiorto_conte` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`analisi` int(11) NOT NULL, -- FK → semiorto_analisi
`lettera` varchar(2) DEFAULT 'A', -- ripetizione: A, B, C, D
-- 2 letture temporali: giorno 1 (prima conta) e giorno 2 (conta finale)
`giorni_1` int(11) DEFAULT NULL, -- es. 5 (giorno 5 dall'inizio)
`germinati_1` int(11) DEFAULT NULL, -- N semi germinati (su 100 seminati)
`duri_1` int(11) DEFAULT NULL, -- N semi duri
`freschi_1` int(11) DEFAULT NULL, -- N semi freschi (non germinati ma vivi)
`anormali_1` int(11) DEFAULT NULL, -- N plantule anormali
`morti_1` int(11) DEFAULT NULL, -- N semi morti
`giorni_2` int(11) DEFAULT NULL, -- es. 10 (giorno finale)
`germinati_2` int(11) DEFAULT NULL,
`duri_2` int(11) DEFAULT NULL,
`freschi_2` int(11) DEFAULT NULL,
`anormali_2` int(11) DEFAULT NULL,
`morti_2` int(11) DEFAULT NULL,
-- vigore visivo
`vigore_1` enum('SCARSO','MEDIO','OTTIMO') DEFAULT NULL,
`vigore_2` enum('SCARSO','MEDIO','OTTIMO') DEFAULT NULL,
PRIMARY KEY (id), UNIQUE KEY (id),
CONSTRAINT FOREIGN KEY (analisi) REFERENCES semiorto_analisi ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Per ciascun'analisi esistono tipicamente 4 conte (lettere A, B, C, D — una per ripetizione del test). La germinabilità finale è la media delle 4 conte (vedi Calcoli).
La somma di (germinati + duri + freschi + anormali + morti) in giorni_1 + giorni_2 = 100 (= i 100 semi piantati in ciascuna capsula Petri/pane di torba). Se l'input non torna, il sistema normalizza modificando germinati_2.
5. semiorto_lotto
CREATE TABLE `semiorto_lotto` (
`id_lotto` int(11) NOT NULL AUTO_INCREMENT, -- PK tecnica
`id` varchar(20) NOT NULL, -- CODICE (es. "VSF012/E-C")
`varieta` int(11) DEFAULT NULL, -- FK semiorto_varieta
`id_produttore` int(11) DEFAULT NULL, -- FK semiorto_produttore
`esaurito` tinyint(1) DEFAULT '0',
`provenienza` varchar(60) DEFAULT NULL,
`kg` double DEFAULT NULL,
`dataArrivo` date DEFAULT NULL,
`dataProssimaAnalisi` date DEFAULT NULL,
`dataEsaurito` date DEFAULT NULL,
PRIMARY KEY (id_lotto),
KEY (varieta), KEY (id_produttore),
CONSTRAINT FOREIGN KEY (varieta) REFERENCES semiorto_varieta ON DELETE CASCADE,
CONSTRAINT FOREIGN KEY (id_produttore) REFERENCES semiorto_produttore ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Il campo id (testuale) NON è UNIQUE nel DB. Se un lotto con lo stesso codice esiste già, il metodo salva() del service lancia avviso, l'utente può forzare con checkbox forza=true per creare un duplicato.
6. Anagrafiche
semiorto_specie (160)CREATE TABLE semiorto_specie ( id int PK, italiano varchar(60), inglese varchar(60), latino varchar(60), codice varchar(10), note varchar(512), giorni1Conta int, giorniTConta int, ur int -- UR target % );
semiorto_varieta (1.332)CREATE TABLE semiorto_varieta ( id int PK, nome varchar(60) NOT NULL, codice varchar(45), specie int FK );
semiorto_produttore (331)CREATE TABLE semiorto_produttore ( id int PK, nome TEXT NOT NULL, codice TEXT );
Tipo TEXT: insolito, normalmente sarebbe varchar. Risolto nel migrazione.
7. Mappa specie × mese × tipologia seme
CREATE TABLE `semiorto_map_specie_analisi_ripetizione_tipologia_seme` (
id int PK,
id_specie int FK → semiorto_specie.id,
id_analisi_ripetizione int FK → semiorto_analisi_ripetizione.id, -- mese 1..12
id_tipologia_seme int FK → semiorto_tipologia_seme.id,
UNIQUE KEY (id_specie, id_analisi_ripetizione)
);
Dice: "per la specie X, nel mese M, la tipologia di seme tipica è T". È usata dalla query "Da Analizzare" (vedi Flussi) per determinare quali lotti devono essere rianalizzati questo mese.
Esempio (inventato): specie=Pomodoro, mese=Maggio, tipologia=Selezionato → significa "in Maggio vanno rianalizzati tutti i lotti di pomodoro selezionato".
Il vincolo UNIQUE(id_specie, id_analisi_ripetizione) impone 1 tipologia per coppia (specie, mese): non sembra realmente così, forse solo una convenzione applicativa.
8. Modello auth
utente
id, username, password (SHA), email,
nome, cognome, attivo, cambiaPassword
permessi (join table)
id_utente → utente.id
id_permesso → permesso.id
permesso
id, permesso ('Amministrazione' | 'Laboratorio' | 'Magazzino')
permesso_menu (join table)
id_permesso → permesso.id
id_menu → menu.id
menu (albero)
id, id_padre (self-FK), valore, url, ordine
Combinando permesso_menu + menu si ottiene il menu alberato personalizzato per ruolo. Esempio: ruolo Laboratorio ha 22 voci abilitate (tutte le CRUD + analisi + Da Analizzare); ruolo Amministrazione ne ha 4 (solo utenti + cancellate).
Vedi pagina Ruoli & Menu per la mappa completa.
9. Vincoli di integrità
Tutti i foreign key sono ON DELETE CASCADE ON UPDATE CASCADE. Questo significa:
- Eliminare una specie → elimina tutte le sue varietà → tutti i lotti → tutte le analisi → tutte le conte
- Eliminare un produttore → elimina tutti i suoi lotti (e cascata successiva)
Una cancellazione accidentale di una specie potrebbe cancellare migliaia di record. In produzione si usa soft delete via campo cancella (solo su semiorto_analisi), ma le altre entità non hanno quel meccanismo.
10. Charset e aspetti fisici
- Charset DB:
latin1(ISO-8859-1, legacy). Tutte le tabelle. - Collation: default
latin1_swedish_ci. - Engine:
InnoDBper tutte. - AUTO_INCREMENT correnti (al dump):
semiorto_analisi=61998,semiorto_lotto=11138,semiorto_conte=109204.
La migrazione in TopSeed deve fare CONVERT(colonna USING utf8mb4) per ogni campo VARCHAR/TEXT. Senza questo step, i caratteri accentati italiani (è, à, ù) vengono corrotti.