Como criar, conseguir e excluir dados no JDO

Para salvar um objeto de dados JDO no armazenamento de dados, basta chamar o método makePersistent() da instância PersistenceManager. A implementação JDO do App Engine utiliza o campo de chave principal do objeto para rastrear qual entidade do armazenamento de dados corresponde ao objeto de dados. Além disso, ela pode gerar chaves para novos objetos automaticamente. É possível usar essas chaves para recuperar entidades rapidamente e construir chaves a partir de valores conhecidos (como IDs de conta).

Como tornar objetos persistentes

Para armazenar um objeto de dados simples no armazenamento de dados, chame o método makePersistent() do PersistenceManager, transmitindo a instância para ele.

PersistenceManager pm = PMF.get().getPersistenceManager();

Employee e = new Employee("Alfred", "Smith", new Date());

try {
    pm.makePersistent(e);
} finally {
    pm.close();
}

Chamar makePersistent() é síncrono e não retorna até que o objeto seja salvo e os índices sejam atualizados.

Para salvar vários objetos na JDO, chame o método makePersistentAll(...) com uma coleção de objetos. Esse método usará uma única operação de salvar em lote de nível inferior, que é mais eficiente do que uma série de invocações makePersistent(...) individuais.

Observação: se algum dos campos persistentes do objeto de dados for uma referência a outros objetos de dados persistentes e se qualquer um desses objetos nunca tiver sido salvo ou se tiver sido alterado desde o carregamento, os objetos referenciados também serão salvos no armazenamento de dados. Consulte Relacionamentos.

Chaves

Cada entidade tem uma chave exclusiva entre todas as entidades do App Engine. Uma chave completa inclui várias informações, incluindo o ID do aplicativo, o tipo e um código de entidade. As chaves também contêm informações sobre grupos de entidades. Consulte Transações para saber mais.

A chave de um objeto é armazenada em um campo da instância. O campo de chave principal é identificado através do uso da anotação @PrimaryKey.

O aplicativo pode fornecer a porção do código da chave como uma string quando o objeto for criado ou pode permitir que o armazenamento de dados gere um código numérico automaticamente. A chave completa precisa ser exclusiva entre todas as entidades do armazenamento de dados. Em outras palavras, um objeto precisa ter um código exclusivo entre todos os objetos do mesmo tipo e com o mesmo pai de grupo de entidades (se houver). Use o tipo do campo e anotações para selecionar o comportamento desejado para a chave.

Se a classe for usada como uma classe "filho" em um relacionamento, o campo de chave precisa ser de um tipo capaz de representar um pai de grupo de entidades: uma instância Key ou um valor Key codificado como uma string. Consulte Transações, para saber mais sobre grupos de entidades e Relacionamentos, para saber mais sobre relacionamentos.

Dica: se o aplicativo criar um novo objeto e der a ele o mesmo código de string de outro objeto do mesmo tipo (e mesmo pai de grupo de entidades), salvar o novo objeto substituirá o outro objeto no armazenamento de dados. Para detectar se um código de string já está em uso antes de criar um novo objeto, use uma transação para tentar receber uma entidade com o código e depois criar outra, caso esta não exista. Consulte Transações.

Há quatro tipos de campos de chave principais:

Longo

Um número inteiro longo (java.lang.Long), um ID de entidade gerado automaticamente pelo armazenamento de dados. Use isso para objetos sem pais de grupos de entidades em que os códigos precisem ser gerados automaticamente pelo armazenamento de dados. O campo de chave longo de uma instância é preenchido quando a instância é salva.

import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

// ...
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Long id;
String não codificada

Uma string (java.lang.String), um ID de entidade ("nome da chave") fornecido pelo aplicativo quando o objeto é criado. Use isso para objetos sem pais de grupos de entidades com códigos que precisem ser fornecidos pelo aplicativo. O aplicativo define esse campo com o código desejado antes de salvar.

import javax.jdo.annotations.PrimaryKey;

// ...
    @PrimaryKey
    private String name;
Chave

Uma instância Chave (com.google.appengine.api.datastore.Key). O valor chave inclui a chave do pai do grupo de entidades (se houver) e o ID de string atribuído pelo aplicativo ou o ID numérico gerado pelo sistema. Para criar o objeto com um código de string atribuído pelo aplicativo, você precisa criar o valor Key com o código e definir o campo para ele. Para criar o objeto com um código numérico atribuído pelo sistema, o campo de chave precisa ser nulo. Para saber mais sobre como usar os pais do grupo de entidades, consulte Transações.

import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
import com.google.appengine.api.datastore.Key;

// ...
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key key;

    public void setKey(Key key) {
        this.key = key;
    }

O aplicativo pode criar uma instância Key usando a classe KeyFactory:

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;

// ...
        Key key = KeyFactory.createKey(Employee.class.getSimpleName(), "Alfred.Smith@example.com");
        Employee e = new Employee();
        e.setKey(key);
        pm.makePersistent(e);
Chave como string codificada

Semelhante à chave, mas o valor é a forma de string codificada da chave. Com as chaves de string codificada, é possível gravar seu aplicativo de maneira portável e aproveitar os grupos de entidade do armazenamento de dados do App Engine.

import javax.jdo.annotations.Extension;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

// ...
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    @Extension(vendorName="datanucleus", key="gae.encoded-pk", value="true")
    private String encodedKey;

O aplicativo pode preencher esse valor antes de salvar usando uma chave com um nome ou pode deixá-lo nulo. Se o campo de chave codificada for nulo, ele será preenchido com uma chave gerada pelo sistema quando o objeto for salvo.

Instâncias Chave podem ser convertidas de e para a representação em string codificada usando os métodos KeyFactory keyToString() e stringToKey().

Ao usar strings de chave codificada, é possível fornecer acesso ao código em string ou valor numérico de um objeto com um campo adicional:

    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    @Extension(vendorName="datanucleus", key="gae.encoded-pk", value="true")
    private String encodedKey;

    @Persistent
    @Extension(vendorName="datanucleus", key="gae.pk-name", value="true")
    private String keyName;

    // OR:

    @Persistent
    @Extension(vendorName="datanucleus", key="gae.pk-id", value="true")
    private Long keyId;

Um campo "gae.pk-name" pode ser definido com um nome de chave antes de salvar o objeto. Quando o objeto é salvo, o campo de chave codificada é preenchido com a chave completa que inclui o nome de chave. Seu tipo deve ser String.

Um campo "gae.pk-id" é preenchido quando o objeto é salvo e não pode ser modificado. Seu tipo precisa ser Longo.

Quando um novo objeto com uma chave gerada (uma chave de campo que usa valueStrategy = IdGeneratorStrategy.IDENTITY) é criado, valor de chave dele é iniciado por null. O campo de chave é preenchido quando o objeto é gravado no armazenamento de dados. Se estiver usando uma transação, o objeto será gravado no momento em que a transação for executada. Caso contrário, o objeto será gravado quando o método makePersistent() for chamado, se o objeto for criado, ou quando o método close() da instância PersistenceManager for chamado se o objeto for atualizado.

Para saber mais sobre como criar chaves, consulte Entidades, propriedades e chaves.

Como conseguir um objeto por meio da chave

Para recuperar um objeto usando sua chave, use o método getObjectById() do PersistenceManager. O método toma a classe do objeto e a chave:

        Key k = KeyFactory.createKey(Employee.class.getSimpleName(), "Alfred.Smith@example.com");
        Employee e = pm.getObjectById(Employee.class, k);

Se a classe usa um campo de chave que é um ID de string não codificado (String) ou um ID numérico (Long ), getObjectByID() pode tomar o valor simples como parâmetro de chave:

        Employee e = pm.getObjectById(Employee.class, "Alfred.Smith@example.com");

O argumento de chave pode ser de qualquer um dos tipos de campo de chave compatíveis (código de string, código numérico, valor Key, string de chave codificada) e pode ser de um tipo diferente do campo de chave da classe. O App Engine precisa ser capaz de derivar a chave completa a partir do nome de classe e do valor fornecido. Os códigos de string e numéricos são exclusivos. Por isso, uma chamada usando um código numérico nunca retorna uma entidade com um código de string. Se for usado um valor Key ou uma string de chave codificada, a chave precisará se referir a uma entidade em que o tipo seja representado pela classe.

Como atualizar um objeto

Uma maneira de atualizar um objeto com JDO é buscar o objeto e depois modificá-lo enquanto o PersistenceManager que retornou o objeto ainda está aberto. As alterações são mantidas quando o PersistenceManager é fechado. Exemplo:

public void updateEmployeeTitle(User user, String newTitle) {
    PersistenceManager pm = PMF.get().getPersistenceManager();
    try {
        Employee e = pm.getObjectById(Employee.class, user.getEmail());
        if (titleChangeIsAuthorized(e, newTitle) {
            e.setTitle(newTitle);
        } else {
            throw new UnauthorizedTitleChangeException(e, newTitle);
        }
    } finally {
        pm.close();
    }
}

Como a instância Employee foi retornada pelo PersistenceManager, o PersistenceManager sabe de quaisquer modificações que tenham sido feitas nos campos persistentes do Employee e atualiza o armazenamento de dados automaticamente com essas modificações quando o PersistenceManager é fechado. Isso ocorre porque a instância "Employee" está "vinculada" ao PersistenceManager.

É possível modificar um objeto depois que o PersistenceManager é fechado declarando a classe como "removível". Para fazer isso, adicione o atributo detachable à anotação @PersistenceCapable:

import javax.jdo.annotations.PersistenceCapable;

@PersistenceCapable(detachable="true")
public class Employee {
    // ...
}

Agora, você pode ler e gravar os campos de um objeto "Employee" depois que o PersistenceManager que o carregou é fechado. O exemplo a seguir ilustra como um objeto desanexado pode ser útil:

public Employee getEmployee(User user) {
    PersistenceManager pm = PMF.get().getPersistenceManager();
    Employee employee, detached = null;
    try {
        employee = pm.getObjectById(Employee.class,
            "Alfred.Smith@example.com");

        // If you're using transactions, you can call
        // pm.setDetachAllOnCommit(true) before committing to automatically
        // detach all objects without calls to detachCopy or detachCopyAll.
        detached = pm.detachCopy(employee);
    } finally {
        pm.close();
    }
    return detached;
}

public void updateEmployeeTitle(Employee e, String newTitle) {
    if (titleChangeIsAuthorized(e, newTitle) {
        e.setTitle(newTitle);
        PersistenceManager pm = PMF.get().getPersistenceManager();
        try {
            pm.makePersistent(e);
        } finally {
            pm.close();
        }
    } else {
        throw new UnauthorizedTitleChangeException(e, newTitle);
    }
}

Objetos desanexados são uma boa alternativa para a criação de objetos de transferência de dados. Para saber mais sobre como trabalhar com objetos desanexados, consulte a Documentação do DataNucleus.

Como excluir um objeto

Para excluir um objeto do armazenamento de dados, chame o método deletePersistent() do PersistenceManager com o objeto:

pm.deletePersistent(e);

Para excluir vários objetos na JDO, chame o método deletePersistentAll(...) com uma coleção de objetos. Esse método usará uma única operação de exclusão em lote de nível inferior, que é mais eficiente do que uma série de invocações deletePersistent(...) individuais.

Se um objeto tiver campos contendo objetos filhos que também são persistentes, os objetos filhos também serão excluídos. Consulte Relacionamentos para saber mais.

Para excluir todos os objetos que correspondem a uma consulta, você pode usar o recurso "excluir por consulta" do JDOQL. Consulte Como excluir entidades por consulta para saber mais.