Continuiamo a parlare di Wicket, il framework Java per applicazioni web, e della sua integrazione con alcuni dei più diffusi framework Java, quali Spring, iBatis e Hibernate. Dopo aver visto nel numero precedente l‘integrazione con Spring, in questo articolo ci concentriamo in particolare su iBatis.
Nell’articolo precedente è stata descritta l’integrazione di Wicket con Spring attraverso tre tipi di strategie di approccio. In questo nuovo articolo vedremo in dettaglio l’implementazione concreta e l’integrazione con il framework iBatis per quanto riguarda l’aspetto della persistenza.
Come per l’articolo precedente, la versione di Wicket a cui faremo riferimento è la 1.4-m3. A partire dalla versione 1.4 Wicket richiede necessariamente Java 1.5 o release successiva.
iBatis
iBatis [5] è un framework Open Source per la persistenza dei dati su database relazionali. La sua caratteristica fondamentale consiste nella possibilità di mappare gli statement SQL su file esterni all’applicazione Java. Il framework crea un mapping 1:1 tra gli oggetti Java e le relazioni sul database. In questo modo si ha il doppio vantaggio di mantenere il codice pulito dall’SQL e, a fronte di eventuali cambiamenti nello schema del database, di non dover effettuare modifiche alle classi. La configurazione dell’accesso alla base dati e tutto il codice SQL vanno inseriti all’interno di file XML gestiti dal framework. iBatis si occupa della gestione dell’interfacciamento verso il DB tramite JDBC e in maniera trasparente allo sviluppatore. Questi quindi non ha bisogno di scrivere delle classi apposite, ma può concentrarsi solo sulla logica applicativa. In figura 1 viene mostrata l’architettura del framework.
Figura 1 – Architettura del framework iBatis.
Oltre che per Java esistono implementazioni di iBatis anche per altri linguaggi, quali .NET e Ruby. Per maggiori approfondimenti consiglio la lettura del libro “iBatis in Action” di Begin, Goodin & Meadors [6]. La release di iBatis a cui faremo riferimento in questo articolo è la 2.3.4. Richiede Java 1.5 o release superiore.
Integrazione di iBatis nell’applicazione Comics Convention Registration
Ricorreremo anche in questo articolo all’ausilio dell’applicazione di esempio implementata per gli altri articoli di questa serie. Nell’articolo dello scorso mese abbiamo trasformato l’applicazione in una applicazione Spring based, descrivendo tre possibili strategie di approccio: avevamo introdotto l’implementazione della logica dello strato di persistenza e l’adozione del pattern DAO [4] senza soffermarci sui dettagli e senza fare riferimenti ad alcun framework specifico.
Per prima cosa creiamo il database applicativo. Esso avrà un’unica tabella, participants, in cui verranno salvati tutti i partecipanti registrati alla convention:
CREATE TABLE participant ( id int(10) NOT NULL auto_increment, firstName varchar(30) default NULL, secondName varchar(30) default NULL, country varchar(20) default NULL, talk varchar(100) default NULL, pictureName varchar(80) default NULL, PRIMARY KEY (id) ) ENGINE=InnoDB;
Nel modello dell’applicazione abbiamo già una classe Java che corrisponde alla tabella, Participant.java . Disegnamo quindi l’interfaccia della classe DAO relativa ai Participant:
public interface ParticipantDAO { public List selectAllParticipants(); public Participant selectParticipantById(String participantId); public void insertParticipant(Participant insertParticipant); public void deleteParticipant(String participantId); public void updateParticipant(Participant participantToUpdate); }
Abbiamo dichiarato i metodi relativi al CRUD (create, read, update and delete) dei Participant e il metodo per recuperarli tutti. Prima di implementare tale interfaccia dobbiamo scrivere il mapping file di iBatis relativo ai participant (Participant.xml):
PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-2.dtd"> insert into participant (firstName, secondName, country, talk, pictureName) values (#firstName#, #secondName#, #country#, #talk#, #pictureName#) delete from participant where id = #value# update participant set firstName = #firstName#, secondName = #secondName#, country = #country#, talk = #talk#, pictureName = #pictureName# where id = #id#
Questo file contiene il mapping fra la classe Java e la corrispondente tabella sul database e le cosiddette Named Queries, cioè delle query identificate da un nome, che possono essere richiamate in qualsiasi punto dell’applicazione. Affinche’ iBatis possa gestirli, tutti i mapping file dell’applicazione vanno registrati nel suo configuration file, SqlMapConfig.xml:
PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-config-2.dtd">
Possiamo a questo punto implementare il DAO:
public class ParticipantDAOImpl extends SqlMapClientDaoSupport implements ParticipantDAO { public List selectAllParticipants() { SqlMapClientTemplate template = getSqlMapClientTemplate(); return (List)template.queryForList("selectAllParticipants"); } public Participant selectParticipantById(String participantId) { SqlMapClientTemplate template = getSqlMapClientTemplate(); Object object = template.queryForObject("selectParticipantById", participantId); return object instanceof Participant ? ((Participant)object) : null; } public void insertParticipant(Participant insertParticipant) { SqlMapClientTemplate template = getSqlMapClientTemplate(); template.insert("insertParticipant", insertParticipant); } public void deleteParticipant(String participantId) { SqlMapClientTemplate template = getSqlMapClientTemplate(); template.delete("deleteParticipant", participantId); } public void updateParticipant(Participant participantToUpdate) { SqlMapClientTemplate template = getSqlMapClientTemplate(); template.update("updateParticipant", participantToUpdate); } }
Esso estende la classe astratta org.springframework.orm.ibatis.support.SqlMapClientDaoSupport di Spring e ovviamente implementa l’interfaccia ParticipantDAO. Come si può vedere, in nessun metodo è stato scritto codice SQL: tramite l’istanza di default di org.springframework.orm.ibatis.SqlMapClientTemplate vengono invocate le named queries mappate in Participant.xml. Possiamo a questo punto aggiornare il context file applicativo di Spring con le nuove dipendenze:
class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> com.mysql.jdbc.Driver jdbc:mysql://localhost/comicon root /WEB-INF/config/SqlMapConfig.xml ...
Dopo aver mappato il DataSource, abbiamo registrato i bean sqlMapClient di iBatis (che si occupa dell’esecuzione degli statement SQL mappati nei file specifici e della gestione delle transazioni) e il ParticipantDAO, avente come attributi il DataSource e il SqlMapClient.
A questo punto bisogna eliminare dalla Wicket WebApplication l’attributo di tipo ParticipantManager e aggiungere la dipendenza dal ParticipantDAO:
public class WicketExamples3Application extends WebApplication { private ParticipantDAO participantDao; ... public ParticipantDAO getParticipantDao() { return participantDao; } public void setParticipantDao(ParticipantDAO participantDao) { this.participantDao = participantDao; } }
e registrarla nel context file.
Nelle WebPage può quindi essere implementato un metodo per l’accesso al ParticipantDAO della WebApplication:
protected ParticipantDAO getParticipantDao() { return ((WicketExamples3Application)getApplication()).getParticipantDao(); }
Nei punti in cui c’è bisogno di eseguire operazioni sui dati presenti sul DB vanno sostituite tutte le invocazioni ai metodi del ParticipantManager come, per esempio, nella RegistrationPage:
public void onSubmit() { Participant currentParticipant = getParticipantData(); if(!uploadPanel.getFilename().equals("")) { currentParticipant.setPictureName(uploadPanel.getFilename()); } if(currentParticipant.getId() == 0) { getParticipantDao().insertParticipant(currentParticipant); } else { getParticipantDao().updateParticipant(currentParticipant); } feedbackPanel.info("Participant succesfully saved!"); }
Data la semplicità dell’applicazione siamo ricorsi in questo caso all'”application object approach”. Per passare al “proxy-based approach” basterebbe implementare il proxy per le dipendenze e adeguare di conseguenza il metodo con cui le WebPage recuperano le istanze di DAO dalla WebApplication come spiegato nell’articolo precedente, senza necessità di modifiche allo strato di persistenza.
Il project Eclipse con tutti i sorgenti completi dell’applicazione di esempio può essere scaricato come allegato dal menu in alto a sinistra. Le librerie necessarie per la compilazione e/o il runtime sono le seguenti:
- commons-dbcp.jar (runtime)
- commons-pool.jar (runtime)
- ibatis-2.3.4.726.jar (compilazione)
- mysql-connector-java-5.1.7-bin.jar (runtime)
- slf4j-api-1.5.0.jar (compilazione)
- slf4j-jcl-1.5.0.jar (runtime)
- spring.jar (compilazione)
- wicket-1.4-m3.jar (compilazione)
- wicket-extensions-1.4-m3.jar (compilazione)
- wicket-spring-1.4-m3.jar (compilazione)
Conclusioni
Come abbiamo visto, l’integrazione tra Spring, Wicket e iBatis non risulta complessa e porta una notevole serie di vantaggi non comune ad altri tipi di approcci nello sviluppo di una web application Java.
Abbiamo anche visto che l’interfaccia del DAO è indipendente da ciò che è stato utilizzato (iBatis) per l’implementazione. Nel prossimo articolo vedremo come è facile passare all’utilizzo di Hibernate per l’implementazione dello strato di persistenza con il minimo impatto sul resto dell’applicazione.
Riferimenti
[1] Karthik Gurumurthy, “Pro Wicket”, Apress, 2006
[2] Sito ufficiale di Wicket presso Apache
[3] Dhananjay Nene, “A beginners guide to Dependency Injection”, The ServerSide.com
[4] S. Rossini – L. Dozio, “Il pattern Data Access Object”, MokaByte 62, Aprile 2002
https://www.mokabyte.it/2002/04/pattern-dao.htm
[5] Sito ufficiale di iBatis
[6] Clinton Begin – Brandon Goodin – Larry Meadors, “iBatis in action”, Manning, 2007