JPA 2 è la specifica Java per l'accesso, la persistenza e la gestione dei dati tra oggetti Java e database relazionali. Abbiamo già introdotto la specifica in articoli precedenti in cui abbiamo presentato un confronto con JDO e visto che uno dei principali motivi per cui JPA è preferibile è l'ampia popolarità che ha portato alla diffusione di diversi tools a supporto degli sviluppatori.
In questo articolo esaminiamo il Dali Java Persistence Tools di Eclipse, uno strumento utile a snellire lo sviluppo di applicazioni che richiedono il supporto di JPA. Esamineremo alcune delle principali funzionalità del tool e vedremo come impostare un nuovo progetto che supporti direttamente JPA o come convertire uno esistente in un progetto JPA.
Ci occuperemo anche di feature che consentono di velocizzare la creazione delle entità o di importarle direttamente dalle tabelle di un database già esistente.
Precondizioni
Per seguire gli esempi che faremo occorre avere installato il Java Development Kit (JDK 6), aver impostata la variabile d'ambiente JAVA_HOME
e aggiornato il Path di sistema.
Inoltre daremo per acquisiti i concetti di base di JPA 2. Ecco due articoli importanti per approfondire o rinfrescare le conoscenze:
>> Leggi Introduzione a JPA e confronto con JDO
>> Leggi JPA 2: generare chiavi primarie e relazioni
In questi articoli troviamo qualche informazione in più su Hibernate che è l'implementazione di JPA che utilizziamo in coppia con il database relazionale HSQLDB (HyperSQL).
>> Leggi Introduzione a HyperSQL.
In allegato abbiamo il file di properties server.properties
per inizializzare il database che utilizzeremo negli esempi seguenti.
Configurare Eclipse
I Dali Java Persistence Tools per Eclipse sono inclusi come parte dell'"Eclipse Web Tools Platform (WTP) 3.1", tools che si trovano inclusi negli Eclipse IDE for Java EE Developers a partire da Eclipse 3.5 Galileo, pertanto non occorrono ulteriori configurazioni o installazioni.
Quindi la via più semplice è lavorare direttamente con la versione JEE dell'IDE.
In alternativa, disponendo dei soli IDE di base (Eclipse Classic o Eclipse ID for Java Developers, a partire dal 3.5), è possibile scaricare il corrispondente Java EE Package.
Come ulteriore possibilità, se non si vuole effettuare l'installazione dell'intero Java EE Package, possiamo scaricare il Web Tools Platform per l'IDE in uso alla voce Help/Install New Software...
Ad esempio, per la versione Eclipse Indigo, occorrerà selezionare (Work with
) la voce Indigo e selezionare il Web, XML, Java EE and OSGi Enterprise Development
. Al suo interno sarà possibile spuntare il Dali Java Persistence Tools - JPA Support
e avviare il download.
Per le versioni precedenti alla Indigo, selezionare l'update WTP dal sito (Work with
) http://download.eclipse.org/webtools/updates/
.
Impostare un progetto JPA
Negli articoli precedenti abbiamo già visto che è sufficiente un normale Java Project per utilizzare il supporto JPA. Eclipse con i Dali Java Persistence Tools, fornisce un insieme di strumenti utili a velocizzare le attività connesse con la persistenza.
Per iniziare a sperimentare il tooltik iniziamo con il creare un progetto JPA: (File > New > JPA Project
oppure File > New > Project... > JPA > JPA Project
). Occorrerà poi configurare alcune voci.
Nella prima schermata scegliamo il nome del progetto, ad esempio JPATestProject
, e la versione di JPA (attualmente la 2.0). È possibile lasciare invariate le altre voci (ci viene chiesto ad esempio se le entità devono essere incluse nel file persistence.xml
o scoperte in automatico).
Clicchiamo due volte sul pulsante Next
passando alla schermata "JPA Facet". Qui possiamo scegliere la piattaforma JPA di riferimento. Se disponibile, scegliamo Hibernate (JPA 2.x)
, altrimenti possiamo utilizzare la voce Generic 2.0
.
Occorre quindi scegliere il set di librerie dell'implementazione JPA. Scegliendo la voce Disable Library Configuration
e ci assumiamo la responsabilità di importare le librerie successivamente. Scegliendo invece la voce User Library
possiamo caricare o creare ex novo una libreria utente che accoglierà il set minimo di librerie di cui necessitiamo.
Se non è già disponibile una Libreria Utente idonea, come evidenziato dall'immagine seguente, scegliere l'opzione Manage libraries...
quindi New
o Import
.
Una volta creata una Libreria Utente, i progetti JPA successivi potranno utilizzare direttamente questa libreria risparmiando il tempo necessario a caricare a mano i singoli Jar.
Scelto un nome, ad esempio "HibHSQL Libraries", senza spuntare la voce System library
(a meno di non voler perdere inutilmente mezzo pomeriggio), possiamo caricare il set minimo di librerie già visto nell'articolo JPA 2 e la persistenza in Java. È possibile creare una Libreria Utente che includa sia le librerie dell'implementazione JPA di riferimento che i driver del database.
La voce Connection
per ora può restare vuota (<None>
). Ne scopriremo in seguito l'utilità.
Possiamo quindi premere Finish
e osservare quanto prodotto. Il progetto inizializzato ospiterà in automatico la cartella META-INF
con all'interno il file persistence.xml
.
In generale, per ragioni di portabilità del progetto, è buona regola importare nel progetto stesso le librerie usate. Il JPA Project mette a disposizione la cartella "build
", ma è possibile creare una nuova directory appositamente.
Quanto presentato finora è il caso standard nel quale si dispone di un database indipendente. È possibile fare riferimento a un embedded database messo a disposizione ad esempio da un Application Server. In questo caso occorrerà indicarlo nella prima schermata, alla voce Target runtime
(ad esempio JBoss Runtime Server), per poi indicare le corrispondenti librerie (Library Provided by Target Runtime
) nella terza schermata ("JPA Facet"). Ancora una volta, per l'impostazione del progetto non sarà necessario indicare esplicitamente la connessione.
Persistence XML Editor
L'XML è potente ma scomodo, soprattutto quando vogliamo popolare il file di configurazione come il persistence.xml
. Per questo i Dali Tools mettono a disposizione un'interfaccia per agevolare l'inserimento dei dati.
Cliccando col tasto destro sul file persistence.xml
, si potrà aprirlo in modalità Persistence XML Editor (persistence.xml > Open With > Persistence XML Editor
).
Appariranno le informazioni su diverse schede. Per fare un po' di pratica, ricostruiamo il file properties.xml
usato negli articoli precedenti precedenti.
La prima scheda è la scheda "General", contenente le informazioni sulle persistence-unit
. Di default viene impostata una persistence-unit
con il nome del progetto. Cambiamola in "testhsqldb
". È interessante notare che, nonostante nei precedenti articoli su JPA abbiamo visto che è possibile avere in contemporanea più di una persistence-unit nel file persistence.xml
, in questo caso l'azione verrebbe interpretata come un errore (nella sezione seguente vedremo come è possibile evitare il messaggio d'errore).
Nella scheda "Connection" modifichiamo il campo Transaction type
, impostando Resource local
.
Nella scheda "Properties" aggiungeremo (Add...
) le seguenti voci:
Parametro | Valore |
---|---|
hibernate.hbm2ddl.auto |
update |
hibernate.connection.url |
jdbc:hsqldb:hsql://localhost/testdb1 |
hibernate.connection.username |
sa |
hibernate.connection.password |
Una volta salvato, il file persistence.xml
sarà pronto. Cliccando sulla scheda "Source", sarà possibile vedere direttamente il codice XML. Se si dispone dell'Hibernate Settings, tra le schede troveremo anche "Hibernate" con le principali proprietà impostabili.
Nella prossima sezione vedremo come creare entità e relazioni.
JPA Entity
Possiamo ora pensare a introdurre la prima entità del progetto. Utilizziamo il wizard messo a disposizione dai JPA tools. Una volta creato un package nella cartella src, lo chiamiamo "jpatestpack", premere con il tasto destro sul package, quindi New > JPA Entity
(oppure New > Other > JPA > JPA Entity
).
Inseriamo il nome della classe, "EntityTest", premiamo su Next e osserviamo le opzioni che abbiamo a disposizione nella scheda successiva.
Possiamo personalizzare il nome della tabella (Table name
), scegliere il tipo di accesso (Access type
), e soprattutto aggiungere gli attributi di cui necessitiamo. Come primo esempio, ci limitiamo ad aggiungere (Add..
.) due campi:
Type | Name |
---|---|
int |
id |
String |
nome |
Spuntiamo la voce Key
per il primo campo, rendendolo chiave primaria dell'entità.
Cliccando su Finish, potremo esplorare l'entità realizzata automaticamente. Di default la classe:
- implementa l'interfaccia
Serializable
, - contiene i metodi di
get
eset
per gli attributi aggiunti dall'utente, - viene aggiunta in automatico come tag
<class>
, nellapersistence-unit
delpersistence.xml
(con relativo nome dell'entità).
Creare semplici entità è molto semplice, ottenendo un POJO senza dovere mettere mano al codice. È possibile immettere tipi complessi come un java.util.ArrayList
, specificando anche il parametro della lista (ad esempio java.util.ArrayList <String>
).
Segnalazione di warnings ed errori
Hibernate non richiede la presenza dei nomi delle entità nel file persistence.xml
. Provando a rimuovere il tag <class>
relativo all'entità appena creata, otterremo per risposta la segnalazione di un errore del tipo:
Class "jpatestpack.EntityTest" is mapped, but is not included in any persistence unit
Negli articoli precedenti abbiamo visto però che lavorando con Hibernate questo non è un errore, pertanto possiamo modificare le impostazioni del progetto affinché tale errore non venga sollevato.
Possiamo scegliere se ignorarlo, oppure se segnalarlo come Info o come Warning. Ad esempio potremmo segnalarlo con un Warning giusto per tenerne traccia nel caso si decidesse di cambiare il persistence provider.
Per modificare le impostazioni, accedere alle proprietà del progetto, selezionare la sottoscheda JPA > Errors/Warnings
, abilitare le modifiche (Enable project specific settings
), esplorare il campo Type
e modificare la voce "Class is mapped, but is not a persistence unit:
", impostando nel menu a tendina la voce Warning
e dare l'OK.
L'errore lascerà il posto a un semplice Warning. Allo stesso modo potremo evitare che la presenza di più di una persistence-unit
generi un messaggio d'errore agendo sulla voce: Project/Multiple persistence units defined
.
JPA Perspective, chiavi primarie e relazioni
Abbiamo esaminato un primo insieme di possibilità che ci viene offerto dal Dali tools. Aprendo la prospettiva JPA avremo a disposizione l'intero spettro di possibilità. Se la prospettiva non è già attiva: Window > Open Perspective > JPA
(eventualmente Window > Open Perspective > Other… > JPA
)
In basso a destra osserviamo la scheda JPA Details
. Premendo con il tasto sinistro sul nome di una entità, vedremo che la scheda ci fornirà una serie di dettagli customizzabili. Avremo ad esempio la possibilità di trasformarla in una classe Embeddable
, o potremo impostare delle politiche automatiche di generazione della chiave primaria (Primary Key Generation
).
Premendo con il tasto sinistro sugli attributi dell'entità, la scheda mostrerà i dettagli del mapping dei campi. Avremo la possibilità di mappare l'attributo come Transient
, o di indicare la presenza di una relazione, ad esempio del tipo One-to-One, o di specificare la politica di Fetch (di default impostata su Eager).
Relazioni
Creando una seconda entità, ad esempio EntityTest2
, tra i tipi messi a disposizione dall'Entity Properties
sarà presente anche il tipo dell'entità creata in precedenza, EntityTest
. Potremo pertanto impostare il tipo di relazione che lega le due classi.
Ipotizziamo dunque che l'entità EntityTest2
possiede un campo del tipo EntityTest
. Cliccandoci sopra con il tasto sinistro del mouse potremo impostare il tipo di relazione nella scheda JPA Details
.
Agendo sulle impostazioni del mapping, possiamo aggiungere l'annotazione desiderata, nel nostro caso @OneToOne
. Una volta scelta l'annotazione nella scheda verranno mostrati una serie di dettagli personalizzabili, come entità target, tipo di Fetch, l'eventuale politica di Cascade, la strategia di Join. Per il nostro esempio è possibile lasciare tutto invariato.
Nella prossima sezione vedremo come generare le entità a partire dalle tabelle.
Connessione al database e generazione delle entità dalle tabelle
Finora abbiamo sempre ottenuto la tabella partendo dall'entità. JPA Project ci mette a disposizione il passaggio inverso, ottenere le entità partendo dalle tabella di un database. Per poter adoperare questo strumento serve però una connessione al database e un supporto non generico da parte delle piattaforma. In questa sezione vedremo come impostare la connessione, ottenere un supporto specializzato, per poter infine ottenere le entità partendo da un database già esistente.
Connessione al database
Avevamo già visto durante la creazione del progetto la possibilità di connettersi a un database ma l'avevamo tralasciata. Questa possibilità di norma non è necessaria, per cui avevamo sorvolato l'argomento.
Entrati nelle proprietà del progetto, accediamo alla voce JPA. Alla voce Connection
potremo aggiungere una connessione (o selezionare una aggiunta in passato).
Scelto di aggiungere una connessione (Add connection...
), selezioniamo il RDBMS cui siamo interessati (nel nostro caso HSQLDB), modifichiamo il nome proposto (se interessati a farlo) e premiamo su Next
.
Se è la prima volta che effettuiamo la connessione, potremmo dover definire un nuovo driver (il pulsante New Driver Definition
mostrato nell'immagine precedente). E, sempre se è la prima volta che effettuiamo la connessione, potremmo dover indicare la posizione dei driver hsqldb.jar. In tal caso, dalla scheda JAR List
selezionare la voce hsqldb.jar
e cliccare sul pulsante Edit Jar/Zip...
, ciò consentirà di selezionare la posizione corretta del jar (è possibile trovarlo nella cartella hsqldb/lib).
A questo punto si aprirà una schermata che ci permetterà di impostare i parametri di connessione al database. Ipotizziamo di avere già disponibile un database attivo in modalità server. Ipotizzando che il database si chiami "db1
", alias "testdb1
", con user e password impostati sui valori default, dovremo impostare i seguenti campi:
Campo | Valore |
---|---|
Database | db1 |
Database location | hsql://localhost/testdb1 |
Per verificare il corretto inserimento dei dati utilizzare il tasto Test Connection
. Se il test ha successo possiamo premere sul tasto Finish
.
Impostata la connessione, se non si è scelto di attivarla alla chiusura del wizard (Connect when the wizard completes
), occorrerà premere su Connect
e il database sarà connesso.
Se la prospettiva JPA è attiva, le connessioni appaiono in basso a sinistra, nella scheda Data Source Explorer
. La connessione ci permetterà di esplorare ed eventualmente interrogare il database cui siamo connessi. Dalla scheda sarà possibile avviare e interrompere la connessione. E sarà possibile trovare le tabelle normalmente inserite nel percorso: db1/Catalogs/PUBLIC/Schemas/PUBLIC/Tables
Hibernate Settings
Volendo generare le entità dal database, occorrerà qualcosa di più del supporto generico offerto dall'IDE. Accedendo alle proprietà del progetto, osservare la voce Platform
. Se è disponibile o già selezionata la voce Hibernate (JPA 2.x)
, una volta selezionata questa voce non occorre fare altro, è possibile passare al prossimo paragrafo.
Se invece la voce non è disponibile e si vuole procedere con la generazione delle entità dalle tabelle del database, occorrerà installare il supporto Hibernate sull'IDE Eclipse. Per farlo, è possibile accedere all'Eclipse Marketplace, cercare la versione di Hibernate Tools
idonea al proprio IDE e installarla.
Una volta completato il processo di installazione e riavviato l'IDE, occorrerà impostare la voce Platform
(proprietà del progetto, scheda JPA) su Hibernate. È possibile a questo punto che sarà necessario modificare alcune delle impostazioni relative ai messaggi d'errore JPA, come visto nella precedente sezione.
Se permane un errore relativo al progetto (Console configuration "" does not exist
), sarà necessario impostare le configurazioni di Hibernate (incorrere in problematiche di questo tipo non è raro nei progetti già esistenti). Occorre per prima cosa andare nelle configurazioni della console:
Run -> Run Configurations...
e selezionare le impostazioni di Hibernate, come mostrato nella figura seguente:
Occorrerà creare una nuova configurazione (tasto destro su Hibernate Console Configuration
, quindi premere New
), scegliere il progetto cui applicarle (nel nostro caso il progetto JPATestProject
), impostare come tipo (Type
) JPA (jdk 1.5+)
e impostare come Database Connection
la voce [JPA Project Configured Connection]
. Applicare le modifiche e accedere alle proprietà del progetto. Alla voce Hibernate Settings
abilitare il supporto (Enable Hibernate support
) e scegliere tra le configurazioni (Default Hibernate Console configuration
) la configurazione appena creata.
Una volta premuto OK
, è possibile che l'errore continui a rimanere. A questo punto effettuare un clean
del progetto (Project -> Clean...
). Se il problema permane, non rimane che spegnere l'IDE e riattivarlo.
Generate Entities from Tables
Impostata una connessione (non deve essere necessariamente attiva, è sufficiente che sia stata impostata e provata per verificarne il corretto funzionamento) e impostato correttamente il supporto della piattaforma, siamo finalmente in grado di generare le entità partendo direttamente dalle tabelle.
Premendo con il tasto destro sul progetto troveremo tra le scelte la voce JPA Tools > Generate Entities from Tables...
Una volta scelto il package di destinazione (se scelto un package non esistente verrà creato automaticamente), sarà possibile utilizzare la configurazione della console già impostata, oppure (rimuovendo la spunta dalla voce Use Console Configuration
), sarà possibile scegliere una specifica connessione al database. È possibile lasciare invariato l'autodetect
per il dialetto del database.
Premuto il tasto Finish
, le entità saranno importate nel progetto. A questo punto è possibile la presenza di alcune entità ripetute o eventuali segnalazioni di errori dovute al disallineamento tra il file persistence.xml
e le entità, nel caso in cui non sia stata disattivata la segnalazione di errore riguardante la presenza di entità non mappate sul file persistence.xml.
SQL statements e Named Queries
Tra gli strumenti messi a disposizione dalla prospettiva JPA c'è l'editor per la scrittura ed esecuzione di statements SQL. Di norma può essere utilizzato come un'alternativa al Database Manager
, con il vantaggio di non richiedere una finestra esterna all'IDE. Per attivare l'editor occorrerà premere sul pulsante Open scrapbook to edit SQL statements
, presente nella scheda Data Source Explorer
, cerchiato in rosso nell'immagine seguente in basso a sinistra.
L'immagine evidenzia i principali elementi di interesse. Premuto il pulsante indicato, si aprirà il Connection profile
(ovale rosso in alto) che chiederà gli estremi della connessione. Viene richiesto tipo, nome della connessione e schema del database. È sufficiente inserire il nome della connessione scegliendola tra quelle già impostate, il tipo verrà individuato automaticamente. Se le tabelle che vogliamo interrogare sono pubbliche, non è nemmeno necessario scegliere lo schema. È però necessario che la connessione sia avviata (è possibile avviarla dal Data Source Explorer).
Ora è possibile scrivere una interrogazione nella scheda SQL Scrapbook
. Per verificarne le funzionalità possiamo inserire una query semplice, del tipo:
select * from EntityTest
Premendo con il tasto destro sulla scheda SQL Scrapbook
e scegliendo l'opzione Execute Current Text
, sarà possibile eseguire la query. I risultati verranno presentati nella scheda SQL Results
. La scheda Status
mostrerà alcuni dati statistici come il tempo necessario per l'esecuzione, mentre la scheda ResultX
mostrerà il risultato dell'interrogazione.
Named Queries
Per la creazione di Named Queries, una volta selezionata una entità, sarà possibile aggiungerne una dalla scheda JPA Details
. Esplorando il menu Queries
, aggiungere (Add...
) una query impostandone il nome e il tipo (Named Query
o Named Native Query
). Selezionata la query aggiunta, verranno visualizzati nella stessa scheda ulteriori dettagli e la possibilità di specificare la forma della query.
Convertire un progetto Java in un progetto JPA
Abbiamo visto che è possibile lavorare con normali progetti Java, ma che i tools del JPA Project possono risultare comodi, soprattutto nel caso di grossi progetti. Può risultare utile allora convertire un progetto Java già esistente in un progetto JPA. Per convertire un progetto esistente premere con il tasto destro sul progetto e scegliere l'opzione Configure > Convert to JPA Project...
Spuntare la voce JPA
(di norma dovrebbe essere già spuntata), premere Next
due volte e... ritorneremo alla schermata "JPA Facet" che ormai ci è divenuta familiare. A questo punto potremo specificare la piattaforma, le librerie e la connessione, tutti task già affrontati in precedenza.
Una volta terminato di impostare la schermata possiamo premere FINISH
e il progetto verrà convertito. Potrebbe essere sollevato qualche errore (ad esempio il solito errore dovuto all'assenza del mapping delle entità nel file persistence.xml
o qualche imprecisione nel file di persistenza), ma in compenso se si imposterà la piattaforma Hibernate le impostazioni Hibernate (Hibernate Settings
) verranno impostate in automatico.
Perché utilizzare un progetto JPA
Abbiamo già visto che è possibile utilizzare JPA con un comune progetto Java. Perché allora ricorrere al progetto JPA? Un progetto JPA permette di risparmiare tempo, ad esempio evitando la necessità di scrivere i POJO, fornendo dei comodi wizard per l'impostazione delle entità o ottenendo le entità partendo direttamente dalle tabelle di un database. Inoltre la panoplia di strumenti messi a disposizione spesso evita la necessità di ricorrere a interfacce esterne all'IDE, ad esempio l'editor per SQL Statements consente di minimizzare l'uso del Database Manager
.
Se si vuole cercare un difetto, l'unico riscontrato riguarda il tempo necessario ad estendere le funzionalità del'IDE. Se abbiamo un IDE per progetti JEE, probabilmente non occorrerà aggiungere niente, mentre se lavoriamo con un IDE classico e sarà necessario un po' di tempo per installare Dali Tools e supporti complementari per le funzionalità avanzate. Considerando i vantaggi precedentemente descritti, non è comunque tempo perso.