Creazione, recupero ed eliminazione di dati in JDO

Per salvare un oggetto dati JDO nel datastore, è sufficiente chiamare il metodo makePersistent() dell'istanza PersistenceManager. L'implementazione JDO di App Engine utilizza il campo di chiave primaria dell'oggetto per tenere traccia dell'entità datastore corrispondente all'oggetto dati e per generare chiavi per nuovi oggetti automaticamente. Puoi utilizzare le chiavi per recuperare rapidamente le entità e creare chiavi da valori noti (come gli ID account).

Rendere persistenti gli oggetti

Per archiviare un oggetto dati semplice nel datastore, devi chiamare il metodo makePersistent() di PersistenceManager, trasmettendolo l'istanza.

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

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

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

La chiamata a makePersistent() è sincrona e non viene restituita finché l'oggetto non viene salvato e gli indici non vengono aggiornati.

Per salvare più oggetti in JDO, chiama il metodo makePersistentAll(...) con una raccolta di oggetti. Questo metodo utilizzerà una singola operazione di salvataggio in batch di basso livello che è più efficiente di una serie di singole chiamate makePersistent(...).

Nota:se uno qualsiasi dei campi permanenti dell'oggetto dati è riferimenti ad altri oggetti dati permanenti e uno qualsiasi di questi oggetti non è mai stato salvato o è stato modificato dopo il caricamento, anche gli oggetti a cui viene fatto riferimento vengono salvati nel datastore. Consulta Relazioni.

Chiavi

Ogni entità ha una chiave univoca rispetto a tutte le entità in App Engine. Una chiave completa include diverse informazioni, tra cui l'ID applicazione, il tipo e un ID entità. Le chiavi contengono anche informazioni sui gruppi di entità. Per ulteriori informazioni, consulta la sezione Transazioni.

La chiave di un oggetto è archiviata in un campo dell'istanza. Puoi identificare il campo della chiave primaria utilizzando l'annotazione @PrimaryKey.

L'app può fornire la parte dell'ID della chiave come stringa al momento della creazione dell'oggetto oppure può consentire al datastore di generare automaticamente un ID numerico. La chiave completa deve essere univoca in tutte le entità nel datastore. In altre parole, un oggetto deve avere un ID univoco tra tutti gli oggetti dello stesso tipo e con lo stesso gruppo di entità padre (se presente). Puoi selezionare il comportamento desiderato della chiave utilizzando il tipo di campo e le annotazioni.

Se la classe viene utilizzata come classe "secondaria" in una relazione, il campo della chiave deve essere di un tipo in grado di rappresentare un elemento padre del gruppo di entità: un'istanza di chiave o un valore di chiave codificato come stringa. Vedi Transazioni per maggiori informazioni sui gruppi di entità e Relazioni per maggiori informazioni sulle relazioni.

Suggerimento:se l'app crea un nuovo oggetto a cui assegna lo stesso ID stringa di un altro oggetto dello stesso tipo (e lo stesso gruppo di entità principale), il salvataggio del nuovo oggetto sovrascrive l'altro oggetto nel datastore. Per rilevare se un ID stringa è già in uso prima di creare un nuovo oggetto, puoi utilizzare una transazione per tentare di ottenere un'entità con un determinato ID e poi crearne una se non esiste. Consulta la sezione Transazioni.

Esistono quattro tipi di campi di chiave primaria:

Lungo

Un numero intero lungo (java.lang.Long), un ID entità generato automaticamente dal datastore. Da utilizzare per gli oggetti senza gruppi di entità principali i cui ID devono essere generati automaticamente dal datastore. Il campo della chiave lunga di un'istanza viene compilato al momento del salvataggio dell'istanza.

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

// ...
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Long id;
Stringa non codificata

Una stringa (java.lang.String), un ID entità ("nome chiave") fornito dall'applicazione al momento della creazione dell'oggetto. Da utilizzare per gli oggetti senza gruppi di entità principali i cui ID devono essere forniti dall'applicazione. L'applicazione imposta questo campo sull'ID desiderato prima del salvataggio.

import javax.jdo.annotations.PrimaryKey;

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

Un'istanza di chiave (com.google.appengine.api.datastore.Key). Il valore della chiave include la chiave dell'eventuale gruppo di entità principale e l'ID stringa assegnato dall'app o l'ID numerico generato dal sistema. Per creare l'oggetto con un ID stringa assegnato dall'app, crea il valore della chiave con l'ID e imposta il campo sul valore. Per creare l'oggetto con un ID numerico assegnato dal sistema, lascia nullo il campo della chiave. Per informazioni su come utilizzare i gruppi di entità principali, consulta Transazioni.

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

L'app può creare un'istanza Key utilizzando la 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);
Chiave come stringa codificata

Simile alla chiave, ma il valore è la forma stringa codificata della chiave. Le chiavi stringa codificate ti consentono di scrivere la tua applicazione in modo portabile e di sfruttare comunque i gruppi di entità del datastore di 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;

L'app può compilare questo valore prima del salvataggio utilizzando una chiave con un nome oppure può lasciarlo null. Se il campo della chiave codificata è nullo, il campo viene completato con una chiave generata dal sistema quando l'oggetto viene salvato.

Le istanze di chiave possono essere convertite da e verso la rappresentazione di stringa codificata utilizzando i metodi KeyFactory keyToString() e stringToKey().

Quando utilizzi stringhe chiave codificate, puoi fornire l'accesso alla stringa o all'ID numerico di un oggetto con un campo aggiuntivo:

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

Un campo "gae.pk-name" può essere impostato sul nome di una chiave prima di salvare l'oggetto. Quando l'oggetto viene salvato, il campo della chiave codificata viene compilato con la chiave completa che include il nome della chiave. Il tipo deve essere String.

Un campo "gae.pk-id" viene compilato quando l'oggetto viene salvato e non può essere modificato. Il tipo deve essere Lungo.

Quando viene creato un nuovo oggetto con una chiave generata (un campo chiave che utilizza valueStrategy = IdGeneratorStrategy.IDENTITY), il valore della chiave inizia null. Il campo chiave viene compilato quando l'oggetto viene scritto nel datastore. Se utilizzi una transazione, l'oggetto viene scritto al momento del commit della transazione. Altrimenti, l'oggetto viene scritto quando viene richiamato il metodo makePersistent() se l'oggetto è in fase di creazione o quando viene richiamato il metodo close() dell'istanza PersistenceManager se l'oggetto è in fase di aggiornamento.

Per saperne di più sulla creazione di chiavi, consulta Entità, proprietà e chiavi.

Recupero di un oggetto per chiave

Per recuperare un oggetto in base alla chiave, usa il metodo getObjectById() di PersistenceManager. Il metodo prende la classe per l'oggetto e la chiave:

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

Se la classe utilizza un campo chiave che è un ID stringa non codificato (String) o un ID numerico (Long), getObjectByID() può utilizzare il valore semplice come parametro chiave:

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

L'argomento chiave può essere qualsiasi tipo di campo chiave supportato (ID stringa, ID numerico, valore chiave, stringa chiave codificata) e può essere di un tipo diverso rispetto al campo chiave della classe. App Engine deve essere in grado di ricavare la chiave completa dal nome della classe e dal valore fornito. Gli ID stringa e gli ID numerici sono esclusivi, quindi una chiamata che utilizza un ID numerico non restituisce mai un'entità con un ID stringa. Se viene utilizzato un valore chiave o una stringa di chiave codificata, la chiave deve fare riferimento a un'entità il cui tipo è rappresentato dalla classe.

Aggiornamento di un oggetto

Un modo per aggiornare un oggetto con JDO consiste nel recuperare l'oggetto per poi modificarlo mentre il PersistenceManager che ha restituito l'oggetto è ancora aperto. Le modifiche vengono mantenute quando PersistenceManager viene chiuso. Ad esempio:

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

Poiché l'istanza Employee è stata restituita da PersistenceManager, PersistenceManager è a conoscenza di eventuali modifiche apportate ai campi permanenti sul Employee e aggiorna automaticamente il datastore con queste modifiche quando PersistenceManager viene chiuso. Lo sa perché l'istanza Employee è "collegata" a PersistenceManager.

Puoi modificare un oggetto dopo la chiusura di PersistenceManager dichiarando la classe come "rimovibile". A questo scopo, aggiungi l'attributo detachable all'annotazione @PersistenceCapable:

import javax.jdo.annotations.PersistenceCapable;

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

Ora puoi leggere e scrivere i campi di un oggetto Employee dopo che il PersistenceManager che lo ha caricato è stato chiuso. L'esempio seguente illustra l'utilità di un oggetto scollegato:

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

Gli oggetti scollegati sono una buona alternativa alla creazione di oggetti Data Transfer. Per ulteriori informazioni sull'utilizzo di oggetti scollegati, consulta la documentazione di DataNucleus.

Eliminazione di un oggetto

Per eliminare un oggetto dal datastore, chiama il metodo deletePersistent() di PersistenceManager con l'oggetto:

pm.deletePersistent(e);

Per eliminare più oggetti in JDO, chiama il metodo deletePersistentAll(...) con una raccolta di oggetti. Questo metodo utilizzerà una singola operazione di eliminazione batch di basso livello che è più efficiente di una serie di singole chiamate deletePersistent(...).

Se un oggetto contiene campi contenenti oggetti secondari che sono permanenti, vengono eliminati anche questi oggetti. Per ulteriori informazioni, consulta Relazioni.

Per eliminare tutti gli oggetti che corrispondono a una query, puoi utilizzare la funzionalità "Elimina per query" di JDOQL. Per ulteriori informazioni, consulta Eliminazione delle entità per query.