Modello dati

Entità JPA, relazioni, schema DB reale e vincoli di integrità

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à JavaTabella DBRecord (live)Funzione
SemiortoAnalisisemiorto_analisi61.998Il cuore: una prova di germinabilità eseguita su un lotto
SemiortoContesemiorto_conte109.204Letture giornaliere delle ripetizioni di una prova (A, B, C, D)
SemiortoLottosemiorto_lotto11.138Un lotto di seme ricevuto in laboratorio
SemiortoSpeciesemiorto_specie160Specie vegetale (Pomodoro, Peperone, …)
SemiortoVarietasemiorto_varieta1.332Varietà di una specie
SemiortoProduttoresemiorto_produttore331Fornitore / produttore del seme
SemiortoTecnicosemiorto_tecnico4Persona fisica che esegue le analisi
SemiortoTipologiaSemesemiorto_tipologia_seme9Portaseme, Natura, Selezionato, Calibrato, Film coated, Pillolato, Trattato, Multiseme, Vigorizzato
SemiortoAnalisiRipetizionesemiorto_analisi_ripetizione12I 12 mesi dell'anno (nome fuorviante, non sono tipi di prova)
SemiortoMapSpecieAnalisiRipetizioneTipologiaSemesemiorto_map_...~200Matrice di fattibilità: quale mese si può fare analisi su quale specie+tipologia
Utenteutente5Account
Permessopermesso3Ruoli: Amministrazione, Laboratorio, Magazzino
PermessipermessiJoin table utente ↔ permesso (M:N)
Menumenu20Voci menu alberate (con id_padre)
PermessoMenupermesso_menu~40Quale ruolo vede quale voce menu
SemiortoErrorisemiorto_erroriLog 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;
📌 Campi denormalizzati

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;
📌 Chiave "codice" non univoca

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)
);
🔑 Semantic della tabella

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:

⚠ Rischio operativo

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

Gestione charset nella migrazione

La migrazione in TopSeed deve fare CONVERT(colonna USING utf8mb4) per ogni campo VARCHAR/TEXT. Senza questo step, i caratteri accentati italiani (è, à, ù) vengono corrotti.