Persistenza degli oggetti e Java

La durabilità degli oggetti, o persistenza , è il termine che si sente spesso usato insieme al problema della memorizzazione di oggetti nei database. La persistenza dovrebbe operare con integrità transazionale e come tale è soggetta a condizioni rigorose. (Vedere la sezione Risorse di questo articolo per ulteriori informazioni sull'elaborazione delle transazioni.) Al contrario, i servizi linguistici offerti tramite librerie e pacchetti di linguaggio standard sono spesso liberi da vincoli transazionali.

Come vedremo in questo articolo, l'evidenza suggerisce che la semplice persistenza di Java deriverà probabilmente dal linguaggio stesso, mentre sofisticate funzionalità di database saranno offerte dai fornitori di database.

Nessun oggetto è un'isola

Nel mondo reale, raramente trovi un oggetto che non abbia relazioni con altri oggetti. Gli oggetti sono componenti dei modelli di oggetti . La questione della durabilità degli oggetti trascende la questione della durabilità e distribuzione del modello a oggetti una volta che abbiamo osservato che gli oggetti sono interconnessi in virtù delle loro relazioni tra loro.

L'approccio relazionale all'archiviazione dei dati tende ad aggregare i dati per tipo. Le righe in una tabella rappresentano l'aggregazione fisica di oggetti dello stesso tipo su disco. Le relazioni tra gli oggetti vengono quindi rappresentate da chiavi condivise tra molte tabelle. Sebbene attraverso l'organizzazione del database, i database relazionali talvolta consentano alle tabelle che è probabile che vengano utilizzate insieme di essere collocate (o raggruppate ) nella stessa partizione logica, come un segmento di database, non hanno alcun meccanismo per memorizzare le relazioni tra gli oggetti nel database. Pertanto, per costruire un modello a oggetti, queste relazioni vengono costruite dalle chiavi esistenti in fase di esecuzione in un processo denominato join di tabella . Questa è la stessa ben nota proprietà dei database relazionali chiamatiindipendenza dai dati . Quasi tutte le varianti dei database a oggetti offrono alcuni meccanismi per migliorare le prestazioni di un sistema che coinvolge complesse relazioni tra oggetti rispetto ai database relazionali tradizionali.

Per interrogare o per navigare?

Nella memorizzazione di oggetti su disco, ci troviamo di fronte alla scelta di co-localizzare oggetti correlati per meglio adattare l'accesso alla navigazione, o per memorizzare oggetti in raccolte simili a tabelle che aggregano oggetti per tipo per facilitare l'accesso basato su predicati (query), o entrambi . La co-locazione degli oggetti nella memoria persistente è un'area in cui i database relazionali e quelli orientati agli oggetti differiscono ampiamente. La scelta del linguaggio di query è un'altra area di considerazione. SQL (Structured Query Language) e le sue estensioni hanno fornito ai sistemi relazionali un meccanismo di accesso basato sui predicati. Object Query Language (OQL) è una variante oggetto di SQL, standardizzata da ODMG, ma il supporto per questo linguaggio è attualmente scarso. I metodi polimorfici offrono un'eleganza senza precedenti nella costruzione di una query semantica per una raccolta di oggetti. Per esempio,immagina un comportamento polimorfico peracccountchiamato isInGoodStanding. Può restituire il valore booleano true per tutti gli account in regola e false in caso contrario. Ora immagina l'eleganza di interrogare la raccolta di account, dove inGoodStandingviene implementata in modo diverso in base alle regole aziendali, per tutti gli account in regola. Potrebbe assomigliare a:

setOfGoodCustomers = setOfAccounts.query(account.inGoodStanding());

Sebbene molti dei database di oggetti esistenti siano in grado di elaborare uno stile di query di questo tipo in C ++ e Smalltalk, è difficile per loro farlo per raccolte più grandi (ad esempio, 500+ gigabyte) ed espressioni di query più complesse. Diverse società di database relazionali, come Oracle e Informix, offriranno presto un'altra sintassi basata su SQL per ottenere lo stesso risultato.

Persistenza e tipo

Un appassionato di linguaggi orientati agli oggetti direbbe che la persistenza e il tipo sono proprietà ortogonali di un oggetto; ovvero, oggetti persistenti e transitori dello stesso tipo possono essere identici perché una proprietà non dovrebbe influenzare l'altra. La vista alternativa sostiene che la persistenza è un comportamento supportato solo da oggetti persistenti e alcuni comportamenti possono essere applicati solo a oggetti persistenti. Il secondo approccio richiede metodi che istruiscono gli oggetti persistenti a memorizzare e recuperare se stessi dalla memoria persistente, mentre il primo offre all'applicazione una vista senza interruzioni dell'intero modello a oggetti, spesso estendendo il sistema di memoria virtuale.

Canonicalizzazione e indipendenza dal linguaggio

Gli oggetti dello stesso tipo in una lingua devono essere archiviati in una memoria persistente con lo stesso layout, indipendentemente dall'ordine in cui vengono visualizzate le loro interfacce. I processi di trasformazione del layout di un oggetto in questo formato comune sono noti collettivamente come canonicalizzazione della rappresentazione degli oggetti. Nei linguaggi compilati con la tipizzazione statica (non Java) gli oggetti scritti nella stessa lingua, ma compilati su sistemi differenti, dovrebbero essere rappresentati in modo identico nella memoria persistente.

Un'estensione della canonicalizzazione riguarda la rappresentazione degli oggetti indipendente dalla lingua. Se gli oggetti possono essere rappresentati in modo indipendente dalla lingua, sarà possibile che diverse rappresentazioni dello stesso oggetto condividano la stessa memoria persistente.

Un meccanismo per eseguire questa attività consiste nell'introdurre un ulteriore livello di riferimento indiretto tramite un IDL (Interface Definition Language). Le interfacce del database degli oggetti possono essere realizzate tramite IDL e le strutture dati corrispondenti. Lo svantaggio dei collegamenti in stile IDL è duplice: primo, il livello aggiuntivo di riferimento indiretto richiede sempre un livello aggiuntivo di traduzione, che influisce sulle prestazioni generali del sistema; in secondo luogo, limita l'uso di servizi di database che sono unici per fornitori particolari e che potrebbero essere preziosi per gli sviluppatori di applicazioni.

Un meccanismo simile consiste nel supportare i servizi a oggetti tramite un'estensione dell'SQL. I fornitori di database relazionali e i fornitori di oggetti / relazionali più piccoli sono i fautori di questo approccio; Tuttavia, resta da vedere quanto successo avranno queste aziende nel dare forma alla struttura per l'archiviazione di oggetti.

Ma la domanda rimane: la persistenza dell'oggetto fa parte del comportamento dell'oggetto o è un servizio esterno offerto agli oggetti tramite interfacce separate? Che ne dici di raccolte di oggetti e metodi per interrogarli? Gli approcci relazionali, relazionali estesi e oggetto / relazionali tendono a sostenere una separazione tra il linguaggio, mentre i database a oggetti - e lo stesso linguaggio Java - vedono la persistenza come intrinseca al linguaggio.

Persistenza Java nativa tramite serializzazione

La serializzazione degli oggetti è il meccanismo specifico del linguaggio Java per la memorizzazione e il richiamo di oggetti e primitive Java nei flussi. È degno di nota che sebbene le librerie commerciali di terze parti per la serializzazione di oggetti C ++ esistano da tempo, C ++ non ha mai offerto un meccanismo nativo per la serializzazione degli oggetti. Ecco come utilizzare la serializzazione di Java:

// Scrivere "foo" in uno stream (ad esempio, un file)

// Passaggio 1. Creare un flusso di output

// ovvero, crea un bucket per ricevere i byte

FileOutputStream out = nuovo FileOutputStream ("fooFile");

// Passaggio 2. Crea ObjectOutputStream

// cioè, crea un tubo e metti la sua testa nel secchio

ObjectOutputStream os = nuovo ObjectOutputStream (out)

// Passaggio 3. Scrivere una stringa e un oggetto nel flusso

// ovvero, lascia che il flusso scorra nel bucket

os.writeObject ("foo");

os.writeObject (new Foo ());

// Passaggio 4. Scarica i dati nella loro destinazione

os.flush ();

Il Writeobjectmetodo serializza foo e la sua chiusura transitiva, ovvero tutti gli oggetti a cui è possibile fare riferimento da foo all'interno del grafico. All'interno del flusso esiste solo una copia dell'oggetto serializzato. Altri riferimenti agli oggetti vengono memorizzati come handle dell'oggetto per risparmiare spazio ed evitare riferimenti circolari. L'oggetto serializzato inizia con la classe seguita dai campi di ogni classe nella gerarchia di ereditarietà.

// Lettura di un oggetto da un flusso

// Passaggio 1. Creare un flusso di input

FileInputStream in = nuovo FileInputStream ("fooFile");

// Passaggio 2. Creare un flusso di input dell'oggetto

ObjectInputStream ins = nuovo ObjectInputStream (in);

// Passaggio 3. Devo sapere cosa stai leggendo

String fooString = (String) ins.readObject ();

Foo foo = (Foo) s.readObject ();

Serializzazione e sicurezza degli oggetti

Per impostazione predefinita, la serializzazione scrive e legge i campi non statici e non temporanei dal flusso. Questa caratteristica può essere utilizzata come meccanismo di sicurezza dichiarando i campi che non possono essere serializzati come transitori privati. Se una classe non può essere serializzato a tutti, writeObjecte readObjectmetodi dovrebbero essere attuate per lanciare NoAccessException.

Persistenza con integrità transazionale: presentazione di JDBC

Modellato su SQL CLI (Client Level Interface) di X / Open e astrazioni ODBC di Microsoft, Java database connectivity (JDBC) mira a fornire un meccanismo di connettività database indipendente dal sistema di gestione database sottostante (DBMS). è necessario supportare almeno l'API entry-level ANSI SQL-2, che offre ai fornitori di strumenti e alle applicazioni di terze parti una flessibilità sufficiente per l'accesso al database.

JDBC è progettato per essere coerente con il resto del sistema Java. I fornitori sono incoraggiati a scrivere un'API con una tipizzazione più forte di ODBC, che offre un maggiore controllo statico del tipo in fase di compilazione.

Ecco una descrizione delle interfacce JDBC più importanti:

  • java.sql.Driver.Manager gestisce il caricamento dei driver e fornisce supporto per nuove connessioni al database.

  • java.sql.Connection rappresenta una connessione a un database particolare.

  • java.sql.Statement funge da contenitore per l'esecuzione di un'istruzione SQL su una determinata connessione.

  • java.sql.ResultSet controlla l'accesso al set di risultati.

È possibile implementare un driver JDBC in diversi modi. Il più semplice sarebbe creare il driver come bridge per ODBC. Questo approccio è più adatto per strumenti e applicazioni che non richiedono prestazioni elevate. Un design più estensibile introdurrebbe un ulteriore livello di riferimento indiretto al server DBMS fornendo un driver di rete JDBC che accede al server DBMS tramite un protocollo pubblicato. Il driver più efficiente, tuttavia, accederà direttamente all'API proprietaria del DBMS.

Database di oggetti e persistenza Java

Numerosi progetti in corso nel settore offrono la persistenza Java a livello di oggetto. Tuttavia, al momento della stesura di questo documento, PSE (Persistent Storage Engine) e PSE Pro di Object Design sono gli unici pacchetti di database orientati agli oggetti completamente basati su Java disponibili (almeno, di cui sono a conoscenza). Controlla la sezione Risorse per ulteriori informazioni su PSE e PSE Pro.

Lo sviluppo Java ha portato a un allontanamento dal paradigma di sviluppo tradizionale per i fornitori di software, in particolare nella sequenza temporale del processo di sviluppo. Ad esempio, PSE e PSE Pro sono sviluppati in un ambiente eterogeneo. E poiché non esiste una fase di collegamento nel processo di sviluppo, gli sviluppatori sono stati in grado di creare vari componenti funzionali indipendenti l'uno dall'altro, il che si traduce in un codice orientato agli oggetti migliore e più affidabile.

PSE Pro ha la capacità di ripristinare un database danneggiato da una transazione interrotta causata da un errore di sistema. Le classi responsabili di questa funzionalità aggiuntiva non sono presenti nella versione PSE. Non esistono altre differenze tra i due prodotti. Questi prodotti sono ciò che chiamiamo "dribbleware", versioni software che migliorano la loro funzionalità inserendo nuovi componenti. In un futuro non così lontano, il concetto di acquisto di software monolitico di grandi dimensioni sarebbe diventato un ricordo del passato. Il nuovo ambiente aziendale nel cyberspazio, insieme al Java computing, consente agli utenti di acquistare solo le parti del modello a oggetti (grafo a oggetti) di cui hanno bisogno, ottenendo prodotti finali più compatti.

PSE funziona post-elaborazione e annotando i file di classe dopo che sono stati creati dallo sviluppatore. Dal punto di vista di PSE, le classi in un grafo a oggetti possono essere persistenti o persistenti. Le classi persistenti possono persistere da sole mentre le classi persistenti possono operare su oggetti persistenti. Questa distinzione è necessaria perché la persistenza potrebbe non essere un comportamento desiderato per determinate classi. Il post-processore del file di classe apporta le seguenti modifiche alle classi: