Questo documento è incentrato sull'utilizzo del framework di persistenza Java Data Objects (JDO) per le query App Engine Datastore. Per informazioni più generali sulle query, consulta la pagina principale Query Datastore.
Una query recupera entità da Datastore che soddisfano un insieme specificato di condizioni. La query opera su entità di un determinato tipo; può specificare filtri sui valori delle proprietà, sulle chiavi e sugli antenati delle entità e può restituire zero o più entità come risultati. Una query può anche specificare ordini di ordinamento per sequenziare i risultati in base ai valori delle proprietà. I risultati includono tutte le entità che hanno almeno un valore (eventualmente null) per ogni proprietà denominata nei filtri e negli ordinamenti e i cui valori delle proprietà soddisfano tutti i criteri di filtro specificati. La query può restituire entità intere, entità proiettate, o solo chiavi di entità.
Una query tipica include quanto segue:
- Un tipo di entità a cui si applica la query
- Zero o più filtri in base ai valori delle proprietà, alle chiavi e agli antenati delle entità
- Zero o più ordini di ordinamento per sequenziare i risultati
Nota:per risparmiare memoria e migliorare le prestazioni, una query deve, se possibile, specificare un limite al numero di risultati restituiti.
Nota:Il meccanismo di query basato su indice supporta un'ampia gamma di query ed è adatto alla maggior parte delle applicazioni. Tuttavia, non supporta alcuni tipi di query comuni in altre tecnologie di database: in particolare, i join e le query di aggregazione non sono supportati nel motore di query Datastore. Consulta la pagina Query Datastore per i limiti relativi alle query Datastore.
Query con JDOQL
JDO include un linguaggio di query per recuperare gli oggetti che soddisfano un insieme di criteri. Questo linguaggio, chiamato JDOQL, si riferisce direttamente a campi e classi di dati JDO e include il controllo dei tipi per parametri e risultati delle query. JDOQL è simile a SQL, ma è più appropriato per i database orientati agli oggetti come App Engine Datastore. L'implementazione dell'API JDO di App Engine non supporta direttamente le query SQL.
L'interfaccia JDO
Query
supporta diversi stili di chiamata: puoi specificare una query completa in una
stringa, utilizzando la sintassi della stringa JDOQL, oppure puoi specificare alcune o tutte le parti della
query chiamando i metodi sull'oggetto Query
. Il seguente
esempio mostra lo stile del metodo di chiamata, con un filtro e un ordine di ordinamento,
utilizzando la sostituzione dei parametri per il valore utilizzato nel filtro. I valori
degli argomenti passati al metodo
execute()
dell'oggetto Query
vengono sostituiti nella query nell'ordine specificato:
import java.util.List; import javax.jdo.Query; // ... Query q = pm.newQuery(Person.class); q.setFilter("lastName == lastNameParam"); q.setOrdering("height desc"); q.declareParameters("String lastNameParam"); try { List<Person> results = (List<Person>) q.execute("Smith"); if (!results.isEmpty()) { for (Person p : results) { // Process result p } } else { // Handle "no results" case } } finally { q.closeAll(); }
Ecco la stessa query che utilizza la sintassi della stringa:
Query q = pm.newQuery("select from Person " + "where lastName == lastNameParam " + "parameters String lastNameParam " + "order by height desc"); List<Person> results = (List<Person>) q.execute("Smith");
Puoi combinare questi stili di definizione della query. Ad esempio:
Query q = pm.newQuery(Person.class, "lastName == lastNameParam order by height desc"); q.declareParameters("String lastNameParam"); List<Person> results = (List<Person>) q.execute("Smith");
Puoi riutilizzare una singola istanza Query
con valori diversi
sostituiti ai parametri chiamando il metodo execute()
più volte. Ogni chiamata esegue la query e restituisce i risultati come
raccolta.
La sintassi della stringa JDOQL supporta la specifica letterale di valori stringa e numerici; tutti gli altri tipi di valori devono utilizzare la sostituzione dei parametri. I valori letterali all'interno
della stringa di query possono essere racchiusi tra virgolette singole ('
) o doppie
("
). Ecco un esempio che utilizza un letterale stringa:
Query q = pm.newQuery(Person.class, "lastName == 'Smith' order by height desc");
Filtri
Un filtro proprietà specifica
- Un nome di proprietà
- Un operatore di confronto
- Un valore della proprietà
Query q = pm.newQuery(Person.class); q.setFilter("height <= maxHeight");
Il valore della proprietà deve essere fornito dall'applicazione; non può fare riferimento ad altre proprietà o essere calcolato in termini di altre proprietà. Un'entità soddisfa il filtro se ha una proprietà con il nome specificato il cui valore viene confrontato con il valore specificato nel filtro nel modo descritto dall'operatore di confronto.
L'operatore di confronto può essere uno dei seguenti:
Operatore | Significato |
---|---|
== |
Uguale a |
< |
Minore di |
<= |
Minore o uguale a |
> |
Maggiore di |
>= |
Maggiore o uguale a |
!= |
Diverso da |
Come descritto nella pagina principale
Query, una singola query non può utilizzare filtri di disuguaglianza
(<
, <=
,
>
, >=
,
!=
) su più di una proprietà. Sono consentiti più filtri di disuguaglianza sulla stessa proprietà, ad esempio l'interrogazione per un intervallo di valori. I filtri contains()
, corrispondenti ai filtri IN
in SQL, sono supportati utilizzando la seguente sintassi:
// Query for all persons with lastName equal to Smith or Jones Query q = pm.newQuery(Person.class, ":p.contains(lastName)"); q.execute(Arrays.asList("Smith", "Jones"));
L'operatore
diverso da (!=
)
esegue in realtà due query: una in cui tutti gli altri filtri rimangono
invariati e il
diverso da
viene sostituito da un filtro
minore di (<
)
e l'altra in cui viene sostituito da un filtro
maggiore di (>
).
I risultati vengono poi uniti, in ordine. Una query può avere un solo filtro
not-equal
e una query che ne ha uno non può avere altri filtri di disuguaglianza.
L'operatore contains()
esegue anche più query: una per ogni elemento dell'elenco specificato, con tutti gli altri filtri invariati e il filtro contains()
sostituito da un filtro di uguaglianza (==
). I risultati vengono uniti in base all'ordine degli elementi nell'elenco. Se una query ha
più di un filtro contains()
, viene eseguita come
più query, una per ogni combinazione possibile di valori negli
elenchi contains()
.
Una singola query contenente
operatori non uguale a (!=
)
o contains()
è limitata a un massimo di 30 sottoquery.
Per ulteriori informazioni su come le query
!=
e contains()
vengono convertite in più query in un framework JDO/JPA, consulta l'articolo
Query con filtri != e IN.
Nella sintassi della stringa JDOQL, puoi separare più filtri con gli operatori
&&
(logico "and") e ||
(logico "or"):
q.setFilter("lastName == 'Smith' && height < maxHeight");
La negazione (operatore logico "not") non è supportata. Tieni presente, inoltre, che l'operatore
||
può essere utilizzato solo quando tutti i filtri che separa
hanno lo stesso nome della proprietà (ovvero quando possono essere combinati in un unico
filtro contains()
):
// Legal: all filters separated by || are on the same property Query q = pm.newQuery(Person.class, "(lastName == 'Smith' || lastName == 'Jones')" + " && firstName == 'Harold'"); // Not legal: filters separated by || are on different properties Query q = pm.newQuery(Person.class, "lastName == 'Smith' || firstName == 'Harold'");
Ordinare gli ordini
Un ordinamento della query specifica
- Un nome di proprietà
- Una direzione di ordinamento (crescente o decrescente)
Ad esempio:
Ad esempio:
// Order alphabetically by last name: Query q = pm.newQuery(Person.class); q.setOrdering("lastName asc"); // Order by height, tallest to shortest: Query q = pm.newQuery(Person.class); q.setOrdering("height desc");
Se una query include più criteri di ordinamento, questi vengono applicati nella sequenza specificata. L'esempio seguente ordina prima in base al cognome in ordine crescente e poi in base all'altezza in ordine decrescente:
Query q = pm.newQuery(Person.class); q.setOrdering("lastName asc, height desc");
Se non vengono specificati ordini di ordinamento, i risultati vengono restituiti nell'ordine in cui vengono recuperati da Datastore.
Nota:a causa del modo in cui Datastore esegue le query, se una query specifica filtri di disuguaglianza su una proprietà e ordini di ordinamento su altre proprietà, la proprietà utilizzata nei filtri di disuguaglianza deve essere ordinata prima delle altre proprietà.
Intervalli
Una query può specificare un intervallo di risultati da restituire
all'applicazione. L'intervallo indica quali risultati nel set di risultati completo devono
essere il primo e l'ultimo restituito. I risultati sono identificati dai relativi indici numerici,
con 0
che indica il primo risultato del set. Ad esempio, un intervallo
di 5,
10
restituisce i risultati dal 6° al 10°:
q.setRange(5, 10);
Nota:l'utilizzo di intervalli può influire sulle prestazioni,
poiché Datastore deve recuperare e poi scartare tutti i risultati precedenti
all'offset iniziale. Ad esempio, una query con un intervallo di 5,
10
recupera dieci risultati da Datastore, scarta i primi
cinque e restituisce i cinque rimanenti all'applicazione.
Query basate su chiavi
Le chiavi entità possono essere oggetto di un filtro di query o di un ordinamento. Datastore considera il valore della chiave completo per queste query, inclusi il percorso degli antenati dell'entità, il tipo e la stringa del nome della chiave assegnata dall'applicazione o l'ID numerico assegnato dal sistema. Poiché la chiave è univoca per tutte le entità del sistema, le query sulle chiavi semplificano il recupero di entità di un determinato tipo in batch, ad esempio per un dump batch dei contenuti di Datastore. A differenza degli intervalli JDOQL, questa operazione funziona in modo efficiente per qualsiasi numero di entità.
Quando si confrontano le chiavi per la disuguaglianza, queste vengono ordinate in base ai seguenti criteri, in ordine:
- Percorso antenato
- Tipo di entità
- Identificatore (nome della chiave o ID numerico)
Gli elementi del percorso predecessore vengono confrontati in modo simile: per tipo (stringa), poi per nome della chiave o ID numerico. I tipi e i nomi delle chiavi sono stringhe e sono ordinati in base al valore dei byte; gli ID numerici sono numeri interi e sono ordinati numericamente. Se le entità con lo stesso elemento principale e tipo utilizzano un mix di stringhe di nomi delle chiavi e ID numerici, quelle con ID numerici precedono quelle con nomi delle chiavi.
In JDO, fai riferimento alla chiave dell'entità nella query utilizzando il campo della chiave primaria
dell'oggetto. Per utilizzare una chiave come filtro di query, specifica il tipo di parametro
Key
per il metodo
declareParameters()
. La seguente query trova tutte le entità Person
con un determinato
cibo preferito, presupponendo una
relazione uno-a-uno non di proprietà
tra Person
e Food
:
Food chocolate = /*...*/; Query q = pm.newQuery(Person.class); q.setFilter("favoriteFood == favoriteFoodParam"); q.declareParameters(Key.class.getName() + " favoriteFoodParam"); List<Person> chocolateLovers = (List<Person>) q.execute(chocolate.getKey());
Una query basata solo su chiavi restituisce solo le chiavi delle entità risultato anziché le entità stesse, con latenza e costi inferiori rispetto al recupero di entità intere:
Query q = pm.newQuery("select id from " + Person.class.getName()); List<String> ids = (List<String>) q.execute();
Spesso è più economico eseguire prima una query solo con le chiavi e poi recuperare un sottoinsieme di entità dai risultati, anziché eseguire una query generale che potrebbe recuperare più entità di quelle di cui hai effettivamente bisogno.
Estensioni
Un'estensione JDO rappresenta ogni oggetto nel datastore di una
determinata classe. Puoi crearlo passando la classe desiderata al metodo
getExtent()
di Persistence Manager. L'interfaccia
Extent
estende l'interfaccia
Iterable
per accedere ai risultati e recuperarli in batch in base alle esigenze. Quando
hai finito di accedere ai risultati, chiama il metodo
closeAll()
dell'intent.
L'esempio seguente esegue l'iterazione su ogni oggetto Person
in
Datastore:
import java.util.Iterator; import javax.jdo.Extent; // ... Extent<Person> extent = pm.getExtent(Person.class, false); for (Person p : extent) { // ... } extent.closeAll();
Eliminazione di entità tramite query
Se stai eseguendo una query con l'obiettivo di eliminare tutte le entità che corrispondono al filtro della query, puoi risparmiare un po' di codice utilizzando la funzionalità "Elimina per query" di JDO. Il seguente comando elimina tutte le persone di altezza superiore a un determinato valore:
Query q = pm.newQuery(Person.class); q.setFilter("height > maxHeightParam"); q.declareParameters("int maxHeightParam"); q.deletePersistentAll(maxHeight);
Noterai che l'unica differenza è che stiamo chiamando
q.deletePersistentAll()
anziché
q.execute()
.
Tutte le regole e le limitazioni descritte sopra per filtri, ordinamenti e
indici si applicano alle query, indipendentemente dal fatto che tu stia selezionando o eliminando il set di risultati.
Tuttavia, proprio come se avessi eliminato queste entità Person
con
pm.deletePersistent()
, anche tutti gli elementi secondari dipendenti delle entità eliminate dalla query verranno eliminati. Per ulteriori informazioni sui figli a carico, consulta la pagina
Relazioni di entità in JDO.
Cursori di query
In JDO, puoi utilizzare un'estensione e la classe JDOCursorHelper
per utilizzare i cursori con le query JDO. I cursori funzionano quando vengono recuperati i risultati come elenco o
quando viene utilizzato un iteratore. Per ottenere un cursore, devi passare l'elenco dei risultati o l'iteratore
al metodo statico JDOCursorHelper.getCursor()
:
import java.util.HashMap; import java.util.List; import java.util.Map; import javax.jdo.Query; import com.google.appengine.api.datastore.Cursor; import org.datanucleus.store.appengine.query.JDOCursorHelper; Query q = pm.newQuery(Person.class); q.setRange(0, 20); List<Person> results = (List<Person>) q.execute(); // Use the first 20 results Cursor cursor = JDOCursorHelper.getCursor(results); String cursorString = cursor.toWebSafeString(); // Store the cursorString // ... // Query q = the same query that produced the cursor // String cursorString = the string from storage Cursor cursor = Cursor.fromWebSafeString(cursorString); Map<String, Object> extensionMap = new HashMap<String, Object>(); extensionMap.put(JDOCursorHelper.CURSOR_EXTENSION, cursor); q.setExtensions(extensionMap); q.setRange(0, 20); List<Person> results = (List<Person>) q.execute(); // Use the next 20 results
Per ulteriori informazioni sui cursori delle query, consulta la pagina Query Datastore.
Norme di lettura del datastore e scadenza della chiamata
Puoi impostare la criterio per la lettura (elevata coerenza o coerenza finale) e
il termine per la chiamata Datastore per tutte le chiamate effettuate da un'istanza
PersistenceManager
utilizzando la configurazione. Puoi anche
eseguire l'override di queste opzioni per un singolo oggetto Query
. Tieni presente, tuttavia, che non è possibile ignorare la configurazione di queste opzioni quando recuperi le entità per chiave.
Quando viene selezionata la coerenza finale per una query Datastore, anche gli indici che la query utilizza per raccogliere i risultati vengono accessi con coerenza finale. Le query restituiscono occasionalmente entità che non corrispondono ai criteri della query, anche se questo vale anche per una criterio per la lettura fortemente coerente. Se la query utilizza un filtro predecessore, puoi utilizzare transazioni per garantire un insieme di risultati coerente.
Per ignorare la criterio per la lettura per una singola query, chiama il metodo
addExtension()
:
Query q = pm.newQuery(Person.class); q.addExtension("datanucleus.appengine.datastoreReadConsistency", "EVENTUAL");
I valori possibili sono "EVENTUAL"
e "STRONG"
; il valore predefinito è "STRONG"
, a meno che non sia impostato diversamente nel file di configurazione jdoconfig.xml
.
Per ignorare la scadenza della chiamata Datastore per una singola query, chiama il metodo
setDatastoreReadTimeoutMillis()
:
q.setDatastoreReadTimeoutMillis(3000);
Il valore è un intervallo di tempo espresso in millisecondi.