Chapter 4통합 티어 패턴

 8.     The Data Access Object pattern
J2EE개발자들은Data Access Object (DAO)디자인 패턴을 사용하여 저수준의 데이터 액세스 로직과 고급 비즈니스 로직을 분리한다. DAO패턴을 구현하는 것은 단순히 데이터 액세스 코드를 작성하는 것 이상이다.이 패턴을 사용하여 저수준 데이터 액세스와 고급 비지니스 로직을 분리한다.전형적인DAO구현에는 다음과 같은 요소들이 있다.
 
n        DAO팩토리 클래스
n        DAO인터페이스
n        DAO인터페이스를 구현하는 구체적 클래스
n        데이터 전송 객체들(밸류(value)객체)
 
구체적인DAO클래스에는 특정 데이터 소스로 부터 데이터에 액세스하는데 쓰이는 로직이 포함되어 있다.
 
구조

사용자 삽입 이미지

Data Access Object pattern class diagram

사용자 삽입 이미지

Data Access Object pattern sequence diagram
 
 
 
역할
n         Business Object :
Business Object
Data클라이언트를 대표한다.이것은Data source에 접근하여 데이트를 얻거나 저장하는 것을 목표로하는 객체이다.Business ObjectSession Bean, Entity Bean또는 별도의Java Object로 구현된다.
n         Data Access Object :
Data Access Object
은 이 패턴에서 중요한 객체이다. Data source에 투명한 접근을 가능하게 하기 위하여Data AccessObjectBusiness Object을 위해 근본적인 데이타 접근를 추상한다. BusinessObjectData AccessObject에게Data loadstore행위를Delegate한다.
n         DataSource :
Data source
구현체를 대표한다. Data sourceRDBMS, OODBMS, XML repository, flat file system등등일 수있다.Data source는 또한 서로다른 시스템(legacy/mainframe),서비스(B2B service)또는 특정한 종류의Repository(LDAP)일 수 있다.
n         Value Object(Transfer Object) :
Data
전송에 사용되는 전송객체를 대표한다. Data Access Object패턴에서 클라이언트에게 전송되는 데이터의 왕복통신비용을 줄이고 객체화된 데이터를 전달하기 위해 사용되며 또,클라이언트로부터Data sourceData을 갱신하기 위해Data Access Object에게 변경데이터를 전달 할때도 사용된다.
 
의도
Data에 대한 접근 방법은Data Base의 종류에 종속적이다.그런 종속적인 영향이 다른 어플리케이션에 전파되지 않게 하기 위해Data Source에 접근하는 추상화 객체를 사용한다.
 
예제소스
 
Class

사용자 삽입 이미지

 
소스
package dao;
 
public class Customer implements java.io.Serializable {
   int CustomerNumber;
   String name;
   String streetAddress;
   String city;
   
   public Customer() {
       
   }
   
   public int getCustomerNumber() {
       return CustomerNumber;
   }
 
   public void setCustomerNumber(int CustomerNumber) {
       this.CustomerNumber = CustomerNumber;
   }
 
   public String getCity() {
       return city;
   }
 
   public void setCity(String city) {
       this.city = city;
   }
 
   public String getName() {
       return name;
   }
 
   public void setName(String name) {
       this.name = name;
   }
 
   public String getStreetAddress() {
       return streetAddress;
   }
 
   public void setStreetAddress(String streetAddress) {
       this.streetAddress = streetAddress;
   }
}
 
package dao;
 
public abstract class DAOFactory {
   public static final int CLOUDSCAPE = 1;
   public static final int ORACLE = 2;
   public static final int SYBASE = 3;
 
   public abstract CustomerDAO getCustomerDAO();
   //public abstract AccountDAO getAccountDAO();
   //public abstract OrderDAO getOrderDAO();
 
   public static DAOFactory getDAOFactory(int whichFactory) {
   
       switch (whichFactory) {
           case CLOUDSCAPE :
               //return new CloudscapeDAOFactory();
           case ORACLE :
               return new OracleDAOFactory();     
           case SYBASE :
               //return new SybaseDAOFactory();
           default :
               return null;
       }
   }
}
 
package dao;
import java.sql.*;
 
public class OracleDAOFactory extends DAOFactory {
   public static final String DRIVER = " oracle.jdbc.driver.OracleDriver";
   public static final String DBURL= "jdbc:oracle:thin:@localhost:1521:topaz";
   
   public static Connection createConnection() {
       // DataBase Connection생성
       return null;
   }
 
   public CustomerDAO getCustomerDAO() {
       return new OracleCustomerDAO();
   }
   
   /*
   public AccountDAO getAccountDAO() {
       // OracleAccountDAO생성
       return new OracleAccountDAO();
   } 
   public OrderDAO getOrderDAO() {
       // OracleOrderDAO생성
       return new OracleOrderDAO();
   }
   */
}
 
package dao;
import java.util.Collection;
import java.util.HashMap;
 
import javax.sql.RowSet;
 
public interface CustomerDAO {
   public int insertCustomer(Customer customer);
 
   public boolean deleteCustomer(int CustomerNumber);
 
   public Customer findCustomer(int CustomerNumber);
 
   public boolean updateCustomer(Customer customer);
 
   public RowSet selectCustomersRS(HashMap where);
 
   public Collection selectCustomersTO(HashMap where);
}
 
package dao;
 
import java.sql.*;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import javax.sql.RowSet;
 
public class OracleCustomerDAO implements CustomerDAO {
 
   public OracleCustomerDAO() {
       //초기화
   }
   
   /**
    *아래의메소드는실행전에Connection받아와야합니다.
    * Data Source필요한작업을작성합니다.
    */
   
   public int insertCustomer(Customer customer) {
       //이렇게받아와야합니다.
       Connection connection =
           OracleDAOFactory.createConnection();
       return 0;
   }
   
   public boolean deleteCustomer(int CustomerNumber) {
       return false;
   }
   
   public Customer findCustomer(int CustomerNumber) {
       return new Customer();
   
   }
   
   public boolean updateCustomer(Customer customer) {
       return false;
   }
   
   public RowSet selectCustomersRS(HashMap where) {
       return null;   
   }
   
   public Collection selectCustomersTO(HashMap where) {
       return null;   
   }
}
 
package dao;
import java.util.Collection;
import java.util.HashMap;
 
public class Client {
   public static void main(String[] args) {
       Client client = new Client();
       client.exec();
   }
   
   public void exec() {
       DAOFactory oracleFactory =  
           DAOFactory.getDAOFactory(DAOFactory.ORACLE);
       
       CustomerDAO custDAO =
         oracleFactory.getCustomerDAO();
   
       Customer customer = new Customer();
       customer.setCity("서울");
       customer.setName("Lee Yun Chang");
       int newCustNo = custDAO.insertCustomer(customer);
   
       customer = custDAO.findCustomer(newCustNo);
       
       customer.setStreetAddress("서울특별시000000번지");
       customer.setName("후루꾸");
       
       custDAO.updateCustomer(customer);
   
       custDAO.deleteCustomer(newCustNo);
   
       HashMap where = new HashMap();
       where.put("city","서울");
       Collection customersList =
         custDAO.selectCustomersTO(where);
   }
}
 
참고사항
 
트랜잭션 경계설정(demarcation)
DAO는 트랜잭션 객체들이라는 것을 반드시 기억해야 한다. DAO에 의해 수행되는 각각의 작동(데이터 구현,업데이트,삭제)은 트랜잭션과 관련있다.따라서 트랜잭션 경계설정(demarcation)개념은 매우 중요하다.
 
선언적(Declarative)트랜잭션 경계설정
프로그램에 입각한(Programmatic)트랜잭션 경계설정
프로그래머는EJB전개 디스크립터를 사용하여 트랜잭션 애트리뷰트를 선언한다.
프로그래머는 트랜잭션 로직을 코딩해야한다.
런타임 환경(EJB컨테이너)은 트랜잭션을 자동으로 관리하기 위해 이 애트리뷰트를 사용한다.
이 애플리케이션은API를 통해 트랜잭션을 제어한다.
 
DAO를 설계할 때 고려 사항

n        트랜잭션은 어떻게 시작하는가?
n        트랜잭션은 어떻게 끝나는가?
n        트랜잭션 시작을 담당하는 객체는 무엇인가?
n        트랜잭션 종료를 담당하는 객체는 무엇인가?
n        DAO가 트랜잭션의 시작과 종료를 담당해야 하는가?
n        이 애플리케이션이 다중의DAO를 통해 데이터에 액세스해야 하는가?
n        트랜잭션에 포함될DAO의 수는?
n        하나의DAO가 또DAO에 대한 메소드를 호출할 수 있는가?
 
JTA개요
Java Transaction API (JTA)Java Transaction Service (JTS)J2EE플랫폼에 분산 트랜잭션 서비스를 제공한다.분산 트랜잭션에는 트랜잭션 매니저와 한 개 이상의 리소스 매니저가 포함된다.리소스 매니저는 일종의 영속데이터스토어이다.트랜잭션 매니저는 모든 트랜잭션 참여자들 간 통신을 조정하는 역할을 담당한다.JTA트랜잭션은JDBC트랜잭션 보다 강력하다. JDBC트랜잭션이 하나의 데이터베이스 연결로 제한되어 있다면JTA트랜잭션은 다중의 참여자들을 가질 수 있다.다음의 자바 플랫폼 컴포넌트 중 어떤 것이든지JTA트랜잭션에 참여할 수 있다.
 
n        JDBC커넥션
n        JDOPersistenceManager객체
n        JMS
n        JMS토픽
n        EnterpriseJavaBeans
n        J2EE Connector Architecture스팩에 호환하는 리소스 어댑터
 
 
JDBC를 이용한 트랜잭션 경계설정
JDBC트랜잭션은Connection객체를 사용하여 제어된다. JDBC Connection인터페이스(java.sql.Connection)는 두 개의 트랜잭션 모드를 제공한다. (auto-commitmanual commit).java.sql.Connection은 트랜잭션 제어에 다음의 메소드를 제공한다.
 
n        public void setAutoCommit(boolean)
n        public boolean getAutoCommit()
n        public void commit()
n        public void rollback()
 
JDBC트랜잭션 경계설정을 이용하면 여러 개의SQL문장을 하나의 트랜잭션으로 결합할 수 있다. JDBC트랜잭션의 단점 중 하나는 트랜잭션 범위가 하나의 데이터베이스 연결로 제한되어 있다는 점이다. JDBC트랜잭션은 다중 데이터베이스로 확장할 수 없다.
 
JTA를 이용한 트랜잭션 경계설정
JTA로 트랜잭션 경계를 설정하기 위해 애플리케이션은javax.transaction.UserTransaction인터페이스에 대한 메소드를 호출한다.javax.transaction.UserTransaction인터페이스는 다음의 트랜잭션 제어 메소드를 제공한다.
n        public void begin()
n        public void commit()
n        public void rollback()
n        public int getStatus()
n        public void setRollbackOnly()
n        public void setTransactionTimeout(int)
 
JTA트랜잭션을 시작할 때 이 애플리케이션은begin()을 호출한다.트랜잭션을 끝내려면commit()또는rollback()을 호출한다.
 
import javax.transaction.*;
import javax.naming.*;
// ...
InitialContext ctx = new InitialContext();
Object txObj = ctx.lookup("java:comp/UserTransaction");
UserTransaction utx = (UserTransaction) txObj;
utx.begin();
// ...
DataSource ds = obtainXADataSource();
Connection conn = ds.getConnection();
pstmt = conn.prepareStatement("UPDATE MOVIES ...");
pstmt.setString(1, "Spinal Tap");
pstmt.executeUpdate();
// ...
utx.commit();
// ...
 
JTAJDBC사용하기
개발자들은DAO클래스에서 저수준 데이터 작동에JDBC를 사용하곤 한다. JTA를 이용하여 트랜잭션의 경계를 설정하려면javax.sql.XADataSource,javax.sql.XAConnection,javax.sql.XAResource인터페이스를 구현하는JDBC드라이버가 필요하다.이러한 인터페이스를 구현하는 드라이버는JTA트랜잭션에 참여할 수 있게된다.
XADataSource객체는XAConnection객체용 팩토리이다.XAConnectionJTA트랜잭션에 참여하는JDBC커넥션이다.애플리케이션 서버의 관리 툴을 사용하여XADataSource를 설정해야 한다.(애플리케이션 서버 문서와JDBC드라이버 문서 참조하여 설정한다.)
J2EE애플리케이션은JNDI를 사용하여 데이터 소스를 검색한다.일단 애플리케이션이 데이터 소스 객체에 대한 레퍼런스를 갖게 되면 이것은javax.sql.DataSource.getConnection()을 호출하여 데이터베이스로의 커넥션을 획득하게 된다. XA커넥션은 비XA커넥션과는 다르다. XA커넥션은JTA트랜잭션에 참여하고 있다는 것을 언제나 기억하라. XA커넥션은JDBC의 자동 커밋 기능을 지원하지 않는다.또한 이 애플리케이션은XA커넥션 상에서java.sql.Connection.commit()또는java.sql.Connection.rollback()을 호출하지 않는다.대신UserTransaction.begin(),UserTransaction.commit(),UserTransaction.rollback()을 사용한다.
 
최상의 접근방법 선택하기
대부분의 프로젝트에서 대부분은JDBC API를 사용하여DAO클래스를 구현한다.
DAO클래스는 다음과 같이요약된다.
 
n        트랜잭션 경계설정 코드는DAO클래스 안으로 임베딩된다.
n        DAO클래스는 트랜잭션 경계설정에JDBC API를 사용한다.
n        콜러가 트랜잭션 경계를 설정할 방법은 없다.
n        트랜잭션 범위는 하나의JDBC Connection으로 제한된다.
 
JDBC트랜잭션이 복잡한 엔터프라이즈 애플리케이션에 언제나 적합한 것은 아니다.트랜잭션이 다중DAO또는 다중 데이터페이스로 확장한다면 다음과 같은 구현 전략이 보다 적합하다.
 
n        트랜잭션은JTA로 경계 설정된다.
n        트랜잭션 경계설정 코드는DAO와 분리되어 있다.
n        콜러가 트랜잭션 경계설정을 담당하고 있다.
n        DAO는 글로벌 트랜잭션에 참여한다.
 
JDBC접근방법은 간단함이 매력이다. JTA접근방법은 유연성이 무기이다.애플리케이션에 따라 구현 방법을 선택해야 한다.
 
DAO에서의 예외 핸들링
DAO패턴을 구현할 때 다음 사항을 자문해보와야 한다.
 
n        DAO의 퍼블릭 인터페이스의 메소드들이 검사된 예외를 던지는가?
n        그렇다면 어떤 예외들인가?
n        DAO구현 클래스에서 예외는 어떻게 처리되는가?
n        DAO메소드는 의미있는 예외를 던져야한다.
n        DAO메소드는java.lang.Exception을 던져서는 안된다.java.lang.Exception은 너무 일반적이다.근본 문제에 대한 정보를 제공하지 않을 것이다.
n        DAO메소드는java.sql.SQLException메소드를 던져서는 안된다. SQLException은 저급JDBC예외이다. DAOJDBC를 나머지 애플리케이션에 노출하는 것이 아니라JDBC를 캡슐화 해야한다.
n        DAO인터페이스의 메소드들은 콜러가 예외를 처리할 수 있다고 합리적으로 판단될 때 에만 검사된예외를 던져야 한다.콜러가 예외를 핸들할 수 없다면 검사되지 않은(런타임)예외를 던질 것을 고려하라.
n        데이터 액세스 코드가 예외를 잡으면 이를 무시하지 말아라.잡힌 예외를 무시하는DAO는 문제해결에 애를 먹는다.
n        연쇄 예외를 사용하여 저급 예외를 고급 예외로 트랜슬레이팅 한다.
n        표준DAO예외 클래스를 정의하라.

Posted by 영웅기삼
,