Introduzione a jOOQ

I parte: Il trattato di pace tra SQL e Javadi

In questo articolo forniamo una introduzione a JOOQ, una libreria Java che fornisce un DSL (Domain Specific Language) per costruire queries tramite classi generate a partite da un database relazionale. Un passo in avanti per favorire l'interazione fra Java e SQL.

DataBase relazionali e paradigma Object Oriented

Tutti coloro che hanno lavorato su applicazioni Java che utilizzino un database relazionale sanno che SQL, il linguaggio standard per per i DB relazionali, non è stato pensato per essere astratto o Object Oriented. Non è un linguaggio general purpose, ma specific purpose. Insomma non è mai stato pensato per essere qualcosa di diverso da... SQL!

Tramite il DSL (Domain-Specific Language) fornito da jOOQ, SQL risulta quasi come se fosse stato pensato nativamente per essere supportato da Java e e può avere avere tutti i benefici e le caratteristiche di quest'ultimo linguaggio.

Primi passi con jOOQ

Vediamo qualche semplice esempio prima di approfondire l'argomento. Una query SQL come la seguente:

SELECT * FROM BOOK WHERE PUBLISHED_IN = 2011 ORDER BY TITLE

in jOOQ diviene:

factory.selectFrom(BOOK)
.where(PUBLISHED_IN.equal(2011))
.orderBy(TITLE)

Ovviamente jOOQ è in grado di gestire anche query più complesse. La seguente:

SELECT FIRST_NAME, LAST_NAME, COUNT(*)
FROM AUTHOR
JOIN BOOK ON AUTHOR.ID = BOOK.AUTHOR_ID
WHERE LANGUAGE = ‘DE'
AND PUBLISHED > ‘2008-01-01'
GROUP BY FIRST_NAME, LAST_NAME
HAVING COUNT(*) > 5
ORDER BY LAST_NAME ASC NULLS FIRST
LIMIT 2
OFFSET 1
FOR UPDATE OF FIRST_NAME, LAST_NAME

in jOOQ diventa:

factory.select(FIRST_NAME, LAST_NAME, count())
.from(AUTHOR)
.join(BOOK).on(Author.ID.equal(Book.AUTHOR_ID))
.where(LANGUAGE.equal("DE"))
.and(PUBLISHED.greaterThan(parseDate("2008-01-01")))
.groupBy(FIRST_NAME, LAST_NAME) .having(count().greaterThan(5))
.orderBy(LAST_NAME.asc().nullsFirst())
.limit(2)
.offset(1)
.forUpdate()
.of(FIRST_NAME, LAST_NAME)

I lettori più addentro alla questione stanno già capendo il tipo di approccio, che introdurremo in questo articolo e che svilupperemo eventualmente in una seconda puntata più pratica.

La release di jOOQ a cui si fa riferimento in questo articolo è la 3.0.1 (ultima stabile al momento), ma le considerazioni fatte valgono anche per le release 2.6.x.

Panoramica su JOOQ

JOOQ (Java Object Oriented Querying) [1] è una libreria Open Source lightweight interamente scritta in Java che consente il mapping di database relazionali. Essa implementa l'active record pattern [2]: non è un OR mapper come Hibernate, per esempio, ma fornisce un mapping 1:1 tra tabelle/viste e classi e tra colonne e class fields. JOOQ al livello più alto propone un DSL che consente di pensare esclusivamente a oggetti, mentre a livello più basso lavora tramite JDBC. Ma esso non introduce ulteriore complessità per gli sviluppatori, come fanno altri ORM quali Hibernate e JPA. Per questi e altri motivi può essere un'ottima alternativa a entrambi.

Caratteristiche

Le caratteristiche principali di jOOQ sono:

  • Code Generation: jOOQ genera una rappresentazione Java semplice ma completa dello schema del database. Ogni tabella, vista, stored procedure, function, enum, etc. è una classe.
  • Implementazione degli Active Records.
  • Consente la scrittura a tempo di compilazione di query SQL typesafe.
  • Supporta tutte le caratteristiche del linguaggio SQL standard, anche le più complesse (UNION, SELECT annidate, join e così via).
  • Supporta anche le caratteristiche avanzate specifiche di ogni database vendor.
  • Consente la generazione automatica anche di POJO/DTO e classi DAO.
  • Prevede la possibilità di esportare i dati in diversi formati (XML, CSV, Json, HTML, testo semplice).

A ben guardare, un arsenale di tutto rispetto.

Valutare i benefici

I maggiori benefici che jOOQ può apportare sono i seguenti:

  • Nessuna necessità di ricorrere a mapping tramite XML o altro (classi Java, annotations): tutto risiede nello schema del database.
  • Poichè la rappresentazione dello schema viene generata in Java, in fase di sviluppo di una applicazione si può beneficiare di tutte le caratteristiche messe a disposizione degli IDE per tale linguaggio (auto-completamento, syntax highlighting, etc.), oltre al già citato vantaggio di lavorare esclusivamente ad oggetti.
  • La possibilità di generare automaticamente anche i value objects e i DTO contribuisce a mantenere il codice più ordinato e manutenibile.
  • Qualora venga cambiato qualcosa nel database, ci si accorgerà subito della modifica in quanto il codice Java non compilerà più. Questo riduce notevolmente il rischio di errori a runtime.
  • Qualora si debba migrare verso un altro DB vendor, il porting dell'applicazione è semplice perchè basta semplicemente rigenerare tutti gli oggetti Java tramite i tool forniti da jOOQ.
  • Il rischio di errori di sintassi negli statement SQL è nullo.
  • Il variable binding è sempre corretto. E non c'è rischio di SQL injection.
  • Non si ha la terribile verbosità di altre soluzioni o di JDBC in fase di query building. Questo vantaggio si apprezza di più nel caso di statement particolarmente complessi.

JOOQ supporta tutti i principali database vendors, inclusi Oracle 11g, Microsoft SQL Server, MySQL, etc. Sul sito ufficiale è possibile trovare la lista aggiornata di tutti i vendor con le versioni supportate.

La figura 1 mostra l'architettura a blocchi di un una web application, mettendo in evidenza quelli  che possono essere gestiti tramite jOOQ:

 

 

Figura 1 - Architettura di una web application che integra jOOQ.

 

Generazione del codice

JOOQ consente la generazione del codice Java a partire da uno schema relazionale in diversi modi: Ant tasks, Maven, strumenti da riga di comando. Ma nel caso in cui si sviluppi un'applicazione in Eclipse risulta agevole creare una Run Configuration per eseguire tale operazione. Quest'ultima possibilità è quella che andiamo ad analizzare in questo paragrafo.

Creare una run configuration per Eclipse

Per prima cosa bisogna creare all'interno del progetto il file di configurazione XML standard di jOOQ per la generazione a partire dal database:



  
     oracle.jdbc.driver.OracleDriver
     jdbc:oracle:thin:@dbhost:1700:DBSID
     DB_TEST
     password
  
 
  
     org.jooq.util.DefaultGenerator
 
     
       org.jooq.util.oracle.OracleDatabase
 
       DB_TEST
 
       .*
 
       
     
 
     
       true
       false
      
    true
      
    true
     
 
     
       jooqbench.generated
 
       src
     

Questo file serve solo per la generazione e il refresh. Non necessita di essere incluso nel deploy dell'applicazione.

Analisi del codice

JOOQ non incide nella configurazione prevista dall'applicazione in cui è integrato e quindi non aggiunge ulteriore complessità anche da questo punto di vista. La connessione al database indicata nel file XML (tag ) serve solo per la generazione delle classi Java. L'altro tag più importante è . In esso vengono definite tutte le proprietà e le azioni relative al processo di generazione. Tramite il tag si indica la classe che si intende utilizzare per la generazione. Nell'esempio è stato indicato il generator di default previsto da jOOQ, org.jooq.util.DefaultGenerator. È possibile comunque estendere questa classe e creare un custom generator. Il tag consente di specificare il vendor, lo schema name e quali oggetti del database includere o escludere dal processo di generazione.

Tramite i tag e è possible scegliere di generare anche i POJO e i DAO per l'applicazione. La generazione di questi ultimi oggetti non è obbligatoria. Il valore di default per entrambi i tag è false. Infine tramite il tag si indicano il package e la directory di destinazione delle classi generate. Questi ultimi valori sono arbitrari: non esistono valori predefiniti in jOOQ. Il tag accetta anche path assoluti.

Una best practice in jOOQ è la seguente: riservare un package esclusivamente per la generazione ed evitare di eseguire modifiche sulle classi generate, altrimenti al primo refresh le modiche apportate manualmente verranno perse. Nessuna delle classi generate da jOOQ è final, quindi possono essere estese tutte ed  è  possibile fare l'override di tutti i metodi, ponendo ovviamente le classi custom in un package diverso da quello scelto per la generazione.

La struttura del classpath

Successivamente, bisogna controllare che nel classpath del progetto siano presenti le seguenti librerie:

  • jooq-3.0.1.jar
  • jooq-codegen-3.0.1.jar
  • jooq-meta-3.0.1.jar
  • log4j-1.2.16.jar
  • slf4j-log4j12-1.7.2.jar

oltre al driver JDBC specifico per il database che si sta usando e al file log4j.xml. A questo punto bisogna creare una nuova Run Configuration, indicando il nome del progetto e la main class di jOOQ per la generazione (figura 2).

 

 

Figura 2 - Esempio di Run Configuration in Eclipse per la generazione di classi Java tramite jOOQ.

 

Per completare la Run Configuration spostiamoci nel tab Arguments e aggiungiamo come come Program argument il nome del file XML per la generazione, preceduto da uno slash (obbligatorio), come mostrato in figura 3.

 

 

Figura 3 - Esempio di Run Configuration in Eclipse per la generazione di classi Java tramite jOOQ.

 

L'esito della run configuration

Cliccando sul button Run, parte il processo di  generazione. Facendo sempre riferimento al file XML dell'esempio, al termine del processo di generazione, jOOQ avrà creato i seguenti packages:

  • jooqbench.generated: contiene le classi con le definizioni delle tabelle, viste, chiavi, routine e sequence.
  • jooqbench.generated.routines: le implementazioni Java di Stored Procedure e Functions.
  • jooqbench.generated.tables: le implementazioni Java delle tabelle e delle viste.
  • jooqbench.generated.daos: i DAO generati.
  • jooqbench.generated.pojos: i POJOs generati.
  • jooqbench.generated.records: i Record (classi che estendono org.jooq.impl.TableRecordImpl o org.jooq.impl.UpdatableRecordImpl).

Qualora vengano apportate modifiche al database, basta eseguire nuovamente questa Configuration per ottenere il refresh dei package e delle classi Java in essi contenute.

Conclusioni

In questo articolo è stata fatta una panoramica sul framework jOOQ ed è stato spiegato perchè esso può essere preso in considerazione come possibile alternativa a altri persistence frameworks come Hibernate o JPA. Un altro interessante aspetto di questo framework è la sua possibile integrazione con Spring. Questo argomento richiede però una specifica trattazione e potrebbe essere affrontato in un articolo futuro.

Riferimenti

[1] Sito ufficiale di JOOQ

http://www.jooq.org/

 

[2] Definizione di Active Record Pattern in Wikipedia

http://en.wikipedia.org/wiki/Active_record_pattern

 

 

 

Condividi

Pubblicato nel numero
185 giugno 2013
Guglielmo Iozzia si è Laureato nel 1999 in Ingegneria Elettronica (indirizzo Biomedico) presso l‘Università di Bologna. Ha progettato e realizzato un software diagnostico per la predizione dell‘andamento della pressione intracranica in pazienti in terapia intensiva neurochirurgica. Frequenta il mondo Java dall‘inizio del 2000. Dopo numerose esperienze presso un‘azienda di Bologna…
Ti potrebbe interessare anche