Gli oggetti di dati in Datastore sono noti come entità. Un'entità ha una o più proprietà denominate, ognuna delle quali può avere uno o più valori. Le entità dello stesso tipo non devono avere le stesse proprietà e i valori di un'entità per una determinata proprietà non devono essere tutti dello stesso tipo di dati. Se necessario, un'applicazione può stabilire e applicare queste limitazioni nel propriomodello dei datii.
Datastore supporta diversi tipi di dati per i valori delle proprietà. Questi includono, tra gli altri:
- Numeri interi
- Numeri in virgola mobile
- Stringhe
- Date
- Dati binari
Per un elenco completo dei tipi, vedi Proprietà e tipi di valori.
Ogni entità in Datastore ha una chiave che la identifica in modo univoco. La chiave è costituita dai seguenti componenti:
- Lo spazio dei nomi dell'entità, che consente il multitenancy
- Il tipo dell'entità, che la classifica ai fini delle query Datastore
- Un identificatore per la singola entità, che può essere
- una stringa nome chiave
- un ID numerico intero
- Un percorso degli antenati facoltativo che individua l'entità all'interno della gerarchia Datastore
Un'applicazione può recuperare una singola entità da Datastore utilizzando la chiave dell'entità oppure può recuperare una o più entità eseguendo una query in base alle chiavi o ai valori delle proprietà delle entità.
L'SDK Java App Engine include una semplice API, fornita nel pacchetto com.google.appengine.api.datastore
, che supporta direttamente le funzionalità di Datastore. Tutti gli esempi in questo documento si basano su questa API di basso livello. Puoi scegliere di utilizzarla direttamente nella tua applicazione o come base per creare il tuo livello di gestione dei dati.
Datastore non impone restrizioni alla struttura delle entità, ad esempio se una determinata proprietà ha un valore di un tipo specifico. Questa attività viene lasciata all'applicazione.
Tipi e identificatori
Ogni entità Datastore è di un particolare tipo,che la classifica ai fini delle query: ad esempio, un'applicazione per le risorse umane potrebbe rappresentare ogni dipendente di un'azienda con un'entità di tipo Employee
. Nell'API Java Datastore, specifichi il tipo di un'entità quando la crei, come argomento del costruttore Entity()
. Tutti i nomi dei tipi che iniziano con due trattini bassi (__
) sono riservati e non possono essere utilizzati.
L'esempio seguente crea un'entità di tipo Employee
, compila i valori delle proprietà e la salva in Datastore:
Oltre a un tipo, ogni entità ha un identificatore, assegnato al momento della creazione dell'entità. Poiché fa parte della chiave dell'entità, l'identificatore è associato in modo permanente all'entità e non può essere modificato. Può essere assegnato in due modi:
- La tua applicazione può specificare una propria stringa key name per l'entità.
- Puoi fare in modo che Datastore assegni automaticamente all'entità un ID numerico intero.
Per assegnare un nome di chiave a un'entità, fornisci il nome come secondo argomento al costruttore quando crei l'entità:
Per fare in modo che Datastore assegni automaticamente un ID numerico, ometti questo argomento:
Assegnazione degli identificatori
Datastore può essere configurato per generare ID automatici utilizzando due diverse norme per gli ID automatici:
- Il criterio
default
genera una sequenza casuale di ID non utilizzati distribuiti in modo approssimativamente uniforme. Ogni ID può contenere fino a 16 cifre decimali. - Il criterio
legacy
crea una sequenza di ID interi più piccoli non consecutivi.
Se vuoi mostrare gli ID entità all'utente e/o dipendere dal loro ordine, la cosa migliore da fare è utilizzare l'allocazione manuale.
Datastore genera una sequenza casuale di ID non utilizzati distribuiti in modo approssimativamente uniforme. Ogni ID può contenere fino a 16 cifre decimali.
Percorsi degli antenati
Le entità in Cloud Datastore formano uno spazio strutturato gerarchicamente simile alla struttura di directory di un file system. Quando crei un'entità, puoi designare facoltativamente un'altra entità come genitore; la nuova entità è un elemento secondario dell'entità padre (tieni presente che, a differenza di un file system, l'entità genitore non deve esistere). Un'entità senza un elemento principale è un'entità radice. L'associazione tra un'entità e la relativa entità principale è permanente e non può essere modificata dopo la creazione dell'entità. Cloud Datastore non assegnerà mai lo stesso ID numerico a due entità con lo stesso elemento principale o a due entità radice (quelle senza un elemento principale).
Il genitore di un'entità, il genitore del genitore e così via in modo ricorsivo sono i suoi antenati; i suoi figli, i figli dei figli e così via sono i suoi discendenti. Un'entità base e tutti i relativi discendenti appartengono allo stesso gruppo di entità. La sequenza di entità che inizia con un'entità principale e procede da genitore a figlio, fino a un'entità specifica, costituisce il percorso degli antenati dell'entità. La chiave completa che identifica l'entità è costituita da una sequenza di coppie tipo-identificatore che specificano il percorso degli antenati e terminano con quelli dell'entità stessa:
[Person:GreatGrandpa, Person:Grandpa, Person:Dad, Person:Me]
Per unentità base, il percorso degli antenati è vuoto e la chiave è costituita unicamente dal tipo e dall'identificatore dell'entità:
[Person:GreatGrandpa]
Questo concetto è illustrato nel seguente diagramma:
Per designare l'elemento principale di un'entità, fornisci la chiave dell'entità principale come argomento
al costruttore
Entity()
quando crei l'entità secondaria. Puoi ottenere la chiave chiamando il metodo getKey()
dell'entità padre:
Se la nuova entità ha anche un nome della chiave, fornisci il nome della chiave come secondo argomento del costruttore Entity()
e la chiave dell'entità padre come terzo argomento:
Transazioni e gruppi di entità
Ogni tentativo di creare, aggiornare o eliminare un'entità avviene nel contesto di una transazione. Una singola transazione può includere un numero qualsiasi di queste operazioni. Per mantenere la coerenza dei dati, la transazione garantisce che tutte le operazioni che contiene vengano applicate a Datastore come unità o, se una delle operazioni non va a buon fine, che nessuna di queste venga applicata. Inoltre, tutte le letture a elevata coerenza (query o recuperi degli antenati) eseguite all'interno della stessa transazione osservano uno snapshot coerente dei dati.
Come accennato in precedenza, un gruppo di entità è un insieme di entità collegate tramite la discendenza a un elemento radice comune. L'organizzazione dei dati in gruppi di entità può limitare le transazioni che possono essere eseguite:
- Tutti i dati a cui accede una transazione devono essere contenuti in al massimo 25 gruppi di entità.
- Se vuoi utilizzare le query all'interno di una transazione, i tuoi dati devono essere organizzati in gruppi di entità in modo da poter specificare i filtri predecessore che corrispondono ai dati corretti.
- Esiste un limite di velocità effettiva di scrittura di circa una transazione al secondo all'interno di un singolo gruppo di entità. Questa limitazione esiste perché Datastore esegue la replica sincrona senza master di ogni gruppo di entità su un'ampia area geografica per garantire elevata affidabilità e tolleranza agli errori.
In molte applicazioni, è accettabile utilizzare la coerenza finale (ad esempio una query non discendente che interessa più gruppi di entità, che a volte può restituire dati leggermente obsoleti) quando si ottiene una visione generale di dati non correlati e quindi utilizzare la elevata coerenza (una query da predecessore o una get
di una singola entità) quando si visualizza o si modifica un singolo insieme di dati altamente correlati. In queste applicazioni, in genere è consigliabile utilizzare un gruppo di entità separato per ogni insieme di dati strettamente correlati.
Per maggiori informazioni, consulta la pagina Strutturazione per una coerenza elevata.
Proprietà e tipi di valori
I valori dei dati associati a un'entità sono costituiti da una o più proprietà. Ogni proprietà ha un nome e uno o più valori. Una proprietà può avere valori di più di un tipo e due entità possono avere valori di tipi diversi per la stessa proprietà. Le proprietà possono essere indicizzate o non indicizzate (le query che ordinano o filtrano in base a una proprietà P ignorano le entità in cui P non è indicizzata). Un'entità può avere al massimo 20.000 proprietà indicizzate.
Sono supportati i seguenti tipi di valori:
Tipo di valore | Tipo/i Java | Ordinamento | Note |
---|---|---|---|
Numero intero | short int long java.lang.Short java.lang.Integer java.lang.Long |
Numerico | Memorizzato come numero intero lungo, poi convertito nel tipo di campo Valori fuori intervallo |
Numero in virgola mobile | float double java.lang.Float java.lang.Double |
Numerico | Doppia precisione a 64 bit, IEEE 754 |
Booleano | boolean java.lang.Boolean |
false <true |
|
Stringa di testo (breve) | java.lang.String |
Unicode | Fino a 1500 byte I valori superiori a 1500 byte generano IllegalArgumentException |
Stringa di testo (lunga) | com.google.appengine.api.datastore.Text |
Nessuno | Fino a 1 megabyte Non indicizzato |
Stringa di byte (breve) | com.google.appengine.api.datastore.ShortBlob |
Ordine dei byte | Fino a 1500 byte I valori superiori a 1500 byte generano IllegalArgumentException |
Stringa di byte (lunga) | com.google.appengine.api.datastore.Blob |
Nessuno | Fino a 1 megabyte Non indicizzato |
Data e ora | java.util.Date |
Cronologica | |
Punto geografico | com.google.appengine.api.datastore.GeoPt |
Per latitudine, poi per longitudine |
|
Indirizzo postale | com.google.appengine.api.datastore.PostalAddress |
Unicode | |
Numero di telefono | com.google.appengine.api.datastore.PhoneNumber |
Unicode | |
Indirizzo email | com.google.appengine.api.datastore.Email |
Unicode | |
Utente Account Google | com.google.appengine.api.users.User |
Indirizzo email in ordine Unicode |
|
Handle di messaggistica istantanea | com.google.appengine.api.datastore.IMHandle |
Unicode | |
Link | com.google.appengine.api.datastore.Link |
Unicode | |
Categoria | com.google.appengine.api.datastore.Category |
Unicode | |
Valutazione | com.google.appengine.api.datastore.Rating |
Numerico | |
Chiave Datastore | com.google.appengine.api.datastore.Key o l'oggetto a cui viene fatto riferimento (come elemento secondario) |
Per elementi del percorso (tipo, identificatore, tipo, identificatore...) |
Fino a 1500 byte I valori superiori a 1500 byte generano IllegalArgumentException |
Chiave Blobstore | com.google.appengine.api.blobstore.BlobKey |
Ordine dei byte | |
Entità incorporata | com.google.appengine.api.datastore.EmbeddedEntity |
Nessuno | Non indicizzato |
Null | null |
Nessuno |
Importante:ti consigliamo vivamente di evitare di memorizzare un users.User
come valore della proprietà, perché include l'indirizzo email insieme all'ID univoco. Se un utente cambia il proprio indirizzo email e confronti il vecchio valore user.User
memorizzato con il nuovo valore user.User
, non corrisponderanno. Utilizza invece il User
valore dell'ID utente come identificatore univoco stabile dell'utente.
Per le stringhe di testo e i dati binari non codificati (stringhe di byte), Datastore supporta due tipi di valori:
- Le stringhe brevi (fino a 1500 byte) vengono indicizzate e possono essere utilizzate nelle condizioni di filtro delle query e negli ordinamenti.
- Le stringhe lunghe (fino a 1 megabyte) non vengono indicizzate e non possono essere utilizzate nei filtri delle query e negli ordinamenti.
Blob
nell'API Datastore. Questo tipo non è correlato ai blob utilizzati nell'API Blobstore.
Quando una query coinvolge una proprietà con valori di tipi misti, Datastore utilizza un ordinamento deterministico basato sulle rappresentazioni interne:
- Valori null
- Numeri in virgola fissa
- Numeri interi
- Date e ore
- Valutazioni
- Valori booleani
- Sequenze di byte
- Stringa di byte
- Stringa Unicode
- Chiavi Blobstore
- Numeri in virgola mobile
- Punti geografici
- Utenti con account Google
- Chiavi Datastore
Poiché le stringhe di testo lunghe, le stringhe di byte lunghe e le entità incorporate non vengono indicizzate, non hanno un ordine definito.
Utilizzo delle entità
Le applicazioni possono utilizzare l'API Datastore per creare, recuperare, aggiornare ed eliminare le entità. Se l'applicazione conosce la chiave completa di un'entità (o può derivarla dalla chiave, dal tipo e dall'identificatore padre), può utilizzarla per operare direttamente sull'entità. Un'applicazione può anche ottenere la chiave di un'entità come risultato di una query Datastore. Per ulteriori informazioni, consulta la pagina Query Datastore.
L'API Java Datastore utilizza i metodi dell'interfaccia DatastoreService
per operare sulle entità. Ottieni un oggetto DatastoreService
chiamando il metodo statico DatastoreServiceFactory.getDatastoreService()
:
Creare un'entità
Puoi creare una nuova entità costruendo un'istanza della classe Entity
, fornendo il tipo di entità come argomento al costruttore Entity()
.
Dopo aver compilato le proprietà dell'entità, se necessario, salvala
nel datastore passandola come argomento al metodo
DatastoreService.put()
. Puoi specificare il nome della chiave dell'entità passandolo come secondo argomento al costruttore:
Se non fornisci un nome della chiave, Datastore genererà automaticamente un ID numerico per la chiave dell'entità:
Recupero di un'entità
Per recuperare un'entità identificata da una determinata chiave, passa l'oggetto Key
al metodo DatastoreService.get()
:
Aggiornamento di un'entità
Per aggiornare un'entità esistente, modifica gli attributi dell'oggetto Entità, quindi passalo al metodo DatastoreService.put()
. I dati dell'oggetto sovrascrivono l'entità esistente. L'intero oggetto viene inviato a Datastore a ogni chiamata a put()
.
Eliminazione di un'entità
Data la chiave di un'entità, puoi eliminarla con il metodo DatastoreService.delete()
:
Proprietà ripetute
Puoi memorizzare più valori all'interno di una singola proprietà.
Entità incorporate
A volte può essere utile incorporare un'entità come proprietà di un'altra entità. Ciò può essere utile, ad esempio, per creare una struttura gerarchica di valori delle proprietà all'interno di un'entità. La classe Java EmbeddedEntity
ti consente di:
Quando un'entità incorporata è inclusa negli indici, puoi eseguire query sulle proprietà secondarie. Se escludi un'entità incorporata dall'indicizzazione, anche tutte le proprietà secondarie vengono escluse dall'indicizzazione. Puoi, se vuoi, associare una chiave a un'entità incorporata, ma (a differenza di un'entità completa) la chiave non è obbligatoria e, anche se presente, non può essere utilizzata per recuperare l'entità.
Anziché compilare manualmente le proprietà dell'entità incorporata, puoi utilizzare il metodo setPropertiesFrom()
per copiarle da un'entità esistente:
In un secondo momento, puoi utilizzare lo stesso metodo per recuperare l'entità originale dall'entità incorporata:
Operazioni batch
I metodi DatastoreService
put()
, get()
e delete()
(e le relative controparti AsyncDatastoreService) hanno versioni batch che accettano un oggetto iterabile (della classe Entity
per put()
, Key
per get()
e delete()
) e lo utilizzano per operare su più entità in una singola chiamata Datastore:
Queste operazioni batch raggruppano tutte le entità o le chiavi per gruppo di entità e poi eseguono l'operazione richiesta su ogni gruppo di entità in parallelo. Queste chiamate batch sono più veloci rispetto all'esecuzione di chiamate separate per ogni singola entità, perché comportano il sovraccarico di una sola chiamata di servizio. Se sono coinvolti più gruppi di entità, il lavoro per tutti i gruppi viene eseguito in parallelo sul lato server.
Generazione delle chiavi
Le applicazioni possono utilizzare la classe KeyFactory
per creare un oggetto Key
per un'entità da componenti noti, come il tipo e l'identificatore dell'entità. Per un'entità senza elemento principale, passa il tipo e l'identificatore (una stringa del nome della chiave o un ID numerico) al metodo statico KeyFactory.createKey()
per creare la chiave. Gli esempi seguenti creano una chiave per un'entità di tipo Person
con nome della chiave "GreatGrandpa"
o ID numerico 74219
:
Se la chiave include un componente di percorso, puoi utilizzare la classe helper KeyFactory.Builder
per creare il percorso. Il metodo addChild
di questa classe aggiunge una singola entità al percorso e restituisce il builder stesso, in modo da poter concatenare una serie di chiamate, a partire dall'entità base, per creare il percorso un'entità alla volta. Dopo aver creato il percorso completo, chiama getKey
per recuperare la chiave risultante:
La classe KeyFactory
include anche i metodi statici keyToString
e stringToKey
per la conversione tra le chiavi e le relative rappresentazioni di stringhe:
La rappresentazione di una chiave come stringa è "sicura per il web": non contiene caratteri considerati speciali in HTML o negli URL.
Utilizzo di un elenco vuoto
In passato, Datastore non aveva una rappresentazione per una proprietà che rappresentava un elenco vuoto. L'SDK Java ha risolto il problema memorizzando le raccolte vuote come valori nulli, quindi non è possibile distinguere tra valori nulli ed elenchi vuoti. Per mantenere la compatibilità con le versioni precedenti, questo rimane il comportamento predefinito, riassunto come segue:- Le proprietà null vengono scritte come null in Datastore
- Le raccolte vuote vengono scritte come null in Datastore
- Un valore nullo viene letto come nullo da Datastore
- Una raccolta vuota viene letta come null.
Tuttavia, se modifichi il comportamento predefinito, l'SDK per Java supporterà l'archiviazione di elenchi vuoti. Ti consigliamo di valutare le implicazioni della modifica del comportamento predefinito della tua applicazione e poi di attivare il supporto per le liste vuote.
Per modificare il comportamento predefinito in modo da poter utilizzare elenchi vuoti, imposta la proprietà DATASTORE_EMPTY_LIST_SUPPORT durante l'inizializzazione dell'app nel seguente modo:
System.setProperty(DatastoreServiceConfig.DATASTORE_EMPTY_LIST_SUPPORT, Boolean.TRUE.toString());
Se questa proprietà è impostata su true
come mostrato sopra:
- Le proprietà null vengono scritte come null in Datastore
- Le raccolte vuote vengono scritte come elenchi vuoti in Datastore
- Un valore nullo viene letto come nullo da Datastore
- Quando si legge da Datastore, un elenco vuoto viene restituito come raccolta vuota.