Java Data Objects (JDO) es una interfaz estándar para acceder a bases de datos en Java que proporciona una asignación entre clases de Java y tablas de bases de datos. Hay un complemento de código abierto disponible para usar JDO con Datastore. En esta página se explica cómo empezar a usarlo.
Advertencia: Creemos que la mayoría de los desarrolladores disfrutarán de una mejor experiencia si usan la API Datastore de bajo nivel o una de las APIs de código abierto desarrolladas específicamente para Datastore, como Objectify. JDO se diseñó para usarse con bases de datos relacionales tradicionales, por lo que no tiene forma de representar explícitamente algunos de los aspectos de Datastore que lo diferencian de las bases de datos relacionales, como los grupos de entidades y las consultas de ancestros. Esto puede provocar problemas sutiles que son difíciles de entender y solucionar.
El SDK de Java de App Engine incluye la versión 2.x del complemento DataNucleus para App Engine. Este complemento corresponde a la versión 3.0 de la plataforma de acceso de DataNucleus, que te permite usar Datastore de App Engine a través de JDO 3.0.
Para obtener más información sobre JDO, consulta la documentación de Access Platform 3.0. En particular, consulta Asignación de JDO y API de JDO.
Advertencia: El complemento DataNucleus 2.x para App Engine usa DataNucleus 3.x. Este nuevo complemento no es totalmente retrocompatible con el complemento anterior (1.x). Si actualizas a la nueva versión, asegúrate de actualizar y probar tu aplicación.
Herramientas de compilación compatibles con JDO 2.x y 3.0
Puedes usar Maven para usar la versión 2.x o 3.0 del complemento DataNucleus para App Engine:
- Para los usuarios de Maven: puedes mejorar las clases con las siguientes configuraciones en tu archivo
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-core</artifactId> <version>3.1.3</version> </dependency> </dependencies> </plugin>
Migrar a la versión 2.x del complemento DataNucleus
En esta sección se proporcionan instrucciones para actualizar tu aplicación de forma que use la versión 2.x del complemento DataNucleus para App Engine, que corresponde a DataNucleus Access Platform 3.0 y JDO 3.0. Este complemento no es totalmente retrocompatible con el complemento 1.x y puede cambiar. Si actualizas, asegúrate de actualizar y probar el código de tu aplicación.
Nuevos comportamientos predeterminados
La versión 2.x del complemento DataNucleus de App Engine tiene algunos valores predeterminados diferentes a los de la versión anterior:
- Las llamadas no transaccionales a
PersistenceManager.makePersistent()
yPersistenceManager.deletePersistent()
ahora se ejecutan de forma atómica. Estas funciones se ejecutaban anteriormente en la siguiente transacción o al hacer clic enPersistenceManager.close()
. - Ya no hay ninguna excepción en la asignación de PersistenceManagerFactory
(PMF) duplicada. En su lugar, si la propiedad de persistencia
datanucleus.singletonPMFForName
tiene el valor true, devolverá el PMF singleton asignado actualmente a ese nombre. - Ahora se admiten las relaciones sin propietario. Consulta Relaciones sin propietario.
Cambios en los archivos de configuración
Para actualizar tu aplicación a la versión 2.x del complemento DataNucleus de App Engine, debes cambiar los ajustes de configuración en build.xml
y jdoconfig.xml
.
Advertencia: Después de actualizar la configuración, debes probar el código de tu aplicación para asegurarte de que es compatible con versiones anteriores. Si vas a configurar una aplicación nueva y quieres usar la versión más reciente del complemento, ve a Configurar JDO 3.0.
- La propiedad
PersistenceManagerFactoryClass
ha cambiado. Cambia esta línea enjdoconfig.xml
:
<property name="javax.jdo.PersistenceManagerFactoryClass" value="org.datanucleus.store.appengine.jdo.DatastoreJDOPersistenceManagerFactory"/>
Para:
<property name="javax.jdo.PersistenceManagerFactoryClass" value="org.datanucleus.api.jdo.JDOPersistenceManagerFactory"/>
En build.xml
El objetivo copyjars
debe cambiar para adaptarse a
DataNucleus 2.x:
- El objetivo de
copyjars
ha cambiado. Actualiza esta sección:
Para:<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>
<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>
- El objetivo de
datanucleusenhance
ha cambiado. Actualiza esta sección:
Para:<target name="datanucleusenhance" depends="compile" description="Performs enhancement on compiled data classes."> <enhance_war war="war" /> </target>
<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>
Configurar JDO 3.0
Si quieres usar JDO para acceder al almacén de datos, necesitas lo siguiente para la aplicación de App Engine:
- Los archivos JAR de JDO y el complemento DataNucleus deben estar en el directorio
war/WEB-INF/lib/
de la aplicación. - Debe haber un archivo de configuración llamado
jdoconfig.xml
en el directoriowar/WEB-INF/classes/META-INF/
de la aplicación, con una configuración que indique a JDO que use el almacén de datos de App Engine. - El proceso de compilación del proyecto debe realizar un paso de "mejora" posterior a la compilación en las clases de datos compiladas para asociarlas a la implementación de JDO.
Copia de los archivos JAR
Los archivos JAR de JDO y del almacén de datos se incluyen en el SDK de Java de App Engine; Puedes encontrarlos en el directorio appengine-java-sdk/lib/opt/user/datanucleus/v2/
.
Copia los archivos JAR en el directorio war/WEB-INF/lib/
de tu aplicación.
Asegúrate de que appengine-api.jar
también esté en el directorio war/WEB-INF/lib/
. (Puede que ya lo hayas copiado al crear el proyecto). El complemento DataNucleus de App Engine usa este archivo JAR para acceder al almacén de datos.
Creación del archivo jdoconfig.xml.
La interfaz JDO necesita un archivo de configuración llamado jdoconfig.xml
en el directorio war/WEB-INF/classes/META-INF/
de la aplicación. Puedes crear este archivo directamente en esta ubicación o hacer que tu proceso de compilación copie este archivo desde un directorio de origen.
Debes crear el archivo con el siguiente contenido:
<?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.api.jdo.JDOPersistenceManagerFactory"/> <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.singletonPMFForName" value="true"/> </persistence-manager-factory> </jdoconfig>
Definir la política de lectura del almacén de datos y el plazo de llamada
Como se describe en la página Consultas de Datastore, puedes personalizar el comportamiento de Datastore configurando la política de lectura (coherencia fuerte o eventual) y el plazo de llamada. En JDO, esto se hace especificando los valores deseados en el elemento <persistence-manager-factory>
del archivo jdoconfig.xml
. Todas las llamadas realizadas con una instancia de PersistenceManager
determinada usarán los valores de configuración vigentes cuando el gestor se haya creado mediante PersistenceManagerFactory
. También puedes anular estos ajustes en un solo objeto Query
.
Para definir la política de lectura de un PersistenceManagerFactory
, incluye
una propiedad llamada datanucleus.appengine.datastoreReadConsistency
.
Los valores posibles son EVENTUAL
y STRONG
. Si no se especifica ningún valor, se utiliza STRONG
de forma predeterminada. Sin embargo, ten en cuenta que estos ajustes solo se aplican a las consultas de ancestros de un grupo de entidades determinado.
Las consultas que no son de ancestros siempre tienen una coherencia final, independientemente de la política de lectura vigente.
<property name="datanucleus.appengine.datastoreReadConsistency" value="EVENTUAL" />
Puedes definir distintos tiempos límite en las invocaciones del almacén de datos tanto para procesos de lectura como de escritura. Para las lecturas, usa la propiedad estándar de JDO javax.jdo.option.DatastoreReadTimeoutMillis
. Para las escrituras, usa
javax.jdo.option.DatastoreWriteTimeoutMillis
. El valor es un periodo de tiempo en milisegundos.
<property name="javax.jdo.option.DatastoreReadTimeoutMillis" value="5000" /> <property name="javax.jdo.option.DatastoreWriteTimeoutMillis" value="10000" />
Si quieres usar transacciones entre grupos (XG), añade la siguiente propiedad:
<property name="datanucleus.appengine.datastoreEnableXGTransactions" value="true" />
Puedes tener varios elementos <persistence-manager-factory>
en el mismo archivo jdoconfig.xml
, con diferentes atributos name
, para usar instancias de PersistenceManager
con diferentes configuraciones en la misma aplicación. Por ejemplo, el siguiente archivo jdoconfig.xml
establece dos conjuntos de configuración, uno llamado "transactions-optional"
y otro llamado "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.api.jdo.JDOPersistenceManagerFactory"/> <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.api.jdo.JDOPersistenceManagerFactory"/> <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" /> <property name="datanucleus.singletonPMFForName" value="true" /> </persistence-manager-factory> </jdoconfig>
Consulta la sección Obtener una instancia de PersistenceManager más abajo para obtener información sobre cómo crear un PersistenceManager
con un conjunto de configuración con nombre.
Mejora de las clases de datos
JDO usa un paso de "mejora" posterior a la compilación en el proceso de compilación para asociar clases de datos con la implementación de JDO.
Puede realizar el paso de mejora en las clases compiladas desde la línea de comandos con el siguiente comando:
java -cp classpath com.google.appengine.tools.enhancer.Enhance class-files
La ruta de clases debe contener el archivo JAR
appengine-tools-api.jar
del directorio
appengine-java-sdk/lib/
, así como todas tus clases de datos.
Para obtener más información sobre el optimizador de bytecode de DataNucleus, consulta la documentación de DataNucleus.
Obtener una instancia de PersistenceManager
Una aplicación interactúa con JDO utilizando una instancia de la clase PersistenceManager. Para obtener esta instancia, debes crear una instancia de la clase PersistenceManagerFactory y llamar a un método en ella. La fábrica usa la configuración de JDO para crear instancias de PersistenceManager.
Como una instancia de PersistenceManagerFactory tarda en inicializarse, una aplicación debe reutilizar una sola instancia. Una forma sencilla de gestionar la instancia de PersistenceManagerFactory es crear una clase envoltorio singleton con una instancia estática, como se muestra a continuación:
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; } }
Nota: "transactions-optional"
hace referencia al nombre del conjunto de configuración del archivo jdoconfig.xml
. Si tu aplicación usa varios conjuntos de configuración, tendrás que ampliar este código para llamar a JDOHelper.getPersistenceManagerFactory()
como quieras. Tu código debe almacenar en caché una instancia singleton de cada PersistenceManagerFactory
.
La aplicación usa la instancia de fábrica para crear una instancia de PersistenceManager por cada solicitud que acceda al almacén de datos.
import javax.jdo.JDOHelper; import javax.jdo.PersistenceManager; import javax.jdo.PersistenceManagerFactory; import PMF; // ... PersistenceManager pm = PMF.get().getPersistenceManager();
Utilizas PersistenceManager para almacenar, actualizar y eliminar objetos de datos, así como para realizar consultas de almacén de datos.
Cuando hayas terminado con la instancia de PersistenceManager, debes llamar a su método close()
. Es un error usar la instancia de PersistenceManager
después de llamar a su método close()
.
try { // ... do stuff with pm ... } finally { pm.close(); }
Funciones no admitidas de JDO 3.0
La implementación de App Engine no admite las siguientes funciones de la interfaz JDO:
- Relaciones de propiedad multidireccionales.
- Consultas "Join". No puedes usar un campo de una entidad secundaria en un filtro cuando realices una consulta en el tipo de entidad principal. Ten en cuenta que puedes probar el campo de relación de los padres directamente en la consulta mediante una clave.
- Consultas de agrupación JDOQL y otras consultas de agrupación conjunta.
- Consultas polimórficas. No puedes realizar una consulta de una clase para obtener instancias de una subclase. Cada clase se representa mediante un tipo de entidad independiente en el almacén de datos.
Inhabilitar transacciones y migrar aplicaciones JDO
La configuración de JDO que recomendamos usar asigna el valor true
a una propiedad llamada datanucleus.appengine.autoCreateDatastoreTxns
.
Se trata de una propiedad específica de App Engine que indica a la implementación de JDO que asocie las transacciones del almacén de datos con las transacciones de JDO que se gestionan en el código de la aplicación. Si vas a crear una aplicación desde cero, probablemente sea lo que buscas. Sin embargo, si tienes una aplicación basada en JDO y quieres que se ejecute en App Engine, puedes usar una configuración de persistencia alternativa que asigne el valor false
a esta propiedad:
<?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.api.jdo.JDOPersistenceManagerFactory"/> <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>
Para entender por qué puede ser útil, recuerda que solo puedes operar con objetos que pertenezcan al mismo grupo de entidades en una transacción. Las aplicaciones creadas con bases de datos tradicionales suelen presuponer la disponibilidad de transacciones globales, que te permiten actualizar cualquier conjunto de registros dentro de una transacción. Como el almacén de datos de App Engine no admite transacciones globales, App Engine genera excepciones si tu código asume la disponibilidad de transacciones globales. En lugar de revisar tu base de código (que puede ser grande) y eliminar todo el código de gestión de transacciones, puedes inhabilitar las transacciones de Datastore. Esto no hace nada para abordar las suposiciones que hace tu código sobre la atomicidad de las modificaciones de varios registros, pero te permite hacer que tu aplicación funcione para que puedas centrarte en refactorizar tu código transaccional de forma incremental y según sea necesario, en lugar de hacerlo todo a la vez.