Crea, obtén y borra datos en JDO

Para guardar un objeto de datos JDO en el almacén de datos, basta con llamar al método makePersistent() de la instancia de PersistenceManager. La implementación JDO de App Engine usa el campo de clave primaria del objeto para realizar un seguimiento de qué entidad del almacén de datos corresponde al objeto de datos, y puede generar claves para objetos nuevos automáticamente. Puedes usar claves para recuperar entidades con rapidez y también construir claves a partir de valores conocidos (por ejemplo, los ID de cuenta).

Cómo hacer que los objetos sean persistentes

Para almacenar un objeto de datos simple en el almacén de datos, se llama al método makePersistent() de PersistenceManager, pasándole la instancia.

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

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

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

La llamada a makePersistent() es síncrona y no se muestra hasta que se guarda el objeto y se actualizan los índices.

Para guardar objetos múltiples en JDO, llama al método makePersistentAll(...) con una colección de objetos. Este método usa una sola operación de guardado por lotes de bajo nivel que es más eficiente que una serie de invocaciones individuales a makePersistent(...).

Nota: Si alguno de los campos persistentes del objeto de datos es una referencia a otros objetos de datos persistentes, y alguno de esos objetos nunca se guardó ni se modificó desde su carga, los objetos referenciados también se guardan en el almacén de datos. Consulta Relaciones.

Claves

Cada entidad tiene una clave que es única entre todas las entidades de App Engine. Una clave completa incluye distintos elementos, como el ID de aplicación, el tipo y un ID de entidad. (Las claves también contienen datos acerca de los grupos de entidad; consulta Transacciones para obtener más información).

La clave de un objeto se almacena en un campo de la instancia. El campo de clave primaria se identifica mediante la anotación @PrimaryKey.

La aplicación puede proveer la porción de la clave correspondiente al ID como una string cuando se crea el objeto, o bien permitir que el almacén de datos genere un ID numérico de forma automática. La clave completa debe ser única entre todas las entidades del almacén de datos. En otras palabras, un objeto debe tener un ID que sea único entre todos los objetos del mismo tipo y con el mismo grupo de entidad principal (si lo hubiera). Para seleccionar el comportamiento deseado de la clave, se usa el tipo del campo y las anotaciones.

Si la clase se usa como clase "secundaria" en una relación, el campo de clave debe ser de un tipo que pueda representar el grupo de entidad principal: ya sea una instancia de Key o un valor de Key codificado como string. Consulta Transacciones para obtener más información sobre grupos de entidad y Relaciones si quieres obtener más información sobre las relaciones.

Sugerencia: Si la aplicación crea un objeto nuevo y le asigna el mismo ID de string que el de otro objeto del mismo tipo (y con el mismo grupo de entidad principal), al guardar el objeto nuevo se reemplaza el otro objeto en el almacén de datos. Si quieres detectar si un ID de string ya está en uso antes de crear un objeto nuevo, puedes usar una transacción para intentar obtener una entidad con un ID determinado, y luego crear el objeto si el ID no existe. Consulta Transacciones.

Hay cuatro tipos de campos de clave primaria:

Long

Un número entero largo (java.lang.Long), un ID de entidad generado automáticamente por el almacén de datos. Este tipo de campo se usa para objetos sin grupo de entidad principal y cuyos ID deben generarse automáticamente por el almacén de datos. El campo de clave largo de una instancia se carga cuando se guarda la instancia.

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

// ...
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Long id;
String sin codificación

Una string (java.lang.String), un ID de entidad (“nombre de la clave”) que proporciona la aplicación cuando se crea el objeto. Este tipo de campo se usa para objetos sin grupo de entidad principal y cuyos ID deben generarse por la aplicación. La aplicación configura este campo con el ID deseado antes de guardar el objeto.

import javax.jdo.annotations.PrimaryKey;

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

Una instancia de Key (com.google.appengine.api.datastore.Key). El valor de Key incluye la clave del grupo de entidad principal (si lo hubiera) y el ID de string que asigna la aplicación o el ID numérico que genera el sistema. Para crear el objeto con un ID de string asignado por la aplicación, se debe crear el valor de Key con el ID y configurar el campo con ese valor. Para crear el objeto con un ID numérico asignado por el sistema, se deja el campo de clave con valor nulo. (Para obtener información sobre cómo usar grupos de entidad principales, consulta Transacciones).

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;
    }

La aplicación puede crear una instancia de Key con la clase 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);
Key como string codificada

Similar a Key, pero el valor es la forma de string codificada de la clave. Las claves de string codificadas te permiten programar tu aplicación de forma portátil y seguir aprovechando los grupos de entidad del almacén de datos de 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;

La aplicación puede cargar este valor antes de guardar el objeto usando una clave con nombre, o bien dejar el valor nulo. Si el campo de clave codificada es nulo, se carga con una clave generada por el sistema cuando se guarda el objeto.

Es posible convertir las instancias de Key en la representación de string codificada y viceversa mediante los métodos keyToString() y stringToKey() de KeyFactory.

Cuando usas strings de clave codificadas, puedes proporcionar acceso al ID numérico o de string de un objeto mediante un 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;

Se puede establecer un campo "gae.pk-name" con un nombre de clave antes de guardar el objeto. Cuando se guarda el objeto, el campo de clave codificada se carga con la clave completa que incluye su nombre. Su tipo debe ser String.

Un campo "gae.pk-id" se propaga cuando se guarda el objeto y no se puede modificar. Debe ser del tipo Long.

Cuando se crea un objeto nuevo con una clave generada (un campo de clave que usa valueStrategy = IdGeneratorStrategy.IDENTITY), su valor de clave comienza con null. El campo de clave se carga cuando se guarda el objeto en el almacén de datos. Si usas una transacción, el objeto se guarda cuando se confirma la transacción. De lo contrario, el objeto se escribe cuando se llama al método makePersistent() si el objeto se está creando, o cuando se llama al método close() de la instancia de PersistenceManager si el objeto se está actualizando.

Para obtener más información sobre la creación de claves, consulta Entidades, propiedades y claves.

Cómo obtener un objeto por clave

Para recuperar un objeto por clave, usa el método getObjectById() de PersistenceManager. Este método toma la clase del objeto y la clave:

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

Si la clase usa un campo de clave que es un ID de string sin codificación (String) o un ID numérico (Long), getObjectByID() puede tomar el valor simple como una parámetro clave:

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

El argumento de clave puede ser de cualquiera de los tipos de campo de clave admitidos (ID de string, ID numérico, valor de Key, string de clave codificada); a su vez, este tipo puede no coincidir con el del campo de clave de la clase. App Engine debe ser capaz de obtener la clave completa a partir del nombre de la clase y el valor provisto. Los ID de string y los ID numéricos son excluyentes, por lo que una llamada que usa un ID numérico nunca devuelve una entidad con un ID de string. Si se usa un valor de Key o una string de clave codificada, la clave debe hacer referencia a una entidad cuyo tipo esté representado por la clase.

Cómo actualizar un objeto

Una forma de actualizar un objeto con JDO es recuperarlo y luego modificarlo mientras la instancia de PersistenceManager que lo devolvió sigue abierta. Los cambios se mantienen cuando se cierra la instancia de PersistenceManager. Por ejemplo:

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();
    }
}

PersistenceManager mostró la instancia de Employee, por lo que conoce todas las modificaciones hechas en los campos persistentes de Employee y con ellas actualiza el almacén de datos de forma automática cuando se cierra. Esto se debe a que la instancia Employee está "vinculada" con la instancia de PersistenceManager.

Puedes modificar un objeto luego de que se cierre PersistenceManager si declaras la clase como "separable". Para eso, agrega el atributo detachable a la anotación @PersistenceCapable:

import javax.jdo.annotations.PersistenceCapable;

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

Ya puedes leer y escribir los campos de un objeto Employee después de que se cierre la instancia de PersistenceManager que lo cargó. El ejemplo siguiente ilustra la utilidad que puede tener un objeto separado:

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);
    }
}

Los objetos separados son una buena alternativa a la creación de objetos de transferencia de datos. Para obtener más información sobre el trabajo con objetos separados, consulta la documentación de DataNucleus.

Cómo borrar un objeto

Para borrar un objeto del almacén de datos, llama al método deletePersistent() de PersistenceManager con el objeto:

pm.deletePersistent(e);

Para borrar objetos múltiples en JDO, llama al método deletePersistentAll(...) con una colección de objetos. Este método usa una sola operación de eliminación por lotes de bajo nivel que es más eficiente que una serie de invocaciones individuales a deletePersistent(...).

Si un objeto tiene campos que contienen objetos secundarios que a su vez son persistentes, estos también se borran. Consulta Relaciones para obtener más información.

Para borrar todos los objetos que coinciden con una consulta, puedes usar la función de "borrar por consulta" de JDOQL. Consulta Borra entidades por consulta para obtener más información.