Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

JOOQ: SQL fluent API in java

Introduzione a JOOQ una SQL fluent API in java/scala, utilizzabile anche come DSL
Introduzione a JOOQ una SQL fluent API in java/scala, utilizzabile anche come DSL
Link copiato negli appunti

In questo articolo introduciamo JOOQ, una libreria progettata per implementare in java una fluent sql api.

Tramite la libreria avremo a disposizione una vera e propria DSL per la costruzione ed esecuzione di query SQL tramite l'invocazione di metodi ed oggetti java.

Un semplice esempio preso dal tutorial ufficiale di JOOQ può aiutarci ad avere fin da subito una idea abbastanza chiara di come possono avvenire le interrogazioni tramite questa libreria:

//...
GuestbookFactory factory = new GuestbookFactory(conn);
Result result = factory.select().from(POSTS).fetch();
//...

questo piccolo snippet di esempio corrisponde ad una query molto semplice, tramite la quale vengono ricavati i dati relativi alla tabella "post" del guestbook generato seguendo il tutorial:

SELECT * FROM 'post'

Si possono notare le due principali peculiarità della libreria: l'invocazione di una catena metodi "in cascata", ed il mapping "ad oggetti" delle tabelle, in perfetto stile ORM.

DSL o fluent api?

Il primo e principale elemento che ci interessa è la possibilità di costruire programmaticamente delle query SQL anche relativamente complesse: ciò avviene tramite l'adozione dello stile fluent api. Il primo oggetto utilizzato è una factory (vedremo dopo come utilizzarla), che esporrà il metodo corrispondente ad una SELECT, restituendo un oggetto che implementa l'interfaccia SelectSelectStep. Tramite oggetti di questo tipo è possibile tra le altre cose l'invocazione di un metodo corrispondente alla clausola WHERE, che restituirà a sua volta un oggetto di tipo SelectConditionStep, e così via:

GuestbookFactory factory = new GuestbookFactory(conn);
// costruiamo la query passo-passo, a partire dalla factory
SelectSelectStep step1 = factory.select();
SelectConditionStep step2 = step1.from(POSTS);
// otteniamo i risultati
Result result = step2.fetch();

Il primo dei due esempi equivalenti mostrati è evidentemente molto più compatto e persino più comprensibile, tramite l'invocazione implicita degli oggetti che utilizzino interfaccie di tipo "step". JOOQ consente anche l'utilizzo delle api dal linguaggio scala in maniera del tutto analoga all'interno di una corrispettiva DSL.

Da notare come l'utilizzo degli "step" garantisca la costruzione di una query almeno strutturalmente corretta.

Un ORM semplice

Per rendere particolarmente semplice la costruzione delle query all'interno del meccanismo di chiamata visto, ad ogni tabella corrisponderà una classe (e in verità avremo anche classi per descrivere tipologie di tuple). Nel caso esposto a partire dal tutorial la tabella nel database si chiama semplicemente "posts", e la tabella corrispettiva POSTS. L'utilizzo anti-convenzionale del nome in maiuscolo non è casuale, ma risponde ad una scelta convenzionale dei nomi interna a JOOQ, per garantire una certa riconoscibilità delle classi la cui funzione è solo di mappatura.

Ovviamente la libreria non ha l'ambizione di replicare le possibilità di mapping offerte da ORM complessi quali Hibernate o ibatis, ma mira ad ottenere un compromesso tra la semplicità degli oggetti java e la flessibilità di utilizzo delle DSL.

Le classi utilizzate per il mapping saranno di volta in volta dipendenti dallo specifico schema di database utilizzato, ed è possibile generarle con un apposito tool, fornito nella distribuzione standard e scritto anch'esso in java. Tale modalità richiama molto la generazione assistita dei bean in hibernate, e in effetti è anche qui richiesta una minima configurazione, che però si può ridurre ad un semplice file di properties, come mostreremo successivamente.

... e jdbc?

Naturalmente anche JOOQ rappresenta nè più nè meno che un livello di astrazione al di sopra dell'usuale bridge jdbc, quindi in effetti dovremo avere cura di caricare il driver corrispondente al dbms che intendiamo utilizzare, e sarà inoltre possibile scegliere una factory specifica (nell'esempio ci viene proposta ad esempio la factory specifica per mysql).

Un piccolo esempio

Creiamo il database di esempio

Vogliamo a questo punto creare un database di prova, con il quale interagiremo tramite JOOQ. A tale scopo abbiamo scelto di utilizzare mysql come dbms, e riutilizzeremo le definizioni DDL proposte per l'esempio "prenotazioni alberghiere" della guida mysql (il file sql relativo è allegato nel progetto).

Per i dettagli di installazione del dbms si rimanda alla guida mysql.

Iniziamo ad utilizzare le fluent SQL Api

la prima cosa da fare è naturalmente il caricamento in memoria del driver java/jdbc per mysql, e l'apertura di una connessione:

Class.forName("com.mysql.jdbc.Driver").newInstance();
// apriamo la connessione al database
Connection connection = DriverManager.getConnection(url, userName, password);
// creazione dei dati...
// TODO JOOQ...
// chiudiamo la connessione al database!
connection.close();

Nel piccolo snippet sopra abbiamo voluto sottolineare un elemento importante da tenere presente utilizzando JOOQ: l'apertura e chiusura delle connessioni al database va gestita esternamente alle chiamate specifiche della libreria, che si occupa soltanto della parte di interrogazione (in pratica: JOOQ non si preoccupa di chiudere le connessioni, lasciando allo sviluppatore il compito di gestirne apertura e chiusura).

Una volta aperta la connessione al database, carichiamo il file SQL con le DDL di creazione di database, tabelle e dati relativi: per semplicità abbiamo omesso i dettagli del metodo di importazione da file, per i quali rimandiamo ai sorgenti dell'esempio allegato.

// importiamo i dati del progetto "prenotazioni alberghiere"
importSQL(connection, new FileInputStream("SQL/PrenotazioniAlberghiere.sql"));
// TODO JOOQ...

Finalmente siamo in grado di effettuare una prima query di prova, molto semplice, per farci restituire i dettagli delle prime 4 camere presenti nella base dati, ordinate in base al tipo:

Factory factory = new Factory(connection, SQLDialect.MYSQL);
Table<Record> camere = new TableImpl<Record>("camere").asTable();
Result<Record> res = factory.select().from(camere).orderBy(3).limit(0, 10).fetch();

Come si vede nell'esempio è possibile istanziare un oggetto factory a partire da una connessione aperta (che chiuderemo alla fine, come già accennato ), e da un parametro che consente di scegliere il dialetto SQL da utilizzare ( in questo caso mysql). Analogamente avremmo potuto utilizzare una classe factory generata ad hoc, ma questo lo vedremo dopo.

Per ora quello che ci interessa è che la query corrispondente alla "catena" di metodi utilizzata è la seguente:

SELECT * FROM `camere` ORDER BY 3 ASC LIMIT 0, 10

Esportazione dei dati in XML e JSON

Una volta ottenuti i risultati è molto facile navigarli (tramite degli iteratori, in maniera quindi molto più immediata degli usuali ResultSet), ed è particolarmente semplice esportarne il contenuto in vari formati: nell'esempio abbiamo generato per semplicità una visualizzazione testuale, ma allo stesso modo si può generare come stringa una rappresentazione html, xml o json della tabella dei risultati, il che può risultare molto utile per applicazioni android e web.

Riepilogo delle caratteristiche

  • fluent api / DSL java e scala
  • generazione assistita delle classi/interfaccie per il mapping
  • supporto (via jdbc) per diversi dialetti SQL:
    • MySQL
    • Postgres
    • Oracle
    • SQL Server
    • SQLite
  • c'è un unico concetto di tabella: è aggiornabile (CRUD) e può essere visitata come lista, in maniera molto più semplice di un ResultSet
  • assenza di transazioni (apertura/chiusura di connessioni e le transazioni vanno gestite manualmente)
  • assenza di sessioni (vedi hibernate) e di cache interne

Approfondimenti

Nel prossimo articolo vedremo di familiarizzare anche con il tool per la genrazione automatica di classi e interfacce


Ti consigliamo anche