Domanda

(Non correlato al controllo delle versioni dello schema del database)

Le applicazioni che si interfacciano con i database spesso hanno oggetti di dominio composti da dati di molte tabelle. Supponiamo che l'applicazione debba supportare il controllo delle versioni, nel senso di CVS, per questi oggetti di dominio.

Per alcuni oggetti di dominio di arbitraggio, come progetteresti uno schema di database per gestire questo requisito? Qualche esperienza da condividere?

È stato utile?

Soluzione

Rifletti attentamente sui requisiti per le revisioni. Una volta che la tua base di codice ha un monitoraggio pervasivo della cronologia integrato nel sistema operativo, diventerà molto complessa. Assicurazioni sottoscrizione sono particolarmente dannosi per questo, con schemi spesso in esecuzione in eccesso di 1000 tabelle. Le query tendono inoltre ad essere piuttosto complesse e questo può portare a problemi di prestazioni.

Se lo stato storico è realmente richiesto solo per i report, prendere in considerazione l'implementazione di un sistema transazionale "stato attuale" con una struttura di data warehouse sospesa sul retro per la cronologia di tracciamento. Le Dimensioni che cambiano lentamente sono una struttura molto più semplice per il monitoraggio dello stato storico rispetto al tentativo di incorporare una cronologia ad hoc meccanismo di tracciamento direttamente nel tuo sistema operativo.

Inoltre, Capture Data Changed è più semplice per un sistema a "stato attuale" con modifiche in corso ai record in atto: le chiavi primarie dei record non cambiano, quindi non è necessario abbinare i record che tengono insieme versioni diverse della stessa entità. Un efficace meccanismo CDC renderà un processo di caricamento del magazzino incrementale abbastanza leggero e possibile eseguirlo abbastanza frequentemente. Se non hai bisogno di un monitoraggio al minuto dello stato storico (quasi, ma non del tutto, e ossimoro), questa può essere una soluzione efficace con una base di codice molto più semplice di un meccanismo di tracciamento della cronologia completo incorporato direttamente nell'applicazione.

Altri suggerimenti

Una tecnica che ho usato per questo in passato è stata quella di avere un concetto di "generazioni" nel database, ogni modifica incrementa il numero di generazione corrente per il database: se si utilizza la sovversione, pensare alle revisioni. A ogni record sono associati 2 numeri di generazione (2 colonne extra sulle tabelle): la generazione per cui il record inizia a essere valido e la generazione per cui smette di essere valida. Se i dati sono attualmente validi, il secondo numero sarebbe NULL o qualche altro marker generico.

Quindi, per inserire nel database:

  1. incrementa il numero di generazione
  2. inserisci i dati
  3. tagga la durata di tali dati con valido da e valido a di NULL

Se stai aggiornando alcuni dati:

  1. contrassegna tutti i dati che stanno per essere modificati come validi al numero di generazione corrente
  2. incrementa il numero di generazione
  3. inserisci i nuovi dati con il numero di generazione corrente

l'eliminazione è solo una questione di contrassegnare i dati come terminanti alla generazione corrente.

Per ottenere una versione particolare dei dati, trova quale generazione stai cercando e cerca dati validi tra quelle versioni di generazione.

Esempio:

Crea una persona.

|Name|D.O.B  |Telephone|From|To  |
|Fred|1 april|555-29384|1   |NULL|

Aggiorna n. tel.

|Name|D.O.B  |Telephone|From|To  |
|Fred|1 april|555-29384|1   |1   |
|Fred|1 april|555-43534|2   |NULL|

Elimina fred:

|Name|D.O.B  |Telephone|From|To  |
|Fred|1 april|555-29384|1   |1   |
|Fred|1 april|555-43534|2   |2   |

Un'alternativa al controllo delle versioni rigoroso è di dividere i dati in 2 tabelle: corrente e cronologia.

La tabella corrente contiene tutti i dati in tempo reale e presenta i vantaggi di tutte le prestazioni in cui è stato creato. Qualsiasi modifica per prima cosa scrive i dati correnti nella "cronologia" associata tabella insieme a un indicatore di data che indica quando è cambiato.

Se si utilizza Hibernate JBoss Envers potrebbe essere un'opzione. Devi solo annotare le classi con @Audited per conservare la loro cronologia.

È necessario un record principale in una tabella principale che contenga le informazioni comuni a tutte le versioni.

Quindi ogni tabella figlio utilizza l'ID record master + versione no come parte della chiave primaria.

Può essere fatto senza la tabella principale, ma nella mia esperienza tenderà a rendere le istruzioni SQL molto più complicate.

Un modo semplice a prova di errore è aggiungere una colonna di versione alle tabelle e archiviare la versione dell'Oggetto e scegliere la logica dell'applicazione appropriata in base a quel numero di versione. In questo modo si ottiene anche la compatibilità con le versioni precedenti a un costo contenuto. Il che è sempre positivo

ZoDB + ZEO implementa un database basato su revisione con rollback completo per qualsiasi supporto temporizzato. Vai a controllarlo.

Parte negativa: è legato a Zope.

Una volta che un oggetto viene salvato in un database, possiamo modificarlo un numero qualsiasi di volte, Se vogliamo sapere quante volte non viene modificato un oggetto, allora dobbiamo applicare questo concetto di versioning.

Ogni volta che usiamo il versioning, quindi l'ibernazione inserisce il numero di versione come zero, quando mai l'oggetto viene salvato per la prima volta nel database. Successivamente l'ibernazione incrementa automaticamente quella versione no di uno ogni volta che viene apportata una modifica su quel particolare oggetto. Per utilizzare questo concetto di versioning, abbiamo bisogno delle seguenti due modifiche nella nostra applicazione

Add one property of type int in our pojo class.

In hibernate mapping file, add an element called version soon after id element

Non sono sicuro di avere lo stesso problema, ma ho richiesto un gran numero di modifiche "proposte" all'attuale set di dati (con proposte concatenate, ovvero proposta su proposta).

Pensa alla ramificazione nel controllo del codice sorgente ma per le tabelle del database.

Volevamo anche un registro storico, ma questo era il fattore meno importante: il problema principale era la gestione delle proposte di modifica che potevano rimanere in sospeso per 6 mesi o più mentre l'attività rimuginava sull'approvazione della modifica e si preparava all'implementazione effettiva della modifica .

L'idea è che gli utenti possano caricare una modifica e iniziare a creare, modificare, eliminare lo stato corrente dei dati senza applicare effettivamente tali modifiche. Annulla le modifiche eventualmente apportate o annulla l'intera modifica.

L'unico modo in cui sono stato in grado di raggiungere questo obiettivo è di avere una serie di campi comuni sulle mie tabelle con versione:

ID radice : obbligatorio: impostato una volta sulla chiave primaria quando viene creata la prima versione di un record. Questo rappresenta la chiave primaria in ogni momento e viene copiato in ogni versione del record. Dovresti prendere in considerazione l'ID radice quando assegni un nome alle colonne della relazione (ad es. PARENT_ROOT_ID anziché PARENT_ID). Poiché l'ID radice è anche la chiave primaria della versione iniziale, è possibile creare chiavi esterne rispetto alla chiave primaria effettiva: la riga desiderata effettiva sarà determinata dai filtri di versione definiti di seguito.

ID modifica : obbligatorio: ogni record viene creato, aggiornato, eliminato tramite una modifica

Copia da ID : Nullable: null indica il record appena creato, non-null indica da quale ID record è stata clonata questa riga durante l'aggiornamento

In vigore dalla data / ora : Nullable: null indica il record proposto, non null indica quando il record è diventato corrente. Sfortunatamente un indice univoco non può essere posizionato su Root ID / Effective From poiché possono esserci più valori null per qualsiasi ID root. (A meno che tu non voglia limitarti a una singola modifica proposta per record)

In vigore a data / ora : Nullable: null indica corrente / proposta, non null indica quando è diventato storico. Non tecnicamente richiesto, ma aiuta ad accelerare le query per trovare i dati correnti. Questo campo può essere corrotto da modifiche manuali, ma può essere ricostruito dalla data / ora effettiva dalla data di inizio.

Elimina flag : booleano: impostato su true quando viene proposto che il record venga eliminato quando diventa corrente. Quando le eliminazioni sono impegnate, la loro data / ora effettiva è impostata sullo stesso valore della data / ora effettiva da, filtrandole dal set di dati corrente.

La query per ottenere lo stato attuale dei dati in base a una modifica sarebbe;

SELECT * FROM table WHERE (CHANGE_ID IN :ChangeId OR (EFFECTIVE_FROM <= :Now AND (EFFECTIVE_TO IS NULL OR EFFECTIVE_TO > :Now) AND ROOT_ID NOT IN (SELECT ROOT_ID FROM table WHERE CHANGE_ID IN :ChangeId)))

(Il filtraggio dei multipli change-on-change viene eseguito al di fuori di questa query).

La query per ottenere lo stato attuale dei dati in un determinato momento sarebbe;

SELECT * FROM table WHERE EFFECTIVE_FROM <= :Now AND (EFFECTIVE_TO IS NULL OR EFFECTIVE_TO > :Now)

Indici comuni creati su (ROOT_ID, EFFECTIVE_FROM), (EFFECTIVE_FROM, EFFECTIVE_TO) e (CHANGE_ID).

Se qualcuno conosce una soluzione migliore, mi piacerebbe sentirne parlare.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top