L'API Java Persistence (JPA) è un'interfaccia standard per accedere ai database in Java, fornendo una mappatura automatica tra le classi e il database Java tabelle. È disponibile un plug-in open source per utilizzare JPA con Datastore. Questa pagina fornisce informazioni su come iniziare a utilizzarlo.
Avviso: riteniamo che la maggior parte degli sviluppatori l'utilizzo dell'API Datastore di basso livello o di una delle le API open source sviluppate appositamente per Datastore, come Objectify. JPA è stato progettato per essere utilizzato con database relazionali tradizionali e quindi non ha per rappresentare esplicitamente alcuni aspetti di Datastore che lo rendono in modo diverso rispetto ai database relazionali, come i gruppi di entità e le query predecessore. Questo può portare a sottili problemi difficili da comprendere e risolvere.
L'SDK Java di App Engine include la versione 2.x del Plug-in DataNucleus per Datastore. Questo plug-in corrisponde alla versione 3.0 del DataNucleus Access Platform, che consente di utilizzare App Engine Datastore tramite JPA 2.0.
Consulta: il Accedi alla documentazione di Platform 3.0 per ulteriori informazioni su JPA. Nella particolare, consulta JPA Documentazione.
Avviso:versione 2.x di DataNucleus per App Engine utilizza DataNucleus v3.x. Il plug-in 2.x non è completamente compatibile con le versioni precedenti del plug-in 1.x. Se esegui l'upgrade al nuovo aggiorna e testa l'applicazione.
Creazione di strumenti che supportano JPA 2.x e 3.0
Puoi usare Apache Ant o Maven per usa la versione 2.x o 3.0 del plug-in DataNucleus per App Engine:
- Per gli utenti Ant:l'SDK include un'attività Ant che esegue la passaggio di miglioramento. Devi copiare i JAR e creare il file di configurazione quando per configurare il progetto.
- Per gli utenti Maven: puoi migliorare i corsi con quanto segue
configurazioni nel file
pom.xml
:<plugin> <groupId>org.datanucleus</groupId> <artifactId>maven-datanucleus-plugin</artifactId> <version>3.2.0-m1</version> <configuration> <api>JDO</api> <props>${basedir}/datanucleus.properties</props> <verbose>true</verbose> <enhancerName>ASM</enhancerName> </configuration> <executions> <execution> <phase>process-classes</phase> <goals> <goal>enhance</goal> </goals> </execution> </executions> <dependencies> <dependency> <groupId>org.datanucleus</groupId> <artifactId>datanucleus-api-jdo</artifactId> <version>3.1.3</version> </dependency> </dependencies> </plugin>
Migrazione alla versione 2.x del plug-in DataNucleus
Questa sezione fornisce le istruzioni per eseguire l'upgrade dell'app all'uso della versione 2.x del plug-in DataNucleus per App Engine, che corrisponde a DataNucleus Accedi alla Piattaforma 3.0 e JPA 2.0. La versione del plug-in 2.x non è completamente è compatibile con le versioni precedenti alla versione 1.x e può cambiare senza preavviso. Se esegui l'upgrade, assicurati di aggiornare e testare il codice dell'applicazione.
Nuovi comportamenti predefiniti
La versione 2.x del plug-in DataNucleus di App Engine ha alcuni valori predefiniti diversi rispetto alla versione 1.x precedente:
- "Fornitore di persistenza" JPA è ora
org.datanucleus.api.jpa.PersistenceProviderImpl
. - La memorizzazione nella cache di livello 2 è abilitata per impostazione predefinita. Per ottenere l'impostazione predefinita precedente
comportamento, imposta la proprietà di persistenza
Da
datanucleus.cache.level2.type
a nessuno. (In alternativa, includere il plug-in datanucleus-cache nel classpath e impostare la persistenzadatanucleus.cache.level2.type
in javax.cache in e usare Memcache per la memorizzazione nella cache L2. - Ora il valore predefinito di Datastore
IdentifierFactory
è datanucleus2. Per ottenere il comportamento precedente, imposta la persistenza proprietàdatanucleus.identifierFactory
in datanucleus1. - Chiamate non transazionali a
EntityManager.persist()
,EntityManager.merge()
eEntityManager.remove()
vengono eseguiti a livello atomico. (In precedenza, l'esecuzione avveniva al transazione o a partire daEntityManager.close()
. - JPA ha
retainValues
abilitato, il che significa che i valori di e vengono conservati negli oggetti dopo un commit. javax.persistence.query.chunkSize
non è più in uso. Utilizza le funzionalità didatanucleus.query.fetchSize
in alternativa.- Non esistono più eccezioni all'allocazione EMF duplicata. Se
avere la proprietà di persistenza
datanucleus.singletonEMFForName
impostata su true, restituirà l'EMF singleton attualmente allocato per quel nome. - Ora sono supportate relazioni senza proprietà.
- Datastore Identity è ora supportato.
Per un elenco completo delle nuove funzionalità, vedi rilascio note.
Modifiche ai file di configurazione
Per eseguire l'upgrade dell'app in modo che utilizzi la versione 2.0 del plug-in DataNucleus per l'app
Engine, devi modificare alcune impostazioni di configurazione in build.xml
e persistence.xml
. Se stai configurando una nuova applicazione e
utilizzare l'ultima versione del plug-in DataNucleus, procedi su
Configurazione di JPA 2.0.
Attenzione: Dopo aver aggiornato la configurazione, occorre testare il codice dell'applicazione per verificarne la compatibilità con le versioni precedenti.
Nel file build.xml
Il target copyjars
deve essere modificato per soddisfare
DataNucleus 2.x:
- Il target
copyjars
è stato modificato. Aggiorna questa sezione:
<target name="copyjars" description="Copies the App Engine JARs to the WAR."> <mkdir dir="war/WEB-INF/lib" /> <copy todir="war/WEB-INF/lib" flatten="true"> <fileset dir="${sdk.dir}/lib/user"> <include name="**/*.jar" /> </fileset> </copy> </target>
a:
<target name="copyjars" description="Copies the App Engine JARs to the WAR."> <mkdir dir="war/WEB-INF/lib" /> <copy todir="war/WEB-INF/lib" flatten="true"> <fileset dir="${sdk.dir}/lib/user"> <include name="**/appengine-api-1.0-sdk*.jar" /> </fileset> <fileset dir="${sdk.dir}/lib/opt/user"> <include name="appengine-api-labs/v1/*.jar" /> <include name="jsr107/v1/*.jar" /> <include name="datanucleus/v2/*.jar" /> </fileset> </copy> </target>
- Il target
datanucleusenhance
è stato modificato. Aggiorna sezione:
<target name="datanucleusenhance" depends="compile" description="Performs enhancement on compiled data classes."> <enhance_war war="war" /> </target>
a:
<target name="datanucleusenhance" depends="compile" description="Performs enhancement on compiled data classes."> <enhance_war war="war"> <args> <arg value="-enhancerVersion"/> <arg value="v2"/> </args> </enhance_war> </target>
In persistence.xml
Il target <provider>
è stato modificato. Aggiorna
sezione:
<provider>org.datanucleus.store.appengine.jpa.DatastorePersistenceProvider</provider>
to:
<provider>org.datanucleus.api.jpa.PersistenceProviderImpl</provider>
Configurazione di JPA 2.0
Per utilizzare JPA per accedere al datastore, un'app App Engine ha bisogno seguenti:
- I JAR giapponesi e datastore devono essere
Directory
war/WEB-INF/lib/
. - Un file di configurazione denominato
persistence.xml
deve essere nel directorywar/WEB-INF/classes/META-INF/
dell'app, con configurazione che indica a JPA di usare il datastore di App Engine. - Il processo di compilazione del progetto deve eseguire un "miglioramento" di post-compilazione passaggio sulle classi di dati compilate per associarle all'JPA implementazione.
Copia dei JAR in corso...
I JAR JPA e datastore sono inclusi con l'SDK Java di App Engine. Puoi
trovali in appengine-java-sdk/lib/opt/user/datanucleus/v2/
.
Copia i JAR nel file war/WEB-INF/lib/
dell'applicazione
.
Assicurati che appengine-api.jar
sia anche nel
Directory war/WEB-INF/lib/
. (Forse lo hai già copiato quando
la creazione del progetto). Il plug-in DataNucleus di App Engine utilizza questo JAR per
per accedere al datastore.
Creazione del file persistence.xml
L'interfaccia JPA richiede un file di configurazione denominato
persistence.xml
nel campo
Directory war/WEB-INF/classes/META-INF/
. Puoi creare questo file
in questa posizione oppure chiedi al tuo processo di compilazione di copiare questo file da un
nella directory di origine.
Crea il file con il seguente contenuto:
<?xml version="1.0" encoding="UTF-8" ?> <persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0"> <persistence-unit name="transactions-optional"> <provider>org.datanucleus.api.jpa.PersistenceProviderImpl</provider> <properties> <property name="datanucleus.NontransactionalRead" value="true"/> <property name="datanucleus.NontransactionalWrite" value="true"/> <property name="datanucleus.ConnectionURL" value="appengine"/> <property name="datanucleus.singletonEMFForName" value="true"/> </properties> </persistence-unit> </persistence>
Norme di lettura e scadenza delle chiamate Datastore
Come descritto nella
Datastore
Query, puoi impostare il criterio per la lettura (elevata coerenza oppure finale
coerenza) e la scadenza della chiamata al datastore per
EntityManagerFactory
nel file persistence.xml
.
Queste impostazioni vengono inserite nell'elemento <persistence-unit>
. Tutti
effettuate con una determinata istanza EntityManager
,
configurazione selezionata al momento della creazione del gestore
EntityManagerFactory
. Puoi anche sostituire queste opzioni per
singolo Query
(descritto di seguito).
Per impostare il criterio per la lettura, includi una proprietà denominata
datanucleus.appengine.datastoreReadConsistency
. I suoi valori possibili
sono EVENTUAL
(per letture con coerenza finale) e
STRONG
(per letture con elevata coerenza). Se non specificato, il parametro
Il valore predefinito è STRONG
.
<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 JPA
javax.persistence.query.timeout
. Per la scrittura, utilizza
datanucleus.datastoreWriteTimeout
. Il valore è una quantità di tempo
in millisecondi.
<property name="javax.persistence.query.timeout" value="5000" /> <property name="datanucleus.datastoreWriteTimeout" value="10000" />
Se vuoi utilizzare le transazioni tra gruppi (XG), aggiungi quanto segue proprietà:
<property name="datanucleus.appengine.datastoreEnableXGTransactions" value="true" />
Puoi avere più elementi <persistence-unit>
nella
stesso file persistence.xml
, utilizzando name
diversi
, per utilizzare EntityManager
istanze con diversi
configurazioni nella stessa app. Ad esempio,
Il file persistence.xml
stabilisce due insiemi di configurazione, uno
chiamato "transactions-optional"
e un altro con nome
"eventual-reads-short-deadlines"
:
<?xml version="1.0" encoding="UTF-8" ?> <persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0"> <persistence-unit name="transactions-optional"> <provider>org.datanucleus.api.jpa.PersistenceProviderImpl</provider> <properties> <property name="datanucleus.NontransactionalRead" value="true"/> <property name="datanucleus.NontransactionalWrite" value="true"/> <property name="datanucleus.ConnectionURL" value="appengine"/> </properties> </persistence-unit> <persistence-unit name="eventual-reads-short-deadlines"> <provider>org.datanucleus.api.jpa.PersistenceProviderImpl</provider> <properties> <property name="datanucleus.NontransactionalRead" value="true"/> <property name="datanucleus.NontransactionalWrite" value="true"/> <property name="datanucleus.ConnectionURL" value="appengine"/> <property name="datanucleus.appengine.datastoreReadConsistency" value="EVENTUAL" /> <property name="javax.persistence.query.timeout" value="5000" /> <property name="datanucleus.datastoreWriteTimeout" value="10000" /> <property name="datanucleus.singletonEMFForName" value="true"/> </properties> </persistence-unit> </persistence>
Consulta Ottenere un EntityManager
istanza di seguito per informazioni sulla creazione di un EntityManager
con un set di configurazione denominato.
Puoi ignorare il criterio per la lettura e la scadenza della chiamata per un singolo
Query
oggetto. Per eseguire l'override del criterio per la lettura per un Query
,
chiama il metodo setHint()
nel modo seguente:
Query q = em.createQuery("select from " + Book.class.getName()); q.setHint("datanucleus.appengine.datastoreReadConsistency", "EVENTUAL");
Come sopra, i valori possibili sono "EVENTUAL"
e
"STRONG"
.
Per eseguire l'override del timeout di lettura, chiama setHint()
in questo modo:
q.setHint("javax.persistence.query.timeout", 3000);
Non è possibile eseguire l'override della configurazione di queste opzioni quando recupera le entità in base alla chiave.
Miglioramento delle classi di dati
L'implementazione DataNucleus di JPA utilizza un "enhancement" di post-compilazione passaggio del processo di compilazione per associare le classi di dati all'JPA implementazione.
Puoi eseguire il passaggio di miglioramento per le classi compilate dal comando con il seguente comando:
java -cp classpath org.datanucleus.enhancer.DataNucleusEnhancer class-files
classpath deve contenere i JAR
datanucleus-core-*.jar
datanucleus-jpa-*
datanucleus-enhancer-*.jar
, asm-*.jar
e
geronimo-jpa-*.jar
(dove *
è la versione appropriata
numero di ogni JAR) dal appengine-java-sdk/lib/tools/
e tutte le tue classi di dati.
Per ulteriori informazioni sul potenziatore bytecode DataNucleus, vedi il documentazione di DataNucleus.
Recupero di un'istanza EntityManager
Un'app interagisce con JPA utilizzando un'istanza di EntityManager
. Puoi ottenere questa istanza creando un'istanza e chiamando un metodo su una
dell'istanza della classe EntityManagerFactory
. La fabbrica utilizza
Configurazione JPA (identificata dal nome "transactions-optional"
)
per creare EntityManager
istanze.
Perché un'istanza EntityManagerFactory
richiede tempo
inizializzare un'istanza, è consigliabile riutilizzare il più possibile una singola istanza. Un
il modo più semplice per farlo è creare una classe wrapper singleton con un
, come segue:
EMF.java
import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; public final class EMF { private static final EntityManagerFactory emfInstance = Persistence.createEntityManagerFactory("transactions-optional"); private EMF() {} public static EntityManagerFactory get() { return emfInstance; } }
Suggerimento: "transactions-optional"
si riferisce alla
nome della configurazione impostata nel file persistence.xml
. Se le tue
utilizza più set di configurazione, devi estendere questo codice per chiamare
Persistence.createEntityManagerFactory()
a tua scelta. Il tuo codice
deve memorizzare nella cache un'istanza singleton di ogni EntityManagerFactory
.
L'app utilizza l'istanza di fabbrica per creare un EntityManager
per ogni richiesta che accede al datastore.
import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import EMF; // ... EntityManager em = EMF.get().createEntityManager();
Puoi utilizzare EntityManager
per archiviare, aggiornare ed eliminare i dati
ed eseguire query sul datastore.
Quando hai finito di utilizzare l'istanza EntityManager
, devi chiamare
il metodo close()
. È un errore utilizzare
EntityManager
dopo la chiamata alla relativa close()
.
try { // ... do stuff with em ... } finally { em.close(); }
Annotazioni per corsi e campi
Ogni oggetto salvato da JPA diventa un'entità nel datastore di App Engine. La il tipo di entità deriva dal nome semplice della classe (senza il pacchetto ). Ogni campo permanente della classe rappresenta una proprietà dell'entità, con il nome della proprietà uguale a quello del campo (con maiuscole e minuscole) conservati).
Per dichiarare una classe Java che può essere memorizzata e recuperata dal
datastore con JPA, assegna alla classe un'annotazione @Entity
. Per
esempio:
import javax.persistence.Entity; @Entity public class Employee { // ... }
I campi della classe di dati da archiviare nel datastore devono
essere di un tipo persistente per impostazione predefinita o dichiarato esplicitamente come permanente.
È disponibile un grafico con i dettagli sul comportamento predefinito della persistenza JPA su
il
Sito web DataNucleus. Per dichiarare esplicitamente un campo come permanente, devi fornire
un'annotazione @Basic
:
import java.util.Date; import javax.persistence.Enumerated; import com.google.appengine.api.datastore.ShortBlob; // ... @Basic private ShortBlob data;
Il tipo di campo può essere:
- uno dei tipi di core supportati dal datastore
- una raccolta (ad esempio
java.util.List<...>
) di valori di un tipo di datastore principale - un'istanza o una raccolta di istanze di un
@Entity
classe - una classe incorporata, archiviata come proprietà sull'entità
Una classe di dati deve avere un costruttore predefinito pubblico o protetto e uno
campo dedicato all'archiviazione della chiave primaria del datastore corrispondente
dell'oggetto. Puoi scegliere tra quattro diversi tipi di campi chiave, ognuno dei quali utilizza un
tipo di valore e annotazioni diversi. (Vedi
Creazione in corso
Dati: chiavi per ulteriori informazioni. Il campo chiave più semplice è un numero intero lungo
valore automaticamente compilato da JPA con un valore univoco in tutte
altre istanze della classe quando l'oggetto viene salvato nel datastore per
per la prima volta. Le chiavi interi lunghe utilizzano un'annotazione @Id
e un
Annotazione @GeneratedValue(strategy = GenerationType.IDENTITY)
:
import com.google.appengine.api.datastore.Key; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; // ... @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Key key;
Ecco un esempio di classe di dati:
import com.google.appengine.api.datastore.Key; import java.util.Date; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @Entity public class Employee { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Key key; private String firstName; private String lastName; private Date hireDate; // Accessors for the fields. JPA doesn't use these, but your application does. public Key getKey() { return key; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public Date getHireDate() { return hireDate; } public void setHireDate(Date hireDate) { this.hireDate = hireDate; } }
Ereditarietà
JPA supporta la creazione di classi di dati che usano l'ereditarietà. Prima di parlare di come funziona l'ereditarietà JPA su App Engine, ti consigliamo di leggere il documentazione di DataNucleus su questo argomento. Completato? Ok. JPA l'ereditarietà su App Engine funziona come descritto nella documentazione di DataNucleus con alcune limitazioni aggiuntive. Parleremo di queste restrizioni e poi fai alcuni esempi concreti.
Lo stato "JOINED" la strategia di ereditarietà consente di suddividere i dati per una singola in più "tabelle", ma poiché il datastore di App Engine non supporta i join, che opera su un oggetto dati con questa strategia di ereditarietà richiede una chiamata di procedura remota per ogni livello di ereditarietà. Questo è potenzialmente molto inefficiente, quindi "JOINED" la strategia di ereditarietà non è supportate sulle classi di dati.
Secondo, la tabella "SINGLE_TABLE" la strategia di ereditarietà consente di archiviare i dati per un oggetto dati in una singola "tabella" associata alla classe persistente alla radice della gerarchia di ereditarietà. Sebbene non esistano modelli inefficienze in questa strategia, al momento non è supportata. Potremmo rivedere nelle versioni future.
Ora la buona notizia: la classe "TABLE_PER_CLASS" e "MAPPED_SUPERCLASS" strategie funzionano come descritto nella documentazione di DataNucleus. Diamo un'occhiata esempio:
Worker.java
import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.MappedSuperclass; @Entity @MappedSuperclass public abstract class Worker { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Key key; private String department; }
Employee.java
// ... imports ... @Entity public class Employee extends Worker { private int salary; }
Intern.java
import java.util.Date; // ... imports ... @Entity public class Intern extends Worker { private Date internshipEndDate; }
In questo esempio abbiamo aggiunto un'annotazione @MappedSuperclass
a
la dichiarazione di classe Worker
. Questo indica ad JPA di archiviare tutti
e campi permanenti di Worker
nelle entità del datastore
le sottoclassi del deployment. L'entità datastore creata a seguito della chiamata
persist()
con un'istanza Employee
avrà due
strutture denominate "reparto" e "stipendio". L'entità datastore creata come
risultato della chiamata a persist()
con un'istanza Intern
avrà due proprietà denominate "reparto" e "inernshipEndDate". Ci sarà
non essere entità di tipo "Worker" nel datastore.
Ora rendiamo le cose un po' più interessanti. Supponiamo, oltre a
con Employee
e Intern
, vogliamo anche che
specializzazione di Employee
che descrive i dipendenti che hanno lasciato l'azienda
l'azienda:
FormerEmployee.java
import java.util.Date; import javax.persistence.Inheritance; import javax.persistence.InheritanceType; // ... imports ... @Entity @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) public class FormerEmployee extends Employee { private Date lastDay; }
In questo esempio abbiamo aggiunto un'annotazione @Inheritance
alla
Dichiarazione di classe FormerEmployee
con strategy
impostato su InheritanceType.TABLE_PER_CLASS
. Questo indica a JPA di
archivia tutti i campi permanenti di FormerEmployee
e i relativi
superclassi in entità del datastore corrispondenti a FormerEmployee
di Compute Engine. L'entità datastore creata a seguito della chiamata
persist()
con un'istanza FormerEmployee
avrà
tre proprietà denominate "repartment", "salary" e "lastDay". Non ci saranno mai
Essere un'entità di tipo "Dipendente" che corrisponde a un
FormerEmployee
, ma se chiami persist()
con un
il cui tipo di runtime è Employee
; verrà creata un'entità di
"Dipendente.
La combinazione delle relazioni con l'ereditarietà funziona purché i tipi di che i campi della relazione corrispondano ai tipi di runtime degli oggetti assegnandoli a questi campi. Consulta la sezione Polimorfico Relazioni per ulteriori informazioni. Questa sezione contiene esempi JDO, ma i concetti e le restrizioni sono gli stessi per JPA.
Funzionalità non supportate di JPA 2.0
L'app non supporta le seguenti funzionalità dell'interfaccia JPA Implementazione del motore:
- Possedere relazioni di tipo many-to-many.
- "Partecipa" query. Non puoi utilizzare un campo di un'entità secondaria in un filtro quando eseguendo una query sul tipo padre. Tieni presente che puoi testare l'impostazione della relazione direttamente in una query utilizzando una chiave.
- Query di aggregazione (raggruppa per, con, somma, media, max, min)
- Query polimorfiche. Non puoi eseguire una query su una classe per ottenere istanze di una sottoclasse. Ogni classe è rappresentata da un tipo di entità separato nel datastore.