Questo documento è incentrato sull'utilizzo del framework di persistenza Java Data Objects (JDO) per le query di 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 ordinare i risultati in base ai valori delle proprietà. I risultati includono tutte le entità che hanno almeno un valore (eventualmente nullo) per ogni proprietà denominata nei filtri e negli ordini di ordinamento e i cui valori delle proprietà soddisfano tutti i criteri di filtro specificati. La query può restituire intere entità, entità proiettate o solo le chiavi delle entità.
Una query tipica include quanto segue:
- Un tipo di entità a cui si applica la query
- Zero o più filtri basati su valori delle proprietà, chiavi e antenati delle entità
- Zero o più ordini di ordinamento per ordinare i risultati
Nota:per risparmiare memoria e migliorare le prestazioni, una query deve, se possibile, specificare un limite per il numero di risultati restituiti.
Nota: il meccanismo di query basato su indici supporta una vasta gamma di query ed è adatto per la maggior parte delle applicazioni. Tuttavia, non supporta alcuni tipi di query comuni in altre tecnologie di database: in particolare, le unioni e le query aggregate non sono supportate nel motore di query di Datastore. Consulta la pagina Query Datastore per informazioni sui limiti delle query Datastore.
Query con JDOQL
JDO include un linguaggio di query per il recupero di oggetti che soddisfano un insieme di criteri. Questo linguaggio, chiamato JDOQL, fa riferimento direttamente alle classi e ai campi di dati JDO e include il controllo del tipo per i parametri e i 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
. L'esempio seguente mostra lo stile di chiamata del metodo, con un filtro e un ordine di ordinamento, utilizzando la sostituzione del parametro per il valore utilizzato nel filtro. I valori dell'argomento 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 di 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 per i parametri chiamando il metodo execute()
più volte. Ogni chiamata esegue la query e restituisce i risultati come raccolta.
La sintassi delle stringhe JDOQL supporta la specifica letterale di valori numerici e di stringa. 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 una stringa
letterale:
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 a o essere calcolato in termini di altre proprietà. Un'entità soddisfa il filtro se possiede 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
delle query, una singola query non può utilizzare filtri di disuguaglianza
(<
, <=
,
>
, >=
,
!=
) in più di una proprietà. Sono consentiti più
filtri di disuguaglianza nella stessa proprietà, ad esempio query su 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 di diseguaglianza (!=
)
esegue in realtà due query: una in cui tutti gli altri filtri rimangono invariati e il
filtro di diseguaglianza viene sostituito con un
filtro minore di (<
)
e un'altra 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
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 nell'elenco specificato, con tutti gli altri filtri invariati e il filtro contains()
sostituito con un
filtro di uguaglianza (==
).
I risultati vengono uniti in base all'ordine degli elementi nell'elenco. Se una query contiene 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 di non uguale (!=
)
o contains()
è limitata a un massimo di 30 sottoquery.
Per ulteriori informazioni su come le query !=
e contains()
vengono tradotte in più query in un framework JDO/JPA, consulta l'articolo
Query con filtri != e IN.
Nella sintassi di stringa JDOQL, puoi separare più filtri con gli operatori &&
("e" logico) e ||
("o" logico):
q.setFilter("lastName == 'Smith' && height < maxHeight");
La negazione ("not" logico) non è supportata. Tieni inoltre presente che l'operatore ||
può essere utilizzato solo quando i filtri che separa hanno tutti lo stesso nome di 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'");
Ordinamenti
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ù ordini 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 per una proprietà e ordini di ordinamento per 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 restituiti. I risultati sono identificati dai relativi indici numerici,
con 0
che indica il primo risultato nell'insieme. Ad esempio, un intervallo
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 poi eliminare tutti i risultati precedenti all'offset iniziale. Ad esempio, una query con un intervallo di 5,
10
recupera dieci risultati da Datastore, ne ignora i primi cinque e restituisce gli altri cinque all'applicazione.
Query basate su chiavi
Le chiavi delle entità possono essere oggetto di un filtro o di un ordine di ordinamento della query. Datastore considera il valore della chiave completo per queste query, incluso il percorso dell'antenato 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 consentono di recuperare facilmente le 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 esegue il confronto per verificare l'ineguaglianza, le chiavi vengono ordinate in base ai seguenti criteri:
- Percorso dell'antenato
- Tipo di entità
- Identificatore (nome della chiave o ID numerico)
Gli elementi del percorso dell'antenato 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 in byte; gli ID numerici sono numeri interi e sono ordinati in ordine numerico. Se le entità con lo stesso elemento principale e lo stesso tipo utilizzano una combinazione di stringhe di nomi di chiavi e ID numerici, quelle con ID numerici precedono quelle con nomi di 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
al metodo
declareParameters()
. Il seguente comando trova tutte le entità Person
con un determinato
cibo preferito, supponendo un
rapporto uno a uno senza proprietario
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 chiavi restituisce solo le chiavi delle entità di risultato anziché le entità stesse, con una latenza e un costo 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 per 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 bisogno.
Estensioni
Un'estensione JDO rappresenta ogni oggetto nel Datastore di una determinata classe. Lo crei passando la classe che ti interessa al metodo
getExtent()
del gestore della persistenza. L'interfaccia Extent
estende l'interfaccia
Iterable
per accedere ai risultati e recuperarli in batch, se necessario. Quando hai finito di accedere ai risultati, chiami il metodo closeAll()
dell'ambito.
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 eseguendo una query allo scopo di eliminare tutte le entità che coincidono con il 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 una determinata:
Query q = pm.newQuery(Person.class); q.setFilter("height > maxHeightParam"); q.declareParameters("int maxHeightParam"); q.deletePersistentAll(maxHeight);
Noterai che l'unica differenza è che chiamiamo
q.deletePersistentAll()
instead of
q.execute()
.
Tutte le regole e le limitazioni descritte sopra per filtri, ordini di ordinamento e indici si applicano alle query indipendentemente dal fatto che tu selezioni o elimini il set di risultati.
Tieni presente, però, che, come se avessi eliminato queste Person
entità con
pm.deletePersistent()
,
verranno eliminati anche tutti gli elementi figli dipendenti delle entità eliminate dalla query. 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
utilizzando un iteratore. Per ottenere un cursore, passa 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 di query, consulta la pagina Query Datastore.
Norme di lettura del datastore e scadenza chiamata
Puoi impostare il criterio per la lettura (elevata coerenza o coerenza finale) e la scadenza della chiamata di 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 eseguire l'override della configurazione per queste opzioni quando recuperi le entità per chiave.
Quando viene selezionata la coerenza finale per una query Datastore, anche agli indici utilizzati dalla query per raccogliere i risultati viene eseguito l'accesso con coerenza finale. A volte le query restituiscono entità che non corrispondono ai criteri di query, anche se questo accade anche con un criterio per la lettura fortemente coerente. Se la query utilizza un filtro antenato, puoi utilizzare transazioni per garantire un insieme di risultati coerente.
Per ignorare il criterio per la lettura per una singola query, chiama il relativo 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 sostituire la scadenza della chiamata a Datastore per una singola query, chiama il relativo metodo
setDatastoreReadTimeoutMillis()
:
q.setDatastoreReadTimeoutMillis(3000);
Il valore è un intervallo di tempo espresso in millisecondi.