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 del datastore.
Una query recupera le entità da Datastore che soddisfano un insieme specificato di condizioni. La query opera su entità di un determinato kind; può specificare filtri su valori, chiavi e predecessori delle proprietà delle entità e può restituire zero o più entità come risultati. Una query può anche specificare ordini di ordinamento per sequenza i risultati in base ai valori delle rispettive proprietà. I risultati includono tutte le entità che hanno almeno un valore (possibilmente nullo) per ogni proprietà denominata nei filtri e negli ordini di ordinamento e i cui valori delle proprietà soddisfano tutti i criteri del filtro specificati. La query può restituire intere entità, entità previste 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 a valori, chiavi e predecessori delle proprietà delle entità
- Zero o più ordini di ordinamento per mettere in sequenza i risultati
Nota: per risparmiare memoria e migliorare le prestazioni, per una query deve, quando possibile, specificare un limite per il 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 aggregate non sono supportati all'interno di Datastore Query Engine. Consulta la pagina Query Datastore per conoscere le limitazioni relative alle query di Datastore.
Query con JDOQL
JDO include un linguaggio di query per il recupero degli oggetti che soddisfano un insieme di criteri. Questo linguaggio, denominato JDOQL, fa riferimento direttamente alle classi e ai campi di dati JDO e include il controllo dei tipi di parametri e risultati delle query. JDOQL è simile a SQL, ma è più appropriato per database orientati agli oggetti come App Engine Datastore. (l'implementazione dell'API JDO da parte 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 delle stringhe JDOQL, oppure specificare alcune o tutte le parti della query richiamando i metodi nell'oggetto Query
. L'esempio seguente mostra lo stile del metodo delle chiamate, con un filtro e un 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 per definire la 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 per i parametri richiamando il metodo execute()
più volte. Ogni chiamata esegue la query e restituisce i risultati sotto forma di raccolta.
La sintassi della stringa JDOQL supporta la specifica letterale dei 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 utilizzando il valore letterale
stringa:
Query q = pm.newQuery(Person.class, "lastName == 'Smith' order by height desc");
Filtri
Un filtro proprietà specifica
- Il nome di una proprietà
- Un operatore di confronto
- Un valore 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à né può essere calcolato. Un'entità soddisfa il filtro se ha una proprietà del nome specificato il cui valore viene confrontato con quello specificato nel filtro nel modo descritto dall'operatore di confronto.
L'operatore di confronto può essere:
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'esecuzione di query 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
non uguale (!=
)
in realtà esegue due query: una in cui tutti gli altri filtri sono
invariati e il
filtro non uguale
viene sostituito con un filtro
minore di (<
)
e l'altro in cui viene sostituito con un
filtro maggiore di (>
)
. I risultati vengono poi uniti, in ordine. Una query non può avere più di un filtro
non uguale
mentre una query che ne ha uno non può avere altri filtri di disuguaglianza.
L'operatore contains()
esegue inoltre più
query: una per ogni elemento nell'elenco specificato, con tutti gli altri filtri
invariati e il filtro contains()
sostituito con un
filtro di uguaglianza (==
)
. I risultati vengono uniti in ordine di elementi nell'elenco. Se una query ha più di un filtro contains()
, viene eseguita come più query, una per ogni possibile combinazione di valori negli elenchi contains()
.
Una singola query contenente operatori
non uguali (!=
)
o contains()
è limitata a un massimo di 30 sottoquery.
Per saperne di più su come le query !=
e contains()
si traducono 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 &&
( "and logico") e ||
("or" logico):
q.setFilter("lastName == 'Smith' && height < maxHeight");
La negazione ("non logico") non è supportata. Tieni presente, inoltre, che l'operatore ||
può essere utilizzato solo quando i filtri che separa tutti hanno lo stesso nome proprietà (ossia 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'");
Ordina ordini
Un ordinamento delle query specifica
- Il nome di una proprietà
- Una direzione di ordinamento (ordine 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ù ordini, questi vengono applicati nella sequenza specificata. L'esempio seguente ordina prima in base al cognome crescente e poi in base all'altezza 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 sono stati 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 ordina ordini su altre proprietà, la proprietà utilizzata nei filtri di disuguaglianza deve essere ordinata prima delle altre.
Intervalli
Una query può specificare un intervallo di risultati da restituire all'applicazione. L'intervallo indica quali risultati devono essere i primi e gli ultimi restituiti nell'insieme di risultati completo. I risultati sono identificati dai rispettivi indici numerici, dove 0
indica il primo risultato dell'insieme. Ad esempio, un intervallo
di 5,
10
restituisce i risultati dal sesto al decimo:
q.setRange(5, 10);
Nota: l'utilizzo degli intervalli può influire sulle prestazioni, poiché Datastore deve recuperare e quindi ignorare tutti i risultati che precedono l'offset iniziale. Ad esempio, una query con un intervallo di 5,
10
recupera dieci risultati da Datastore, ignora i primi
cinque e restituisce i cinque rimanenti all'applicazione.
Query basate su chiave
Le chiavi di entità possono essere oggetto di un filtro di query o di un ordinamento. Datastore prende in considerazione il valore chiave completo per queste query, inclusi il percorso predecessore 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 in tutte le entità del sistema, le query chiave semplificano il recupero delle entità di un determinato tipo in batch, ad esempio per un dump batch dei contenuti di Datastore. A differenza degli intervalli JDOQL, funziona in modo efficiente per un numero qualsiasi di entità.
Quando si confronta la disuguaglianza, le chiavi vengono ordinate in base ai seguenti criteri:
- Percorso predecessore
- Tipo di entità
- Identificatore (nome chiave o ID numerico)
Gli elementi del percorso predecessore vengono confrontati in modo simile: per tipo (stringa), poi per nome chiave o ID numerico. I tipi e i nomi delle chiavi sono stringhe e sono ordinati per valore byte; gli ID numerici sono numeri interi in ordine numerico. Se le entità con lo stesso tipo e l'elemento principale utilizzano una combinazione di stringhe di nomi chiave e ID numerici, quelle con ID numerici le antepongono ai nomi delle chiavi.
In JDO, fai riferimento alla chiave di entità nella query utilizzando il campo di chiave primaria dell'oggetto. Per utilizzare una chiave come filtro di query, devi specificare il tipo di parametro Key
nel metodo
declareParameters()
. Quanto segue trova tutte le entità Person
con un determinato
alimento preferito, supponendo una
relazione one-to-one senza 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 solo con chiavi restituisce solo le chiavi delle entità risultati anziché le entità stesse, con una latenza e un costo inferiori rispetto al recupero di intere entità:
Query q = pm.newQuery("select id from " + Person.class.getName()); List<String> ids = (List<String>) q.execute();
Spesso è più economico eseguire prima una query basata solo su 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'extent JDO rappresenta ogni oggetto nel datastore di una determinata classe. Puoi crearla passando la classe desiderata al metodo
getExtent()
di Persistence Manager. L'interfaccia Extent
estende l'interfaccia
Iterable
per accedere ai risultati, recuperandoli in batch secondo necessità. Quando hai terminato di accedere ai risultati, chiami il metodo closeAll()
dell'estensione.
L'esempio seguente esegue l'iterazione su ogni oggetto Person
nel 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à per query
Se stai inviando 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. Con la seguente azione vengono eliminate tutte le persone che superano una determinata altezza:
Query q = pm.newQuery(Person.class); q.setFilter("height > maxHeightParam"); q.declareParameters("int maxHeightParam"); q.deletePersistentAll(maxHeight);
Noterai che l'unica differenza qui è che stiamo chiamando
q.deletePersistentAll()
anziché
q.execute()
.
Tutte le regole e le restrizioni descritte in precedenza per filtri, ordini e indici si applicano alle query indipendentemente dal fatto che tu stia selezionando o eliminando il set di risultati.
Tuttavia, tieni presente che, proprio come se avessi eliminato queste entità Person
con
pm.deletePersistent()
, verranno eliminati anche tutti i figli dipendenti delle entità eliminate dalla query. Per maggiori informazioni sui figli dipendenti, consulta la pagina
Relazioni delle entità in JDO.
Cursori di query
In JDO, puoi usare un'estensione e la classe JDOCursorHelper
per usare i cursori con le query JDO. I cursori funzionano quando recuperano i risultati come
elenco o utilizzano un iteratore. Per ottenere un cursore, passi l'elenco di 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 di Datastore e scadenza chiamate
Puoi impostare il criterio per la lettura (elevata coerenza o coerenza finale) e la scadenza della 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
. Tuttavia, tieni presente che non è possibile eseguire l'override della configurazione per queste opzioni quando recuperi le entità per chiave.
Quando viene selezionata la coerenza finale per una query Datastore, è possibile accedere anche agli indici utilizzati dalla query per raccogliere i risultati con coerenza finale. Le query a volte restituiscono entità che non corrispondono ai criteri di query, anche se questo è vero anche con un criterio per la lettura a elevata coerenza. Se la query utilizza un filtro dei predecessori, puoi utilizzare le transazioni per garantire un set di risultati coerente.
Per eseguire l'override del criterio per la lettura per una singola query, chiama il metodo
addExtension()
corrispondente:
Query q = pm.newQuery(Person.class); q.addExtension("datanucleus.appengine.datastoreReadConsistency", "EVENTUAL");
I valori possibili sono "EVENTUAL"
e "STRONG"
; il valore predefinito è "STRONG"
, se non diversamente impostato nel file di configurazione jdoconfig.xml
.
Per eseguire l'override della scadenza della chiamata Datastore per una singola query, chiama il metodo
setDatastoreReadTimeoutMillis()
corrispondente:
q.setDatastoreReadTimeoutMillis(3000);
Il valore è un intervallo di tempo espresso in millisecondi.