Java Data Objects (JDO) è un'interfaccia standard per l'accesso ai database in Java, che fornisce una mappatura tra le classi Java e le tabelle di database. È disponibile un plug-in open source per l'utilizzo di JDO con Datastore e questa pagina fornisce informazioni su come iniziare a utilizzarlo.
Avviso:riteniamo che la maggior parte degli sviluppatori avrà un'esperienza migliore utilizzando l'API Datastore di basso livello o una delle API open source sviluppate appositamente per Datastore, come Objectify. JDO è stato progettato per l'utilizzo con database relazionali tradizionali e quindi non ha modo di rappresentare esplicitamente alcuni aspetti di Datastore che lo rendono diverso dai database relazionali, come i gruppi di entità e le query di antenati. Ciò può portare a problemi sottili difficili da comprendere e risolvere.
L'SDK Java di App Engine include un'implementazione di JDO 2.3 per App Engine Datastore. L'implementazione si basa sulla versione 1.0 della piattaforma di accesso DataNucleus, l'implementazione di riferimento open source per JDO 2.3.
Nota: le istruzioni riportate in questa pagina si applicano a JDO versione 2.3, che utilizza la versione 1.0 del plug-in DataNucleus per App Engine. App Engine ora offre il plug-in DataNucleus 2.x che ti consente di eseguire JDO 3.0. Il nuovo plug-in supporta le relazioni non di proprietà e fornisce una serie di nuove API e funzionalità. Questo upgrade non è completamente compatibile con la versione precedente. Se ricompili un'applicazione utilizzando JDO 3.0, devi aggiornare e testare nuovamente il codice. Per saperne di più sulla nuova versione, consulta JDO 3.0. Per saperne di più sull'upgrade, consulta Utilizzo di JDO 3.0 con App Engine.
Configurare JDO 2.3
Per utilizzare JDO per accedere al datastore, un'app App Engine deve soddisfare i seguenti requisiti:
- I file JAR del plug-in JDO e DataNucleus App Engine devono trovarsi nella directory
war/WEB-INF/lib/dell'app. - Un file di configurazione denominato
jdoconfig.xmldeve trovarsi nella directorywar/WEB-INF/classes/META-INF/dell'app, con una configurazione che indica a JDO di utilizzare il datastore App Engine. - La processo di compilazione del progetto deve eseguire un passaggio di "miglioramento " post-compilazione sulle classi di dati compilate per associarle all'implementazione JDO.
Copia dei file JAR
I file JAR JDO e datastore sono inclusi nell'SDK Java di App Engine. Puoi trovarli nella directory appengine-java-sdk/lib/user/orm/.
Copia i file JAR nella directory war/WEB-INF/lib/
dell'applicazione.
Assicurati che appengine-api.jar si trovi anche nella directory
war/WEB-INF/lib/. (Potresti averlo già copiato durante la
creazione del progetto.) Il plug-in DataNucleus di App Engine utilizza questo file JAR per
accedere al datastore.
Creazione del file jdoconfig.xml
L'interfaccia JDO richiede un file di configurazione denominato jdoconfig.xml
nella directory war/WEB-INF/classes/META-INF/ dell'applicazione. Puoi
creare questo file direttamente in questa posizione o fare in modo che laprocesso di compilazioned lo copi
da una directory di origine.
Crea il file con i seguenti contenuti:
<?xml version="1.0" encoding="utf-8"?> <jdoconfig xmlns="http://java.sun.com/xml/ns/jdo/jdoconfig" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://java.sun.com/xml/ns/jdo/jdoconfig"> <persistence-manager-factory name="transactions-optional"> <property name="javax.jdo.PersistenceManagerFactoryClass" value="org.datanucleus.store.appengine.jdo.DatastoreJDOPersistenceManagerFactory"/> <property name="javax.jdo.option.ConnectionURL" value="appengine"/> <property name="javax.jdo.option.NontransactionalRead" value="true"/> <property name="javax.jdo.option.NontransactionalWrite" value="true"/> <property name="javax.jdo.option.RetainValues" value="true"/> <property name="datanucleus.appengine.autoCreateDatastoreTxns" value="true"/> </persistence-manager-factory> </jdoconfig>
Impostazione di Datastore Read Policy e Call Deadline
Come descritto nella pagina Query Datastore, puoi personalizzare il comportamento di Datastore impostando la criterio per la lettura (coerenza forte o finale) e il termine della chiamata. In JDO, puoi
specificare i valori desiderati nell'elemento
<persistence-manager-factory> del file
jdoconfig.xml. Tutte le chiamate effettuate con una determinata istanza PersistenceManager utilizzeranno i valori di configurazione in vigore al momento della creazione del gestore da parte di PersistenceManagerFactory. Puoi anche eseguire l'override di queste impostazioni per
un singolo oggetto Query.
Per impostare la criterio per la lettura per un PersistenceManagerFactory, includi
una proprietà denominata datanucleus.appengine.datastoreReadConsistency.
I valori possibili sono EVENTUAL e STRONG: se non specificato, il valore predefinito è STRONG. Tieni presente che queste
impostazioni si applicano solo alle query ancestor all'interno di un determinato gruppo di entità.
Le query non di tipo discendente e tra gruppi sono sempre coerenti alla fine, indipendentemente dalla
criterio per la letturaa prevalente.
<property name="datanucleus.appengine.datastoreReadConsistency" value="EVENTUAL" />
Puoi impostare scadenze separate per le chiamate al datastore per le letture e per le scritture. Per le
letture, utilizza la proprietà standard JDO
javax.jdo.option.DatastoreReadTimeoutMillis. Per le scritture, utilizza
javax.jdo.option.DatastoreWriteTimeoutMillis. Il valore è un
periodo di tempo, in millisecondi.
<property name="javax.jdo.option.DatastoreReadTimeoutMillis" value="5000" /> <property name="javax.jdo.option.DatastoreWriteTimeoutMillis" value="10000" />
Se vuoi utilizzare transazioni cross-group (XG), aggiungi la seguente proprietà:
<property name="datanucleus.appengine.datastoreEnableXGTransactions" value="true" />
Puoi avere più elementi <persistence-manager-factory>
nello stesso file jdoconfig.xml, utilizzando attributi
name diversi, per utilizzare istanze PersistenceManager
con configurazioni diverse nella stessa app. Ad esempio, il seguente
file jdoconfig.xml stabilisce due set di configurazione, uno denominato
"transactions-optional" e un altro denominato
"eventual-reads-short-deadlines":
<?xml version="1.0" encoding="utf-8"?> <jdoconfig xmlns="http://java.sun.com/xml/ns/jdo/jdoconfig" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://java.sun.com/xml/ns/jdo/jdoconfig"> <persistence-manager-factory name="transactions-optional"> <property name="javax.jdo.PersistenceManagerFactoryClass" value="org.datanucleus.store.appengine.jdo.DatastoreJDOPersistenceManagerFactory"/> <property name="javax.jdo.option.ConnectionURL" value="appengine"/> <property name="javax.jdo.option.NontransactionalRead" value="true"/> <property name="javax.jdo.option.NontransactionalWrite" value="true"/> <property name="javax.jdo.option.RetainValues" value="true"/> <property name="datanucleus.appengine.autoCreateDatastoreTxns" value="true"/> </persistence-manager-factory> <persistence-manager-factory name="eventual-reads-short-deadlines"> <property name="javax.jdo.PersistenceManagerFactoryClass" value="org.datanucleus.store.appengine.jdo.DatastoreJDOPersistenceManagerFactory"/> <property name="javax.jdo.option.ConnectionURL" value="appengine"/> <property name="javax.jdo.option.NontransactionalRead" value="true"/> <property name="javax.jdo.option.NontransactionalWrite" value="true"/> <property name="javax.jdo.option.RetainValues" value="true"/> <property name="datanucleus.appengine.autoCreateDatastoreTxns" value="true"/> <property name="datanucleus.appengine.datastoreReadConsistency" value="EVENTUAL" /> <property name="javax.jdo.option.DatastoreReadTimeoutMillis" value="5000" /> <property name="javax.jdo.option.DatastoreWriteTimeoutMillis" value="10000" /> </persistence-manager-factory> </jdoconfig>
Per informazioni sulla creazione di un PersistenceManager con un insieme di configurazioni denominato, vedi Ottenere un'istanza PersistenceManager di seguito.
Miglioramento delle classi di dati
JDO utilizza un passaggio di "miglioramento" post-compilazione nel processo di compilazione per associare le classi di dati all'implementazione JDO.
Puoi eseguire il passaggio di miglioramento sulle classi compilate dalla riga di comando con il seguente comando:
java -cp classpath com.google.appengine.tools.enhancer.Enhance class-files
Il classpath deve contenere il file JAR
appengine-tools-api.jar dalla directory
appengine-java-sdk/lib/, nonché tutte le classi di dati.
Per saperne di più su DataNucleus bytecode enhancer, consulta la documentazione di DataNucleus.
Ottenere un'istanza di PersistenceManager
Un'app interagisce con JDO utilizzando un'istanza della classe PersistenceManager. Ottieni questa istanza creando un'istanza e chiamando un metodo su un'istanza della classe PersistenceManagerFactory. La factory utilizza la configurazione JDO per creare istanze PersistenceManager.
Poiché l'inizializzazione di un'istanza PersistenceManagerFactory richiede tempo, un'app deve riutilizzare una singola istanza. Per applicare questa regola, viene generata un'eccezione se l'app crea più di una PersistenceManagerFactory (con lo stesso nome di configurazione). Un modo semplice per gestire l'istanza PersistenceManagerFactory è creare una classe wrapper singleton con un'istanza statica, come segue:
PMF.java
import javax.jdo.JDOHelper; import javax.jdo.PersistenceManagerFactory; public final class PMF { private static final PersistenceManagerFactory pmfInstance = JDOHelper.getPersistenceManagerFactory("transactions-optional"); private PMF() {} public static PersistenceManagerFactory get() { return pmfInstance; } }
Suggerimento:"transactions-optional" si riferisce al
nome della configurazione impostata nel file jdoconfig.xml. Se la tua
app utilizza più set di configurazione, dovrai estendere questo codice per chiamare
JDOHelper.getPersistenceManagerFactory() come preferisci. Il codice
deve memorizzare nella cache un'istanza singleton di ogni
PersistenceManagerFactory.
L'app utilizza l'istanza di fabbrica per creare un'istanza PersistenceManager per ogni richiesta che accede al datastore.
import javax.jdo.JDOHelper; import javax.jdo.PersistenceManager; import javax.jdo.PersistenceManagerFactory; import PMF; // ... PersistenceManager pm = PMF.get().getPersistenceManager();
Utilizzi PersistenceManager per archiviare, aggiornare ed eliminare gli oggetti dati ed eseguire query datastore.
Al termine dell'utilizzo dell'istanza PersistenceManager, devi chiamare il metodo
close(). È un errore utilizzare l'istanza PersistenceManager
dopo aver chiamato il relativo metodo close().
try { // ... do stuff with pm ... } finally { pm.close(); }
Funzionalità non supportate di JDO 2.3
Le seguenti funzionalità dell'interfaccia JDO non sono supportate dall'implementazione di App Engine:
- Relazioni non di proprietà. Puoi implementare relazioni non di proprietà utilizzando valori chiave espliciti. La sintassi di JDO per le relazioni non proprietarie potrebbe essere supportata in una release futura.
- Relazioni many-to-many di proprietà.
- Query "Join". Non puoi utilizzare un campo di un'entità secondaria in un filtro quando esegui una query sul tipo principale. Tieni presente che puoi testare il campo relazione genitore direttamente nella query utilizzando una chiave.
- Raggruppamento JDOQL e altre query di aggregazione.
- Query polimorfiche. Non puoi eseguire una query di una classe per ottenere istanze di una sottoclasse. Ogni classe è rappresentata da un tipo di entità separato nel datastore.
IdentityType.DATASTOREper l'annotazione@PersistenceCapable. È supportato soloIdentityType.APPLICATION.- Al momento è presente un bug che impedisce le relazioni one-to-many di proprietà in cui il genitore e il figlio sono la stessa classe, rendendo difficile la modellazione di strutture ad albero. Questo problema verrà risolto in una release futura. Puoi aggirare questo problema memorizzando valori chiave espliciti per il genitore o i figli.
Disattivazione delle transazioni e porting delle app JDO esistenti
La configurazione JDO che ti consigliamo di utilizzare imposta una proprietà denominata
datanucleus.appengine.autoCreateDatastoreTxns su true.
Si tratta di una proprietà specifica di App Engine che indica all'implementazione JDO di associare le transazioni del datastore alle transazioni JDO gestite nel codice dell'applicazione. Se stai creando una nuova app da zero, probabilmente
è quello che ti serve. Tuttavia, se hai un'applicazione esistente basata su JDO che vuoi eseguire su App Engine, potresti voler utilizzare una configurazione di persistenza alternativa che imposta il valore di questa proprietà su false:
<?xml version="1.0" encoding="utf-8"?> <jdoconfig xmlns="http://java.sun.com/xml/ns/jdo/jdoconfig" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://java.sun.com/xml/ns/jdo/jdoconfig"> <persistence-manager-factory name="transactions-optional"> <property name="javax.jdo.PersistenceManagerFactoryClass" value="org.datanucleus.store.appengine.jdo.DatastoreJDOPersistenceManagerFactory"/> <property name="javax.jdo.option.ConnectionURL" value="appengine"/> <property name="javax.jdo.option.NontransactionalRead" value="true"/> <property name="javax.jdo.option.NontransactionalWrite" value="true"/> <property name="javax.jdo.option.RetainValues" value="true"/> <property name="datanucleus.appengine.autoCreateDatastoreTxns" value="false"/> </persistence-manager-factory> </jdoconfig>
Per capire perché questa operazione può essere utile, ricorda che puoi operare solo su oggetti che appartengono allo stesso gruppo di entità all'interno di una transazione. Le applicazioni create utilizzando database tradizionali in genere presuppongono la disponibilità di transazioni globali, che consentono di aggiornare qualsiasi insieme di record all'interno di una transazione. Poiché il datastore App Engine non supporta le transazioni globali, App Engine genera eccezioni se il codice presuppone la disponibilità di transazioni globali. Anziché esaminare la tua codebase (potenzialmente di grandi dimensioni) e rimuovere tutto il codice di gestione delle transazioni, puoi semplicemente disattivare le transazioni del datastore. Ciò non fa nulla per risolvere i problemi relativi alle ipotesi che il tuo codice fa sull'atomicità delle modifiche multirecord, ma ti consente di far funzionare la tua app in modo da poterti concentrare sul refactoring del codice transazionale in modo incrementale e in base alle necessità, anziché tutto in una volta.