Creazione, recupero ed eliminazione dei dati in JDO

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

Rendere persistenti gli oggetti

Per archiviare un semplice oggetto dati nel datastore, chiami il metodo makePersistent() di PersistenceManager, passandogli 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 restituisce alcun risultato 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 batch a basso livello, più efficiente di una serie di singole invocazioni di makePersistent(...).

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

Chiavi

Ogni entità ha una chiave univoca su 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 Transazioni.

La chiave di un oggetto è archiviata in un campo nell'istanza. Il campo della chiave primaria viene identificato utilizzando l'annotazione @PrimaryKey.

L'app può fornire la parte 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 per tutti gli oggetti dello stesso tipo e con lo stesso gruppo di entità principale (se presente). Seleziona il comportamento desiderato della chiave utilizzando il tipo di campo e le annotazioni.

Se la classe è utilizzata come "figlio" in una relazione, il campo chiave deve essere di un tipo in grado di rappresentare l'elemento padre di un gruppo di entità: un'istanza di chiave o un valore di chiave codificato come stringa. Per saperne di più sui gruppi di entità, consulta la sezione Transazioni e per saperne di più sulle relazioni, consulta la sezione Relazioni.

Suggerimento: se l'app crea un nuovo oggetto e gli assegna lo stesso ID stringa di un altro oggetto dello stesso tipo (e dello 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, quindi crearne una se non esiste. Vedi Transazioni.

Esistono quattro tipi di campi della chiave primaria:

Lungo

Un numero intero lungo (java.lang.Long), un ID entità generato automaticamente dal datastore. Utilizzalo per gli oggetti senza gruppi di entità principali i cui ID devono essere generati automaticamente dal datastore. Il campo chiave lungo di un'istanza viene compilato quando l'istanza viene salvata.

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. Utilizzalo 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 chiave include la chiave dell'elemento padre del gruppo di entità (se presente) 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 chiave. Per informazioni su come utilizzare i gruppi di entità padre, 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 a Chiave, ma il valore è la forma di stringa codificata della chiave. Le chiavi stringa codificate consentono di scrivere l'applicazione in modo portabile e di continuare a sfruttare 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 compilato con una chiave generata dal sistema al momento del salvataggio dell'oggetto.

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 di chiavi codificate, puoi fornire l'accesso all'ID numerico o di stringa 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;

È possibile impostare un campo "gae.pk-name" 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 Long.

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 stai utilizzando una transazione, l'oggetto viene scritto al momento del commit della transazione. In caso contrario, l'oggetto viene scritto quando viene chiamato il metodo makePersistent() durante la creazione dell'oggetto o quando viene chiamato il metodo close() dell'istanza PersistenceManager se l'oggetto è in fase di aggiornamento.

Per ulteriori informazioni sulla creazione di chiavi, consulta Entità, proprietà e chiavi.

Ottenere un oggetto tramite chiave

Per recuperare un oggetto data la sua 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 costituito da 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 di uno qualsiasi dei tipi di campi chiave supportati (ID stringa, ID numerico, valore chiave, stringa chiave codificata) e può essere di 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, pertanto 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 chiavi 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 è recuperarlo e modificarlo mentre 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 tutte le modifiche apportate ai campi permanenti in Employee e aggiorna automaticamente il datastore con queste modifiche alla chiusura di PersistenceManager. Lo sa perché l'istanza Employee è "collegata" a PersistenceManager.

Puoi modificare un oggetto dopo che PersistenceManager è stato chiuso dichiarando la classe come "staccabile". A tal fine, 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 la chiusura del PersistenceManager che lo ha caricato. L'esempio seguente illustra come un oggetto scollegato può essere utile:

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 un'ottima alternativa alla creazione di oggetti Data Transfer. Per saperne di più 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à un'unica operazione di eliminazione batch di basso livello più efficiente rispetto a una serie di singole chiamate deletePersistent(...).

Se un oggetto dispone di campi contenenti oggetti secondari anch'essi permanenti, vengono eliminati anche gli oggetti secondari. Per ulteriori informazioni, consulta la sezione Relazioni.

Per eliminare tutti gli oggetti che corrispondono a una query, puoi utilizzare il comando "delete by query" di JDOQL funzionalità. Per ulteriori informazioni, consulta Eliminazione di entità per query.