A
cosa serve questo pattern
L'accesso
a sorgenti di dati implica la conoscenza e l'utilizzo
delle relative modalità di accesso Questo in
applicazione distribuite causa un dipendenza (coupled)
tra la logica di business (EJB, CORBA servant,
) e la logica di accesso ai dispositivi di persistenza
come database relazionali (RDBMS), database object oriented
(OODBMS), file XML, flat file, ecc
L'intento
del pattern DAO (Data Access Object) è di disaccoppiare
la logica di business dalla logica di accesso ai dati.
Questo si ottiene spostando la logica di accesso ai
dati dai componenti di business ad una classe DAO rendendo
gli EJB indipendenti dalla natura del dispositivo di
persistenza. Questo
approccio garantisce che un eventuale cambiamento del
dispositivo di persistenza non comporti modifiche sui
componenti di business. Inoltre legando il componente
EJB ad un particolare tipo di data repository, ne si
limita il riuso in contesti differenti.
Funzionamento
L'idea del pattern DAO si basa sulla possibilità
di concentrare il codice per l'accesso al sistema di
persistenza in una classe che si occupa di gestire la
logica di accesso ai dati. Ad esempio nel caso di un
accesso a DBMS si inserisce nel DAO tutto il codice
per effettuare operazioni sulla base dati, nascondendo
le problematiche JDBC.
Di seguito si riporta il codice di un metodo di business
che reperisce i dati direttamente dal DBMS :
import
javax.sql.*;
import java.sql.* ;
. . .
public void doSomething(String key) throws SQLException{
InitialContext ic = new InitialContext();
DataSource ds =(DataSource) ic.lookup("jdbc/MyDB");
Connection con = ds.getConnection();
String selectStatement =
"SELECT
NAME FROM ACCOUNT where ID=?";
prepStmt =con.prepareStatement(selectStatement);
prepStmt.setString(1, key);
ResultSet result = prepStmt.executeQuery();
if (result.next()) {
this.name = result.getString(1);
}
else{
throw
new SQLException("Name NOT found !");
}
}
E'
evidente come una modifica del dispositivo di persistenza
implichi modifiche al codice di tutti gli EJB che vi
accedono. Per confronto si propone lo stesso metodo
che utilizza una classe DAO che incapsula l'utilizzo
di JDBC :
import
it.mokabyte.dao.*;
public
void doSomething(String key)throws MyDataAccessException{
MyObjectValue result = this.dao.readAccount(key);
if (result != null) {
this.name = result.getName();
}
else{
throw
new MyDataAccessException("Name NOT found !");
}
}
A
parità di funzionalità offerte, l'utilizzo
della classe DAO rende indipendente l'EJB dal DBMS e
riutilizzabile per qualsiasi mezzo di persistenza accessibile
con la medesima interfaccia DAO.
Struttura
Il DAO viene invocato dal Business Object e si occupa
di effettuare l'accesso ai dati restituendoli all'applicazione.
Le
informazioni che il DAO restituisce al Business Object
sono oggetti generici ( indipendenti dal dispositivo
di persistenza ) e le eventuali eccezioni specifiche
della risorsa dati sono mascherate ( wrapped ) in eccezioni
generiche di tipo applicativo .
Sequence
Diagram
Implementazione
Il
client crea l'EJB invocando il metodo create sull'istanza
della Home Interface recuperata mediante le operazioni
di lookup e narrow.
Account
account = accountHome.create("004","Java
Duke","duke@hero.com",0);
Il Class Diagram
(clicca per un ingrandimento)
Il
container invoca il metodo ejbCreate che si avvale dell'istanza
dao creata nel metodo setEntityContext :
public
void setEntityContext(EntityContext ctx) {
. . .
try {
this.dao = new MyDaoImpl("java:comp/env/jdbc/MokaDataBase");
}
catch (NamingException e) {
. . .
}
}
Allo
sviluppatore dell'EJB è reso trasparente l'accesso
alla tabella del DBMS mediante JDBC, infatti si limita
a chiamare il metodo insertAccount del DAO.
Si noti come nel codice del bean non si ha nessun riferimento
ai package sql (java.sql e javax.sql) bensì vengono
importate l'interfaccia MyDao, la classe concreata MyDaoImpl
e l'eccezione DAOException.
import
it.mokabyte.pattern.dao.MyDao;
import it.mokabyte.pattern.dao.MyDaoImpl;
import it.mokabyte.pattern.exception.DAOException;
public
AccountPK ejbCreate(String id,String name,String address,double
balance)
throws
CreateException {
...
try {
// Use DAO to insert account
into the underlaying layer
AccountPK pk = new AccountPK(id);
this.dao.insertAccount(pk, name,
address, balance);
this.name = name;
this.email = address;
this.balance = balance;
return pk;
}
catch
(DAOException e) {
. . .
}
}
Il
metodo insertAccount effettua l'accesso fisico alla
tabella del DBMS mediante JDBC. Le SQLException vengano
"wrappate" in eccezioni applicative DaoException.
public
class MyDaoImpl implements MyDao {
public int insertAccount(AccountPK pk, String name,
String
address, double balance)
throws DAOException {
Connection con = null;
PreparedStatement stmt = null;
try {
con = dataSource.getConnection();
stmt = con.prepareStatement(
"INSERT INTO
MOKA_ACCOUNT(ID, NAME, ADDRESS, BALANCE)
VALUES (?, ?, ?,
?)");
stmt.setString(1, pk.id);
stmt.setString(2, name);
stmt.setString(3, address);
stmt.setDouble(4, balance);
int res = stmt.executeUpdate();
return res;
}
catch(SQLException sqle) {
throw new DAOException(sqle.getMessage());
}
finally {
closeResources(con, stmt, null);
}
}
Il
funzionamento degli altri metodi è analogo a
quanto appena spiegato :
Client
EJB Account DAO SQL
create ejbCreate insertAccount INSERT
findByPrimaryKey ejbFindByPrimaryKey accountExists SELECT
remove ejbRemove deleteAccount REMOVE
ejbLoad readAccount SELECT
ejbStrore updateAccount UPDATE
Un caso d'uso
Il pattern DAO si inserisce in quei contesti in cui
un accesso a componenti di persistenza deve essere reso
trasparente all'applicazione. L'esempio proposto rappresenta
il caso di una banca che ha la necessità di memorizzare
le informazioni del conto corrente utente su un dispositivo
di persistenza. In una situazione di questo tipo è
utile che gli EJB si interfaccino con il DB con un layer
di disaccoppiamento garantito dal DAO.
(clicca per un ingrandimento)
Allegati
L'esempio è scaricabile qui
Bibliografia
e riferimenti
[1] http://java.sun.com/blueprints/patterns/j2ee_patterns/data_access_object/index.html
|