MokaByte 62 - Aprile 2002 
J2EE Patterns
I puntata - Il pattern Data Access Object
di

S. Rossini
e
L. Dozio

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 qu
i

Bibliografia e riferimenti
[1] http://java.sun.com/blueprints/patterns/j2ee_patterns/data_access_object/index.html

MokaByte® è un marchio registrato da MokaByte s.r.l. 
Java®, Jini® e tutti i nomi derivati sono marchi registrati da Sun Microsystems.
Tutti i diritti riservati. E' vietata la riproduzione anche parziale.
Per comunicazioni inviare una mail a info@mokabyte.it