Untitled Document
   
 
Usare JDO 2.0: JDOQL - Parte 1
di Robin Roos, traduzione di Lorenzo Felici

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.