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.
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.
BusinessObject (OrderEJB
)
abstrakcja biznesowych operacji.
przechowuje referencje do DAOImplementor.
Nie wie z jakiej bazy danych korzysta.
DataAccessObject (OrderDAO
)
interfejs z możliwymi operacjami na bazie danych.
niezależny od bazy danych
DAOImplementor (OrderDAOCS
,
OrderDAOOracle
,
OrderDAOSybase
)
implementuje DataAccessObject interface.
zależny od bazy danych.
Baza Danych (Cloudscape, Oracle, or Sybase database via JDBC API)
przechowuje dane :)
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:
Większa niezależność przy łączeniu (deployment). DAO zawiera dostęp do danych w interfejsie. Implementacja interfejsu może być konfigurowana podczas fazy łączenia (deployment time)
Niezależność implementacji od sposobu składowania danych.
Wady:
Zwiększenie złożoności rozwiązania. Dodajemy dostęp do obiektów DAO, zwiększamy liczbę obiektów w systemie, oraz kod staje się trochę mniej intuicyjny.