Data Access Object (DAO)

Intencje

Oddzielenie logiki biznesowej od dostępu do danych. Chcemy móc w łatwy sposób zmieniać sposób składowania danych (bazę danych) bez zmieniania całego kodu poziomu logiki biznesowej.

Sposób realizacji

W standardowych aplikacjach dostęp do danych z bazy danych jest umieszczony w enterprise beans odpowiedzialnych za logikę biznesową. Tak wiec zmienienie bazy danych wiąże się ze znacznymi zmianami w kodzie spowodowanymi rożnymi sposobami dostępu do danych. Sposób który opisze ma na celu wyłączenie dostępu do danych z enterprise beans i umieszczenie abstrakcji tego dostępu w oddzielnych interfejsach. Enterprise beans dba o logikę biznesowa działając na tym interfejsie, który jest implementowany różnie w zależności od tego jaką baza danych będziemy się posługiwać.

W momencie łączenia plików (deployment time) administrator aplikacji ustawia właściwą implementację dla danego interfejsu.

Struktura

Structure Diagram

Opis

BusinessObject (OrderEJB)

DataAccessObject (OrderDAO)

DAOImplementor (OrderDAOCS, OrderDAOOracle, OrderDAOSybase)

Baza Danych (Cloudscape, Oracle, or Sybase database via JDBC API)

Przykład kodu

Jako przykład pokażę, jak komponent OrderEJB tworzy nowe zamówienie (co wiąże się z wstawieniem tego zamówienia do bazy danych). Działa na OrderDAO bedacym interfejsem DAO.

public Integer ejbCreate(Collection lineItems, Address shipToAddr, 
                         Address billToAddr, String shipToFirstName, 
                         String shipToLastName, String billToFirstName,
                         String billToLastName, CreditCard chargeCard, 
                         String carrier, String userId,
                         double totalPrice, Locale locale)  
    throws CreateException, OrderAppException {

    orderDetails 
        = new MutableOrderModel(-1, lineItems, shipToAddr,
                                billToAddr, shipToFirstName,
                                shipToLastName, billToFirstName,
                                billToLastName, chargeCard,
                                carrier, userId,
                                Calendar.getInstance(),
                                Order.PENDING, totalPrice, locale);
    // ...

    OrderDAO dao = getDAO();
    int id = dao.create(this.orderDetails);
    // ...
}

Metoda getDAO() znajdująca się w OrderDAOFactory pobiera (przez JNDI) implementacje DAO.

public static OrderDAO getDAO() throws OrderDAOSysException {
    // ...

    InitialContext ic = new InitialContext();
    String className = (String) ic.lookup(JNDINames.ORDER_DAO_CLASS);
    orderDao = (OrderDAO) Class.forName(className).newInstance();
    // ...

    return orderDao;
}

OrderEJB potem woła metodę create() z DAO, która tworzy nowe zamowienie. Czesc działania tej metody znajduje się w metodzie insertOrder(), która jest różnie implementowana w zależności od używanej bazy danych.

Sprawdzanie bładów może wyglądać w następujący sposób w Claudscape ( OrderDAOCS.insertOrder())

stmt = createPreparedStatement(dbConnection, queryStr);
int resultCount = stmt.executeUpdate();

if ( resultCount != 1 )
    throw new OrderDAODBUpdateException(
        "ERROR in ORDER_TABLE INSERT !! resultCount = " 
            + resultCount);

Zaś w Oracle ( OrderDAOOracle.insertOrder())

stmt = dbConnection.prepareStatement(queryStr);
resultCount = stmt.executeUpdate();

if ( resultCount != 1 )
    throw new OrderDAODBUpdateException("ERROR in ORDER_TABLE INSERT "
                                        + "!! resultCount " + resultCount);
else {
    queryStr = "SELECT ordernum.currval from " + DatabaseNames.ORDER_TABLE;
    ResultSet rs = stmt.executeQuery(queryStr);
    if ( !rs.next() ) {
        throw new OrderDAOAppException("ERROR in selecting OrderId !!");
    } else {
        oid = rs.getInt(1);
        if (oid < 1)
            throw new OrderDAOAppException("ERROR in getting OrderId !!"
                                           + " orderId = "+ oid);
    }
}

Zalety i wady rozwiązania

Zalety:

Wady: