Premessa
Benvenuti alla prima parte di questa serie in quattro
parti su JDOQL. Gli articoli di questa serie illustreranno
le nuove caratteristiche che lo standard JDO 2.0 di
prossima adozione apporterà a JDOQL.
La serie si compone di:
- JDOQL
Parte I - Supporto per nuovi operatori e metodi, paging
dei risultati delle query, e "deletion by query"
delegato dal datastore.
- JDOQL
Parte II - Proiezione, aggregazione, raggruppamento
deduplicazione (distinzione) dei risultati della
query.
- JDOQL
Parte III - Una nuova rappresentazione a stringa singola
di JDOQL, con la definizione di parametri e variabili
implicite per query più concise
- JDOQL
Parte IV - Uso di SQL come linguaggio di query: potenza
a discapito di uno stretto accoppiamento tra codice
e schema del database.
Questo
particolare articolo utilizza il domain object model
discusso separatamente e i dati stabiliti per Hammer.org
- The Online Auction Domain (è possibile visionare
il diagramma UML per il dominio persistente).
Altre
utili risorse includono le due pagine del JDOQL 2.0
Quick Reference di SolarMetric e l'edizione in PDF non
stampabile del mio libro, Java Data Objects (Addison-Wesley).
Sono stati fatti diversi miglioramenti all'insieme degli
operatori supportati dai filtri JDOQL, come pure al
supporto per l'invocazione di nuovi metodi sui tipi
String, Collection e Map.
Introduzione
a JDOQL
Ad alcuni lettori può interessare avere una panoramica
su JDOQL prima di guardare alle novità di JDO
2.0. Parecchi libri sullo standard JDO trattano bene
anche JDOQL. L'edizione in PDF non stampabile del mio
libro, Java Data Objects (Addison-Wesley) è scaricabile
gratuitamente. Andate direttamente al Capitolo 8 dove
è trattato JDOQL.
JDOQL fornisce un potente meccanismo di query espresso
in termini di modello di dominio persistente ed è
perciò indipendente dalla struttura del database
sottostante. Il linguaggio è stato appositamente
progettato per essere tradotto efficientemente in SQL
tramite un'implementazione JDO.
In sostanza (e questa è realmente una semplificazione)
ogni query JDOQL ha un insieme di Candidates e una clausola
Filter potrebbe prevedere altre clausole che governano
l'ordinamento dei risultati e la definizione dei parametri.
I Candidates sono solitamente l'insieme di tutte le
istanze persistenti di una classe stabilita (sottoclassi
comprese).
Il Filter è un'espressione booleana scritta secondo
la grammatica di JDOQL, una notazione molto simile a
quella di Java. Se l'espressione booleana del filtro
per quell'istanza dà come risultato true, ogni
singola istanza candidata è inclusa nella Collection
restituita tramite l'esecuzione della query.
Le query sono generate tramite i metodi newQuery() sull'interfaccia
PersistenceManager. La query è di fatto un'istanza
dell'interfaccia Query, che fornisce i metodi per l'impostazione
delle varie clausole della query e per l'esecuzione
della query con i valori dei parametri specificati.
JDOQL
(#1)
Ecco
una semplice query che ricerca tutte le istanze di AuctionItem
la cui Location corrisponde all'isoCode "UK":
//
definizione della query
Query q = pm.newQuery (AuctionItem.class,
"location.isoCode == 'UK'"); // estensione
di candidato e filtro
// esecuzione della query
Collection results = (Collection) q.execute ();
/*
iterazione della collection 'results'; i relativi elementi
sono istanze reali di AuctionItem*/
Iterator iter = results.iterator();
while (iter.hasNext()) {
AuctionItem item = (AuctionItem) iter.next();
// opportune ulteriori operazioni
}
Iterando
i risultati della collection, le istanze contenute all'interno
della stessa sono quelle istanze di AuctionItem che
verificano la condizione di filtro "location.isoCode
== 'UK'", vale a dire quelle il cui campo 'location'
possiede un riferimento a un'istanza di Location che
ha il suo campo 'isoCode' impostato a 'UK'. Da Notare
che JDOQL 2.0 permette la definizione di stringhe literal
delimitati per comodità da apici singoli siano
delimitate per convenienza dalle singole virgolette.
La query è tradotta nel linguaggio nativo del
db sottostante, che è solitamente, ma non necessariamente,
SQL.
JDOQL
(#2)
Come
con i prepared statement in SQL, è possibile
scrivere query JDOQL che prendano parametri. In questo
secondo esempio, la query presentata è del tutto
equivalente alla precedente salvo l'utilizzo di un parametro
denominato code, in sostituzione della stringa literal
'UK'. Ciò aumenta la flessibilità della
query.
//definizione
della query
Query q = pm.newQuery (AuctionItem.class,
"location.isoCode == code"); // estensione
di candidato e filtro
q.declareParameters("String code"); // dichiarazione
del parametro formale
//esecuzione
della query con valori appropriati dei parametri
Collection results = (Collection) q.execute ("UK");
Per
ora può bastare. Pur essendo convinto che le
vostre conoscenze siano sufficienti per capire le nuove
caratteristiche che sono descritte ed illustrate nel
seguito dell'articolo, vi rimando, per maggiori dettagli
e approfondimenti sul tema JDOQL, al mio libro.
Ricercare
gli utenti - Confronto case insensitive (non viene fatta
distinzione tra maiuscola e minuscola)
Quando gli utenti si recano nel sito di Hammer.org per
accedere all'asta, il sistema individua, all'inserimento
del nome da parte dell'utente, l'oggetto RegisteredUser
corrispondente. La query riportata sotto mostra il test
di uguaglianza case insensitive fra il campo userName
e il parametro ricevuto "name".
Query
q = pm.newQuery (RegisteredUser.class, "userName.toLowerCase()
== name");
q.declareParameters("String name");
Collection results = (Collection)q.execute("jaques");
Risultati
La collection restituita contiene l'unico oggetto RegisteredUser
con userName "Jaques" ('J ' maiuscola).
SQL. Espressa attraverso JDOQL la query risulta interamente
portabile. Per coloro che fossero interessati, l'SQL
che questa query ha generato verso il mio schema relazionale
era:
SELECT
t0.JDOID, t0.JDOCLASS, t0.JDOVERSION, t0.BIDDER_JDOID,
t0.EMAILADDRESS, t0.SELLER_JDOID, t0.USERNAME
FROM REGISTEREDUSER t0
WHERE (LOWER(t0.USERNAME) = 'jaques')
Ricerca di articoli - Corrispondenza
con espressioni regolari
Un'ottima nuova funzionalità per la ricerca consiste
nell'aggiunta del supporto di espressioni regolari per
i tipi String tramite il metodo matches(). Il parametro
da passare a matches() è un sottoinsieme della
grammatica per le espressioni regolari, limitato a case
insensitivity e a operatori wildcard. Questo sottoinsieme
è tradotto facilmente in notazione SQL wildcard.
Ecco qui una query che cerca gli articoli dell'asta
che contengono la parola "Connect" all'interno
del titolo.
JDOQL
(#5)
Query
q = pm.newQuery (AuctionItem.class, "description.matches('.*Connect.*')");
Collection results = (Collection)q.execute ();
Risultati
La collection risultante contiene le istanze di AuctionItem
che soddisfano la condizione del filtro. Con i miei
dati la collection conteneva soltanto un articolo dell'asta,
quello con la descrizione "Java Database Connectivity".
SQL
Espressa attraverso JDOQL la query risulta interamente
portabile. Per coloro che fossero interessati, l'SQL
che questa query ha generato sulla base del mio schema
relazionale era:
SELECT
t0.JDOID, t0.JDOCLASS, t0.JDOVERSION, t0.CATEGORY_JDOID,
t0.DESCRIPTION,
t0.LOCATION_JDOID, t0.OPENINGPRICE, t0.RESERVEPRICE,
t0.SELLER_JDOID, t0.TITLE,
t0.WINNINGBID_JDOID, t0.WINNINGPROXYBID_JDOID
FROM AUCTIONITEM t0
WHERE (t0.DESCRIPTION LIKE '%Connect%')
Sfruttare
il Regex case insensitive
Riscriviamo la query appena vista, in modo da eseguire
la ricerca case insensitive (ovvero senza distinzione
tra maiuscole e minuscole) della parola "connect"
JDOQL
(#6)
Query
q = pm.newQuery (AuctionItem.class, "description.matches('(?i).*Connect.*')");
Collection results = (Collection) q.execute ();
Risultati
Ora che la query è case insensitive, i risultati
contengono due articoli dell'asta, con le descrizioni
"Java Database Connectivity" e "Disconnected
Data Manipulation Strategies".
SQL
Ecco l'SQL case insensitive:
SELECT
t0.JDOID, t0.JDOCLASS, t0.JDOVERSION, t0.CATEGORY_JDOID,
t0.DESCRIPTION,
t0.LOCATION_JDOID, t0.OPENINGPRICE, t0.RESERVEPRICE,
t0.SELLER_JDOID,
t0.TITLE, t0.WINNINGBID_JDOID, t0.WINNINGPROXYBID_JDOID
FROM AUCTIONITEM t0
WHERE (LOWER(t0.DESCRIPTION) LIKE '%connect%')
Articoli
vinti con l'offerta per procura - Tipo di operatore
a runtime (instanceof)
In JDO 1.0 una query poteva usare l'operatore di cast
(< typename >) per la determinazione a runtime
del tipo, anche se non sempre ciò è facilmente
leggibile.
Ecco un esempio che usa l'operatore instanceof appena
introdotto per selezionare soltanto quegli articoli
dell'asta che hanno come offerta vincente l'offerta
per procura.
JDOQL
(#7)
Query
q = pm.newQuery (AuctionItem.class, "winningBid
instanceof ProxyBid");
Collection results = (Collection) q.execute ();
Risultati
La collection dei risultati contiene le istanze di AuctionItem
che soddisfano la condizione del filtro. Con i miei
dati ho ottenuto soltanto il singolo articolo dell'asta,
quello per "Enterprise JavaBeans", per cui
l'offerta vincente è effettivamente l'offerta
per procura piazzata da David.
SQL
Espressa attraverso JDOQL la query è interamente
portabile. Per coloro che fossero interessati, l'SQL
che questa query ha generato verso il mio schema relazionale
era:
SELECT
t0.JDOID, t0.JDOCLASS, t0.JDOVERSION, t0.CATEGORY_JDOID,
t0.DESCRIPTION,
t0.LOCATION_JDOID, t0.OPENINGPRICE, t0.RESERVEPRICE,
t0.SELLER_JDOID, t0.TITLE,
t0.WINNINGBID_JDOID, t0.WINNINGPROXYBID_JDOID
FROM AUCTIONITEM t0 INNER JOIN BID t1 ON t0.WINNINGBID_JDOID
= t1.JDOID
WHERE (t1.JDOCLASS = 'org.hammer.domain.ProxyBid')
Articoli
senza offerta - Contenimento di collection
Ci sono ancora degli articoli dell'asta per i quali
non sono state fatte delle offerte? Creare questo tipo
di query è diventato facile grazie al nuovo metodo
isEmpty() per i tipi Collection.
JDOQL
(#8)
Query q = pm.newQuery (AuctionItem.class, "bids.isEmpty()");
Collection results = (Collection) q.execute ();
Risultati
La collection dei risultati contiene le istanze di AuctionItem
per cui la collection delle offerte è vuota.
Con i miei dati ne ho ottenute due: "Disconnected
Data Manipulation Strategies" e "Speaking
Javanese".
SQL
Espressa attraverso JDOQL la query è interamente
portabile. Per coloro che fossero interessati, l'SQL
che questa query ha generato verso il mio schema relazionale
era:
SELECT
t0.JDOID, t0.JDOCLASS, t0.JDOVERSION, t0.CATEGORY_JDOID,
t0.DESCRIPTION,
t0.LOCATION_JDOID, t0.OPENINGPRICE, t0.RESERVEPRICE,
t0.SELLER_JDOID, t0.TITLE,
t0.WINNINGBID_JDOID, t0.WINNINGPROXYBID_JDOID
FROM AUCTIONITEM t0
WHERE (0 = (SELECT COUNT(*)
FROM AUCTI_BIDS
WHERE AUCTI_BIDS.JDOID = t0.JDOID))
Ricerca
su proprietà definite dall'utente - Contenimento
di map
Qualche volta è utile guardare all'interno dei
costrutti Map entro il filtro di una query. Per raggiungere
questi risultati, JDOQL adesso supporta i metodi containsValue()
e containsKey().
Ecco una query che seleziona gli articoli dell'asta
la cui map itemProperty contiene la chiave"Pages"
ed anche, indipendentemente, contiene il valore "Addison
Wesley".
JDOQL
(#9)
String
filter = "itemProperties.containsKey('Pages') &&
itemProperties.containsValue('Addison Wesley')";
Query q = pm.newQuery (AuctionItem.class, filter);
Collection results = (Collection)q.execute ();
Risultati
La collection dei risultati contiene le istanze di AuctionItem
che soddisfano la condizione del filtro. Ne ho ottenuto
solo uno: "Java Data Objects".
SQL
Espresso attraverso JDOQL la query è interamente
portabile. Per coloro che fossero interessati, l'SQL
che questa query ha generato verso il mio particolare
schema relazionale era:
SELECT
DISTINCT t0.JDOID, t0.JDOCLASS, t0.JDOVERSION, t0.CATEGORY_JDOID,
t0.DESCRIPTION,
t0.LOCATION_JDOID, t0.OPENINGPRICE, t0.RESERVEPRICE,
t0.SELLER_JDOID, t0.TITLE,
t0.WINNINGBID_JDOID, t0.WINNINGPROXYBID_JDOID
FROM AUCTIONITEM t0 INNER JOIN AUCTI_ITEMPROPERTIES
t1 ON t0.JDOID = t1.JDOID INNER
JOIN AUCTI_ITEMPROPERTIES t2 ON t0.JDOID = t2.JDOID
WHERE (t1.KEY0 = 'Pages' AND t2.VALUE0 = 'Addison Wesley')
Eliminare
il Collection Wrapper - Unicità del risultato
In JDO 1.0 l'esecuzione di una query di solito restituisce
sempre una collection di istanze che soddisfano il filtro.
Se nessuna istanza verifica la condizione del filtro
allora la collection dovrebbe risultare vuota (l'iteratore
ricavato dalla collection restituirebbe false alla prima
invocazione del metodo hasNext()). Se un'istanza soddisfacesse
esattamente la condizione del filtro, allora la collection
conterrebbe esattamente una sola istanza, ma per recuperarla
bisognerebbe iterare ancora la collection. Spesso, naturalmente,
la collection contiene più istanze che soddisfano
la condizione.
Quando le query sono state scritte specificatamente
in modo tale da restituire al massimo un'istanza, l'iterazione
sulla collection risultante è più pesante.
Per ovviare a questo problema è stata aggiunta
una proprietà "unique" all'interfaccia
Query. Impostando la proprietà a true ed eseguendo
la query il risultato non è più una collection
ma un singolo riferimento dell'oggetto (il quale è
null se nessuna istanza soddisfa la condizione del filtro).
JDOQL
(#10) senza unicità
Di
seguito riportiamo una query di esempio che restituisce
la singola istanza di Seller che ha un dato numero di
conto. Il numero di conto è passato come parametro.
Questo esempio non usa la proprietà "unique",
in tal modo si deve iterare la collection.
Query
q = pm.newQuery (Seller.class, "accountNumber ==
'0072'");
Collection result = (Collection) q.execute ();
Iterator iter = result.iterator();
if (iter.hasNext()) {
Seller slr = (Seller) iter.next();
}
else {
// non esistono venditori con numero di conto 0072
}
JDOQL
(#11) con unicità
Il
prossimo esempio mostra quanto è più semplice
questo tipo di query se la proprietà "unique"
è uguale a true. Una volta ottenuto il risultato,
le operazioni da intraprendere sono semplicemente il
cast a tipo Seller e il test per verificare se l'oggetto
è null.
Query
q = pm.newQuery (Seller.class, "accountNumber ==
'0072'");
q.setUnique (true);
Seller slr = (Seller) q.execute ();
if (slr == null) {
// non esistono venditori con numero di conto 0072
}
Unicità
e proiezione
L'esecuzione della query restituisce solitamente una
collection di risultati. Ciascuno dei risultati nella
collection è un'istanza dello stesso tipo. Così
una query con classe candidata Employee una volta eseguita,
restituisce una collection di istanze di Employee (ed
eventualmente di sottoclassi). Con l'unicità
abilitata il risultato non è più una collection,
ma solo un singolo riferimento del tipo Employee (o
una sottoclasse), che potrebbe essere null.
Osservando Projection si vedrà che le query possono
adesso restituire i dati grezzi del campo come collection
di Object[ ] (anziché che come collection di
istanze persistenti). Con l'unicità abilitata
viene restituito solo un singolo riferimento di tipo
Object[ ] (che potrebbe essere null).
In JDO 2.0 è inoltre possibile passare il descrittore
di una classe JavaBean alla Query, condizionando il
risultato ad essere una Collection di istanze di questo
JavaBean. Ogni campo proiettato è mappato in
una proprietà della classe del bean. Con l'unicità
abilitata su tali query, il risultato dell'esecuzione
è un singolo riferimento del tipo della classe
bean (che potrebbe essere null).
Query
non uniche
In conclusione si noti che un tentativo di eseguire,
con l'unicità abilitata, una query che restituisce
più di un'istanza che soddisfa il filtro, è
illegale. In tal caso viene sollevata un'eccezione JDOUserException
con un messaggio informativo. Ciò accadrebbe
nelle query riportate sopra se più di un venditore
avesse come numero di conto 0072. Naturalmente ci si
attende uno schema di database con vincoli che impediscano
questa eventualità.
Pagine
e pagine di articoli - Paginazione del risultato della
query
È una necessità diffusa dover elaborare
i risultati generati dall'esecuzione di una query una
pagina alla volta, ma finora non è mai stato
fatto in modo portabile. Forse lo scenario più
tipico è quello di un sito web che visualizza
le pagine di dati.
JDO 2.0 fornisce una caratteristica di range sull'interfaccia
Query. Il range è specificato da due tipi primitivi
long, start e end. Quando viene eseguita la query, i
primi record "start" sono scartati e al massimo
i successivi "end - start" sono restituiti.
Da specifiche non è richiesto al manager della
persistenza di rimanere aperto fra le richieste di pagina
successive. Ciò facilita le applicazioni scalabili
in cui il manager della persistenza viene chiuso dopo
che ogni pagina è stata recuperata, permettendo
di riallocare le risorse.
Ecco un esempio che recupera le terza pagina di AuctionItems
con 5 record per pagina.
JDOQL
(#12)
Query
q = pm.newQuery (AuctionItem.class); // no filter, so
all instances match
q.setRange (10, 15); // start from the 10th up to but
excluding the 15th
q.setOrdering ("title ascending");
Collection results = (Collection) q.execute ();
Risultati
La collection restituita conterrà al massimo
5 istanze di AuctionItem.
SQL
Espressa attraverso JDOQL la query è interamente
portabile. Per coloro che fossero interessati, l'SQL
che questa query ha generato verso il mio particolare
schema relazionale era:
SELECT
LIMIT 10 5 t0.JDOID, t0.JDOCLASS, t0.JDOVERSION, t0.CATEGORY_JDOID,
t0.DESCRIPTION,
t0.LOCATION_JDOID, t0.OPENINGPRICE, t0.RESERVEPRICE,
t0.SELLER_JDOID, t0.TITLE,
t0.WINNINGBID_JDOID, t0.WINNINGPROXYBID_JDOID
FROM AUCTIONITEM t0
ORDER BY t0.TITLE ASC
Ordinamento delle query
Quando si usa la funzionalità di paginazione
per le query, è consigliabile utilizzare una
clausola d'ordinamento esplicita nella query a meno
che il vostro datastore non mantenga i dati ordinati.
I dati nella maggior parte delle basi di dati relazionali
non sono garantiti in alcun ordine particolare senza
una clausola d'ordinamento esplicita.
Isolamento
della transazione
È interessante notare che la paginazione di query
non fornisce di per sé stessa le garanzie d'isolamento
della transazione. Se richiedete pagine successive all'interno
della singola transazione allora i risultati saranno
consistenti con quella transazione. Tuttavia se richiedete
le pagine successive in transazioni differenti, come
dovrebbe essere tipico in queste situazioni, allora
i risultati che vedete possono essere influenzati da
inserimenti e cancellazioni che stanno per essere sottoposti
a commit nella base di dati da altri client mentre fate
le vostre richieste di pagina. Questo è il normale
comportamento previsto.
Rimuovere
efficientemente articoli non desiderati - Query di cancellazione
In
JDO la cancellazione di una singola istanza persistente
è realizzata passandola al Manager di persistenza
come argomento del metodo deletePersistent(). Se avete
una collection/array di oggetti e desiderate cancellarli,
potete passare la collection al metodo deletePersistentAll().
Poiché ciascuno di questi approcci richiede al
client JDO di forzare la cancellazione, quest'ultimo
deve avere conoscenza di ogni istanza persistente da
cancellare.
Come possiamo delegare questa intera operazione al server?
In JDO 1.0 non era possibile farlo in modo portabile.
Tuttavia, JDO 2.0 aggiunge, per facilitare questo, la
funzionalità specifica all'interfaccia Query.
Cancellare gli articoli dell'asta selezionati.
Qualsiasi query che, una volta eseguita, restituirebbe
una o più istanze persistenti al client può
invece essere eseguita "per cancellazione".
In questo caso gli statement in questione vengono sottoposti
non per restituire i dati per le istanze ma invece per
cancellare le istanze che soddisfano la condizione.
Ecco un frammento di codice che usa una query che seleziona
tutti gli articoli dell'asta per cui la collection di
offerte è vuota e cancella ogni articolo restituito:
JDOQL
(#13) cancellazione individuale forzata dal client -
kodo workbench screenshot
Query q = pm.newQuery (AuctionItem.class, "bids.isEmpty()");
Collection results = (Collection) q.execute();
Iterator iter = results.iterator();
while (iter.hasNext) {
pm.deletePersistent(iter.next());
}
SQL
Questa sequenza di istruzioni riportata di seguito mostra
come tali azioni potrebbero essere mappate in SQL:
SELECT
t0.JDOID, t0.JDOCLASS, t0.JDOVERSION, t0.CATEGORY_JDOID,
t0.DESCRIPTION,
t0.LOCATION_JDOID, t0.OPENINGPRICE, t0.RESERVEPRICE,
t0.SELLER_JDOID, t0.TITLE,
t0.WINNINGBID_JDOID, t0.WINNINGPROXYBID_JDOID
FROM AUCTIONITEM t0
WHERE (0 = (SELECT COUNT(*)
FROM AUCTI_BIDS
WHERE AUCTI_BIDS.JDOID = t0.JDOID))
DELETE
FROM AUCTIONITEM t0 WHERE t0.JDOID = 1
DELETE
FROM AUCTIONITEM t0 WHERE t0.JDOID = 2
DELETE
FROM AUCTIONITEM t0 WHERE t0.JDOID = 3 etc.
Ecco
una query equivalente per eseguire una cancellazione.
La cancellazione è delegata al server della base
di dati. L'oggetto restituito da Query.deletePersistentAll()
è un numero intero che rappresenta il numero
di oggetti che sono stati cancellati.
JDOQL
(#14) cancellazione delegata al db
Query q = pm.newQuery (AuctionItem.class, "bids.isEmpty()");
int deletedCount = q.deletePersistentAll();
Ci
sono due dettagli da notare. In primo luogo, gli oggetti
cancellati non sono richiamati nella JVM a meno che
la loro classe persistente implementi l'interfaccia
DeleteCallback. In secondo luogo, l'invocazione di deletePersistentAll()
è stata fatta sulla query (per delegare la cancellazione
alla base di dati) e non su PersistenceManager (nel
qual caso un collection/array di istanze già
presenti in memoria doveva essere passato come parametro).
SQL
Penso che siate d'accordo che il codice SQL risultante
dalla deletion by query, sia molto più efficiente:
DELETE
FROM AUCTIONITEM
WHERE (0 = (SELECT COUNT(*)
FROM AUCTI_BIDS
WHERE AUCTI_BIDS.JDOID = AUCTIONITEM.JDOID))
Variabili
di query unbound
JDOQL ha supportato le variabili fin dalla versione
JDO 1.0. Le variabili sono usate solitamente in filtri
che esaminano il contenuto delle relazioni uno-a-molti
(per esempio, le collection), in cui la variabile filtra
il contenuto della collection. Possono anche essere
usate per rappresentare tutte le istanze persistenti
dei loro tipi di classi persistenti, nel qual caso vengono
definite unbound, ("non limitate").
JDO 2.0 fornisce il supporto per la definizione di un'opzione
di tipo costante che indica se un'implementazione supporta
le variabili di query unbound. Ciò rende esplicita
la portabilità delle query che usano tali variabili.
Oltre la formalizzazione di questa caratteristica, in
JDO 2.0 non c'è niente di nuovo riguardo al ruolo
indispensabile coperto dalla variabile in JDOQL. Comunque
illustrerò qui di seguito il loro uso, in parte
per aumentare la conoscenza delle possibilità
che offrono e in parte per fornire argomenti con i
quali illustrerò una nuova caratteristica di
JDO 2.0 (nella terza parte di questa serie) per la
definizione implicita di variabili, in base al contesto
nel quale sono utilizzate.
JDOQL
(#15) variabili di query bound
Ecco un esempio di una query che usa una variabile di
query bound per identificare tutti gli articoli dell'asta
per cui le offerte corrispondenti includono almeno un'offerta
fatta da David.
String
filter = "bids.contains(bid) && bid.bidder.user.userName
== 'David'";
Query q = pm.newQuery (AuctionItem.class, filter);
q.declareVariables ("Bid bid");
Collection results = (Collection)q.execute ();
SQL
SELECT
DISTINCT t0.JDOID, t0.JDOCLASS, t0.JDOVERSION, t0.CATEGORY_JDOID,
t0.DESCRIPTION,
t0.LOCATION_JDOID, t0.OPENINGPRICE, t0.RESERVEPRICE,
t0.SELLER_JDOID, t0.TITLE,
t0.WINNINGBID_JDOID, t0.WINNINGPROXYBID_JDOID
FROM AUCTIONITEM t0 INNER JOIN AUCTI_BIDS t1 ON t0.JDOID
= t1.JDOID INNER JOIN BID t2 ON
t1.BIDS_JDOID = t2.JDOID INNER JOIN BIDDER t3 ON t2.BIDDER_JDOID
= t3.JDOID INNER
JOIN REGISTEREDUSER t4 ON t3.USER_JDOID = t4.JDOID
WHERE (t4.USERNAME = 'David')
Risultati
Con il mio insieme di dati ho ottenuto soltanto un AuctionItem,
quello con la descrizione "Enterprise JavaBeans".
Sembra che David abbia interessi molto mirati!
JDOQL
(#16) variabili di query unbound
Ecco un esempio di una query che illustra le variabili
unbound. La query seleziona tutti gli AuctionItems che
hanno un offerente che inoltre ha fatto un'offerta su
un altro articolo offerto dallo stesso venditore.
La query è facilmente espressa con un candidato
estensione di AuctionItem, di una variabile non limitata
(bidder di tipo Bidder) e la variabile limitata (b di
tipo Bid). La variabile unbound itera logicamente su
tutte le istanze persistenti dell'offerente per ogni
istanza di AuctionItem del candidato.
String
filter = "bidder.bids.contains(b) && b.item.seller
== seller && b.item != this";
Query q = pm.newQuery (AuctionItem.class, filter);
q.declareVariables ("Bidder bidder; Bid b");
Collection results = (Collection)q.execute ();
Risultati
La classe candidata della query è AuctionItem,
in modo tale che la collection restituita contenga istanze
di AuctionItem. Per ogni AuctionItem, il filtro "bidder.bids.contains(b)
&& b.item.seller == seller && b.item
!= this" è controllato per tutti gli offerenti
(ricordarsi che la variabile "bidder" è
unbound). La collection dei risultati, quindi, contiene
tutti gli AuctionItems (articoli dell'asta) per i quali
esiste almeno un offerente nel quale la collection delle
offerte contenga almeno un'offerta per un diverso AuctionItem
venduto dallo stesso venditore. Nel mio insieme di dati
ho ottenuto tutti e 6 gli articoli.
SQL
SELECT DISTINCT t4.JDOID, t4.JDOCLASS, t4.JDOVERSION,
t4.CATEGORY_JDOID, t4.DESCRIPTION,
t4.LOCATION_JDOID, t4.OPENINGPRICE, t4.RESERVEPRICE,
t4.SELLER_JDOID, t4.TITLE,
t4.WINNINGBID_JDOID, t4.WINNINGPROXYBID_JDOID
FROM AUCTIONITEM t3, AUCTIONITEM t4, BID t2, BIDDER
t0, BIDDE_BIDS t1
WHERE (t3.SELLER_JDOID = t4.SELLER_JDOID AND t2.ITEM_JDOID
<> t4.JDOID) AND t0.JDOID =
t1.JDOID AND t1.BIDS_JDOID = t2.JDOID AND t2.ITEM_JDOID
= t3.JDOID
Non
ci sono grosse novità per quanto riguarda le
nuove funzionalità apportate dalle variabili
a JDOQL, rispetto a quanto già esistente in JDO
1.0. Tuttavia la formalizzazione della caratteristica
opzionale "javax.jdo.option.UnconstrainedQueryVariables"
rende esplicita la portabilità di tali query.
Si presume che tutte le principali implementazioni di
JDO supporteranno questa caratteristica.
L'autore
Robin Roos è l'autore di Java Data Objects (Addison-Wesley)
ed è membro del gruppo di esperti di JDO 2.0
(JSR-243), e VP professional services presso SolarMetric.
|