Oggi esistono molteplici modi per gestire la persistenza in applicazioni J2EE:

- Hibernate
- Entity Bean
- Entity BMP
- DAO con classi POJO
- Spring
- e altro ancora

In questo articolo mi vorrei soffermare su una mia implementazione del pattern DAO.

Problema

Persistere un'entity rispettando i seguenti requisiti:

- indipenenza dal back end (DB2, Oracle, XML, LDAP, Lucene, ecc.);
- possibilità di far partecipare uno o più DAO ad una singola transazione;
- nascondere le eccezioni specifiche di ciascun back end;
- persistere una entità logica e non fisica (qusto implica che un DAO può manipolare una o più tabelle se queste rappresentano un'unica entità logica).

Soluzione

La prima cosa che faccio quando sviluppo applicazioni web è quella di identificare le entità logiche del problema (domain problem). Ad ogni entità associo un DAO che sarà responsabile di tutte le attività di persistenza su quell'entità, es. le operazioni CRUD (CRreate, Update, Delete).

Iniziamo con il definire la nostra entità.

public class EntityValue {

    private object primaryKey;
    private String property;

    Object getPrimaryKey() {

      return primaryKey;
    }

    public void setPrimaryKey(Object key) {

      this.primaryKey = key;
    }

    String getProperty() {

      return property;
    }

    public void setProperty(String property) {

      this.property = property;
    }
}

A questo punto creiamo un'interfaccia DAO per la nostra entity.

public interface DAOEntity {

    Object create(EntityValue entity) throws DAOException;
    void update(EntityValue entity) throws DAOException;
    void delete(Object key) throws DAOException;

    // finders
    EntityValue findByPrimaryKey(Object key) throws DAOException;
    Collection findAll() throws DAOException;
}

A seconda dei back end che vogliamo supportare implementeremo l'interfaccia DAOEntity. Prima di fare ciò, è necessario definire una factory che, in base ad un parametro di configurazione, instanzia l'implementazione specifica per un dato back end.

public abstract class DAOFactory {

    private static final TransactionManager tm; // la classe figlia associerà il transaction manager relativo ad esso

    public static DAOFactory makeFactory() {

      // leggi da file di configurazione il back end desiderato (DB2, MySQL, Oracle, XML, ecc.).
    }

    public abstract DAOEntity getDAOEntity();
    // aggiungi qui altri metodi getDAOXXX

    public TransactionManager getTransactionManager() {

      return tm;
    }
}

A questo punto, per ogni back end, va implementata una classe che estende DAOFactory e implementa i metodi astratti.

Vediamo ora un'ipotetica implementazione di DAO entity nell'ipotesi che il back end sia DB2. In particolare vedremo l'implementazione della create, le altre sono più o meno simili.

public class DB2DAOEntity imlements DAOEntity {

    private static final String SQL_CREATE="INSERT INTO ....";
    private static final String SQL_UPDATE="UPDATE ....";
    private static final String SQL_DELETE="DELETE ....";

    // finders
    private static final String SQL_FIND_BY_PRIMARY_KEY="SELECT ... FROM ... WHERE KEY=?";
    private static final String SQL_FIND_ALL ="SELECT ... FROM ...";

    Object create(EntityValue entity) throws DAOException {

      DB2Transaction tx = (DB2Transaction) DAOFactory.makeFactory.getTransactionManager().getTransaction();
      Connection conn = tx.getConnection();
      PreparedStatement stmt = null;

      try {

        entity.setPrimeryKey(// generate oid);
        stmt = conn.prepareStatement(SQL_CREATE);
        stmt.setXXX(1, entity.getPrimaryKey());
        ...
        stmt.setXXX(N, entity.getProperty());
        stmt.execute();
      } catch(SQLException exception) {

        // convert in DAOException e rilancia
      } finally {

        stmt.close();
      }
    }

    void update(EntityValue entity) throws DAOException {

      ...
    }

    void delete(Object key) throws DAOException {

      ...
    }

    // finders
    EntityValue findByPrimaryKey(Object key) throws DAOException {

      ...
    }

    Collection findAll() throws DAOException {

      ...
    }
}

Si osservi come la transazione viene aperta nel business layer e passata al DAO attraverso l'area local al thread (ThreadLocal, vedi articolo Transazioni JDBC). Il DAO gestisce solo statement e result set (quest'ultimo solo nei finders). E' importante l'uso del finally per la chiusura degli statement e result set.


Page Information

  • 2 years ago [history]
  • View page source
  • You're not logged in
  • No tags yet learn more

Wiki Information

Recent PBwiki Blog Posts