Utilizzo di JDO 2.3 con App Engine

Java Data Objects (JDO) è un'interfaccia standard per accedere ai database in Java, fornendo una mappatura tra classi e database Java tabelle. È disponibile un plug-in open source per utilizzare JDO con Datastore. 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 i database relazionali tradizionali e, pertanto, 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 sugli antenati. Ciò può portare a problemi sottili che sono difficili da comprendere e risolvere.

L'SDK Java di App Engine include un'implementazione di JDO 2.3 per Datastore di App Engine. L'implementazione si basa sulla versione 1.0 del DataNucleus Access Platform, l'implementazione di riferimento open source per JDO 2.3.

Nota: le istruzioni riportate in questa pagina si applicano alla versione 2.3 di JDO, 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 offre una serie di nuove API e funzionalità. Questo upgrade non è completamente compatibile con le versioni precedenti. Se ricostruisci un'applicazione utilizzando JDO 3.0, devi aggiornare e riesaminare il codice. Per ulteriori informazioni sulla nuova versione, consulta JDO 3.0. Per ulteriori informazioni sull'upgrade, consulta Utilizzo di JDO 3.0 con App Engine.

Configurazione di JDO 2.3

Per utilizzare JDO per accedere al datastore, un'app App Engine deve avere quanto segue:

  • I JAR dei plug-in JDO e DataNucleus App Engine devono essere Directory war/WEB-INF/lib/.
  • Un file di configurazione denominato jdoconfig.xml deve essere nella cartella directory war/WEB-INF/classes/META-INF/, con una configurazione indica a JDO di utilizzare il datastore di App Engine.
  • Il 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 JAR in corso...

I file JAR JDO e Datastore sono inclusi nell'SDK Java di App Engine. Puoi nella directory appengine-java-sdk/lib/user/orm/.

Copia i JAR nel file war/WEB-INF/lib/ dell'applicazione .

Assicurati che appengine-api.jar sia anche nel Directory war/WEB-INF/lib/. Potresti averlo già copiato al momento della creazione del progetto. Il plug-in DataNucleus di App Engine utilizza questo JAR per 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 chiedere alla procedura di compilazione di copiarlo da una directory di origine.

Crea il file con il seguente contenuto:

<?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 del criterio di lettura del datastore e della scadenza della chiamata

Come descritto nella pagina Query Datastore, puoi personalizzare il comportamento del Datastore impostando il criterio di lettura (coerenza forte o eventuale) e la scadenza della chiamata. In JDO, specificando i valori desiderati nel <persistence-manager-factory> elemento di jdoconfig.xml file. 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 il criterio di 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 sugli antenati all'interno di un determinato gruppo di entità. Le query non predecessori e tra gruppi sono sempre coerenti alla fine indipendentemente dal prevalente di criterio per la lettura.

        <property name="datanucleus.appengine.datastoreReadConsistency" value="EVENTUAL" />

Puoi impostare scadenze separate per le chiamate del datastore per letture e scritture. Per 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 le transazioni tra gruppi (XG), aggiungi quanto segue proprietà:

        <property name="datanucleus.appengine.datastoreEnableXGTransactions" value="true" />

Puoi avere più <persistence-manager-factory> elementi nello stesso file jdoconfig.xml, utilizzando diversi name, per utilizzare le istanze PersistenceManager con configurazioni diverse nella stessa app. Ad esempio, Il file jdoconfig.xml stabilisce due insiemi di configurazione, uno denominato "transactions-optional" e un altro nome "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>

Consulta Ottenere un'istanza di PersistenceManager di seguito per informazioni su come creare un PersistenceManager con un insieme di configurazioni denominato.

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 JARappengine-tools-api.jar della directoryappengine-java-sdk/lib/, nonché tutti i tuoi livelli di dati.

Per ulteriori informazioni sul potenziatore bytecode DataNucleus, vedi il documentazione di DataNucleus.

Ottenere un'istanza di PersistenceManager

Un'app interagisce con JDO utilizzando un'istanza della classe PersistenceManager. Puoi ottenere questa istanza creando un'istanza e chiamando un metodo su un'istanza la classe PersistenceManagerFA. La fabbrica utilizza la configurazione JDO per delle istanze PersistenceManager.

Poiché l'inizializzazione di un'istanza PersistenceManager Fabbrica richiede tempo, deve riutilizzare una singola istanza. Per applicare questa regola, viene lanciata un'eccezione se l'app esegue l'inizializzazione di più di un 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" fa riferimento al nome della configurazione impostata nel file jdoconfig.xml. Se le tue utilizza più set di configurazione, devi estendere questo codice per chiamare JDOHelper.getPersistenceManagerFactory() a tua scelta. Il codice deve memorizzare nella cache un'istanza singleton di ogni PersistenceManagerFactory.

L'app utilizza l'istanza di fabbrica per creare un'istanza di 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();

Puoi usare PersistenceManager per archiviare, aggiornare ed eliminare gli oggetti dati, per eseguire query sul datastore.

Al termine dell'utilizzo dell'istanza PersistenceManager, devi chiamarne il metodo close(). Utilizzare PersistenceManager è un errore istanza dopo aver chiamato il 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'app Implementazione del motore:

  • Relazioni non di tua proprietà. Puoi implementare relazioni non di proprietà utilizzando valori chiave espliciti. La sintassi di JDO per le relazioni senza proprietà potrebbe essere supportata in una versione 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 l'impostazione della relazione direttamente nella query utilizzando una chiave.
  • Raggruppamento JDOQL e altre query aggregate.
  • Query polimorfiche. Non puoi eseguire una query su una classe per ottenere istanze di una sottoclasse. Ogni classe è rappresentata da un tipo di entità distinto nel datastore.
  • IdentityType.DATASTORE per l'annotazione @PersistenceCapable. Solo IdentityType.APPLICATION è supportato.
  • Attualmente esiste un bug che impedisce le relazioni one-to-many di proprietà principale e secondaria sono la stessa classe, il che rende difficile la creazione di modelli strutture. Questo problema verrà risolto in una release futura. Puoi aggirare il problema archiviando valori chiave espliciti per l'elemento principale o secondario.

Disattivazione delle transazioni e porting delle app JDO esistenti

La configurazione JDO che consigliamo di utilizzare imposta una proprietà denominata Da datanucleus.appengine.autoCreateDatastoreTxns a 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 si tratta quello che vuoi. Tuttavia, se hai già un'applicazione basata su JDO che vuoi eseguire su App Engine, ti consigliamo di utilizzare una configurazione di persistenza alternativa che imposti 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é può essere utile, ricorda che puoi eseguire operazioni solo sugli 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 set di record transazione. Poiché il datastore di App Engine non supporta le transazioni, App Engine genera eccezioni se il codice assume la disponibilità delle transazioni globali. Anziché esaminare la base di codice (potenzialmente di grandi dimensioni) e rimuovere tutto il codice di gestione delle transazioni, puoi semplicemente disattivare le transazioni del datastore. Ciò non risolve le supposizioni fatte dal codice sull'atomicità delle modifiche di più record, ma ti consente di far funzionare l'app in modo da poterti concentrare sul refactoring del codice transazionale in modo incrementale e in base alle necessità, anziché tutto in una volta.