Os objetos de dados Java (JDO) são uma interface padrão para aceder a bases de dados em Java, que fornece um mapeamento entre classes Java e tabelas de bases de dados. Existe um plug-in de código aberto disponível para usar o JDO com o Datastore, e esta página fornece informações sobre como começar a usá-lo.
Aviso: acreditamos que a maioria dos programadores terá uma melhor experiência com a API Datastore de baixo nível ou uma das APIs de código aberto desenvolvidas especificamente para o Datastore, como o Objectify. O JDO foi concebido para utilização com bases de dados relacionais tradicionais e, por isso, não tem forma de representar explicitamente alguns dos aspetos do Datastore que o diferenciam das bases de dados relacionais, como grupos de entidades e consultas de antecessores. Isto pode levar a problemas subtis difíceis de compreender e corrigir.
O SDK Java do App Engine inclui a versão 2.x do plug-in DataNucleus para o App Engine. Este plug-in corresponde à versão 3.0 da DataNucleus Access Platform, que lhe permite usar o App Engine Datastore através do JDO 3.0.
Consulte a documentação da plataforma de acesso 3.0 para mais informações sobre o JDO. Em particular, consulte Mapeamento JDO e API JDO.
Aviso: o plug-in DataNucleus 2.x para o App Engine usa o DataNucleus v3.x. Este novo plugin não é totalmente retrocompatível com o plugin anterior (1.x). Se atualizar para a nova versão, certifique-se de que atualiza e testa a sua aplicação.
Ferramentas de criação que suportam JDO 2.x e 3.0
Pode usar o Maven para usar a versão 2.x ou 3.0 do plug-in DataNucleus para o App Engine:
- Para utilizadores do Maven: pode melhorar as classes com as seguintes configurações no ficheiro
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 para a versão 2.x do plug-in DataNucleus
Esta secção fornece instruções para atualizar a sua app de modo a usar a versão 2.x do plug-in DataNucleus para o App Engine, que corresponde à DataNucleus Access Platform 3.0 e ao JDO 3.0. Este plugin não é totalmente retrocompatível com o plugin 1.x e pode mudar. Se fizer a atualização, certifique-se de que atualiza e testa o código da aplicação.
Novos comportamentos predefinidos
A versão 2.x do plug-in App Engine DataNucleus tem algumas predefinições diferentes das da versão anterior:
- As chamadas não transacionais para
PersistenceManager.makePersistent()
ePersistenceManager.deletePersistent()
são agora executadas de forma atómica. (Anteriormente, estas funções eram executadas na transação seguinte ou quandoPersistenceManager.close()
.) - Já não existe uma exceção na atribuição de PersistenceManagerFactory
(PMF) duplicada. Em alternativa, se tiver a propriedade de persistência
datanucleus.singletonPMFForName
definida como true, devolve o PMF singleton atualmente atribuído para esse nome. - As relações não pertencentes são agora suportadas. Consulte a secção Relações não detidas.
Alterações aos ficheiros de configuração
Para atualizar a sua aplicação para a versão 2.x do plug-in App Engine DataNucleus, tem de alterar as definições de configuração em build.xml
e jdoconfig.xml
.
Aviso! Depois de atualizar a configuração, tem de testar o código da aplicação para garantir a compatibilidade com versões anteriores. Se estiver a configurar uma nova aplicação e quiser usar a versão mais recente do plug-in, avance para Configurar o JDO 3.0.
- A propriedade
PersistenceManagerFactoryClass
foi alterada. Altere esta linha emjdoconfig.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"/>
Em build.xml
O alvo copyjars
tem de ser alterado para se adequar ao DataNucleus 2.x:
- O alvo de
copyjars
foi alterado. Atualize esta secção:
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>
- O alvo de
datanucleusenhance
foi alterado. Atualize esta secção:
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 o JDO 3.0
Para usar o JDO para aceder ao arquivo de dados, uma app do App Engine precisa do seguinte:
- Os ficheiros JARs para JDO e o plugin DataNucleus têm de estar no diretório
war/WEB-INF/lib/
da app. - Tem de existir um ficheiro de configuração denominado
jdoconfig.xml
no diretóriowar/WEB-INF/classes/META-INF/
da app, com uma configuração que indique ao JDO para usar o armazenamento de dados do App Engine. - O processo de compilação do projeto tem de executar um passo de "melhoramento" pós-compilação nas classes de dados compiladas para as associar à implementação do JDO.
Copiar os JARs
Os JARs do JDO e do armazenamento de dados estão incluídos no SDK Java do App Engine. Pode encontrá-los no diretório appengine-java-sdk/lib/opt/user/datanucleus/v2/
.
Copie os ficheiros JAR para o diretório war/WEB-INF/lib/
da sua aplicação.
Certifique-se de que o appengine-api.jar
também está no diretório war/WEB-INF/lib/
. (Pode já ter copiado este código quando criou o projeto.) O plug-in DataNucleus do App Engine usa este JAR para aceder ao datastore.
Criar o ficheiro jdoconfig.xml
A interface JDO precisa de um ficheiro de configuração denominado jdoconfig.xml
no diretório war/WEB-INF/classes/META-INF/
da aplicação. Pode criar este ficheiro diretamente nesta localização ou fazer com que o processo de compilação copie este ficheiro de um diretório de origem.
Crie o ficheiro com o seguinte conteúdo:
<?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 a política de leitura do armazenamento de dados e o prazo da chamada
Conforme descrito na página Consultas
do Datastore, pode personalizar o comportamento do Datastore definindo a política de leitura (consistência forte versus eventual) e o prazo de chamada. No JDO, isto é feito especificando os valores pretendidos no elemento <persistence-manager-factory>
do ficheiro jdoconfig.xml
. Todas as chamadas feitas com uma determinada instância PersistenceManager
usam os valores de configuração em vigor quando o gestor foi criado pelo PersistenceManagerFactory
. Também pode substituir estas definições para um único objeto Query
.
Para definir a política de leitura para um PersistenceManagerFactory
, inclua
uma propriedade denominada datanucleus.appengine.datastoreReadConsistency
.
Os valores possíveis são EVENTUAL
e STRONG
. Se não for especificado, a predefinição é STRONG
. (Tenha em atenção, no entanto, que estas definições aplicam-se apenas a consultas de antecessores num determinado grupo de entidades.
As consultas não antecessoras são sempre eventualmente consistentes, independentemente da política de leitura predominante.
<property name="datanucleus.appengine.datastoreReadConsistency" value="EVENTUAL" />
Pode definir prazos separados de chamadas da base de dados para leituras e escritas. Para leituras, use a propriedade padrão JDO javax.jdo.option.DatastoreReadTimeoutMillis
. Para escritas, use
javax.jdo.option.DatastoreWriteTimeoutMillis
. O valor é um período, em milissegundos.
<property name="javax.jdo.option.DatastoreReadTimeoutMillis" value="5000" /> <property name="javax.jdo.option.DatastoreWriteTimeoutMillis" value="10000" />
Se quiser usar transações entre grupos (XG), adicione a seguinte propriedade:
<property name="datanucleus.appengine.datastoreEnableXGTransactions" value="true" />
Pode ter vários elementos <persistence-manager-factory>
no mesmo ficheiro jdoconfig.xml
, usando diferentes atributos name
, para usar instâncias PersistenceManager
com diferentes configurações na mesma app. Por exemplo, o seguinte ficheiro jdoconfig.xml
estabelece dois conjuntos de configuração, um denominado "transactions-optional"
e outro denominado "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>
Consulte a secção Obter uma instância de PersistenceManager abaixo para ver informações sobre a criação de um PersistenceManager
com um conjunto de configurações denominado.
Melhorar as classes de dados
O JDO usa um passo de "melhoramento" pós-compilação no processo de compilação para associar classes de dados à implementação do JDO.
Pode executar o passo de melhoramento em classes compiladas a partir da linha de comandos com o seguinte comando:
java -cp classpath com.google.appengine.tools.enhancer.Enhance class-files
O classpath tem de conter o JAR
appengine-tools-api.jar
do diretório
appengine-java-sdk/lib/
, bem como todas as suas classes de dados.
Para mais informações sobre o melhorador de bytecode do DataNucleus, consulte a documentação do DataNucleus.
Obter uma instância de PersistenceManager
Uma app interage com o JDO através de uma instância da classe PersistenceManager. Obtém esta instância ao instanciar e chamar um método numa instância da classe PersistenceManagerFactory. A fábrica usa a configuração JDO para criar instâncias de PersistenceManager.
Uma vez que uma instância de PersistenceManagerFactory demora a inicializar, uma app deve reutilizar uma única instância. Uma forma fácil de gerir a instância de PersistenceManagerFactory é criar uma classe de wrapper singleton com uma instância estática, da seguinte forma:
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; } }
Dica: "transactions-optional"
refere-se ao nome do conjunto de configurações no ficheiro jdoconfig.xml
. Se a sua app usar vários conjuntos de configuração, tem de estender este código para chamar JDOHelper.getPersistenceManagerFactory()
conforme pretendido. O seu código deve armazenar em cache uma instância única de cada PersistenceManagerFactory
.
A app usa a instância de fábrica para criar uma instância de PersistenceManager para cada pedido que acede ao repositório de dados.
import javax.jdo.JDOHelper; import javax.jdo.PersistenceManager; import javax.jdo.PersistenceManagerFactory; import PMF; // ... PersistenceManager pm = PMF.get().getPersistenceManager();
Usa o PersistenceManager para armazenar, atualizar e eliminar objetos de dados, e para executar consultas da base de dados.
Quando terminar de usar a instância PersistenceManager, tem de chamar o respetivo método close()
. É um erro usar a instância PersistenceManager após chamar o respetivo método close()
.
try { // ... do stuff with pm ... } finally { pm.close(); }
Funcionalidades não suportadas do JDO 3.0
As seguintes funcionalidades da interface JDO não são suportadas pela implementação do App Engine:
- Relações de muitos-para-muitos pertencentes.
- Consultas "join". Não pode usar um campo de uma entidade secundária num filtro quando executar uma consulta no tipo principal. Tenha em atenção que pode testar o campo de relação do elemento principal diretamente na consulta através de uma chave.
- Agrupamento JDOQL e outras consultas agregadas.
- Consultas polimórficas. Não pode executar uma consulta de uma classe para obter instâncias de uma subclasse. Cada classe é representada por um tipo de entidade separado no arquivo de dados.
Desativar transações e transferir apps JDO existentes
A configuração do JDO que recomendamos usar define uma propriedade denominada datanucleus.appengine.autoCreateDatastoreTxns
como true
.
Esta é uma propriedade específica do App Engine que indica à implementação do JDO para associar transações da base de dados a transações do JDO geridas no código da aplicação. Se estiver a criar uma nova app de raiz, é provável que seja isto que quer. No entanto, se tiver uma aplicação existente baseada em JDO que queira executar no App Engine, pode usar uma configuração de persistência alternativa que defina o valor desta propriedade como 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.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 compreender por que motivo isto pode ser útil, lembre-se de que só pode operar em objetos que pertencem ao mesmo grupo de entidades numa transação. As aplicações criadas com bases de dados tradicionais assumem normalmente a disponibilidade de transações globais, que lhe permitem atualizar qualquer conjunto de registos numa transação. Uma vez que o armazenamento de dados do App Engine não suporta transações globais, o App Engine gera exceções se o seu código pressupuser a disponibilidade de transações globais. Em vez de percorrer a sua base de código (potencialmente grande) e remover todo o código de gestão de transações, pode simplesmente desativar as transações da base de dados. Isto não faz nada para resolver as suposições que o seu código faz sobre a atomicidade das modificações de vários registos, mas permite-lhe fazer com que a sua app funcione para que possa, em seguida, concentrar-se na refatoração do seu código transacional de forma incremental e conforme necessário, em vez de tudo de uma vez.