ejb

dev 2008. 8. 5. 17:03

출처 : 월간 마이크로 소프트웨어


EJB 아키텍처를 기반으로 웹애플리케이션을 구축하는 프로젝트들이 2000년 이후부터 증가해왔음은 모두가 아는 사실. 더불어 EJB 관련 서적들도 다양하게 출판됐고, EJB를 사용하는 개발자들도 늘어나고 있다.

그러나 실제 EJB를 사용하면서 개발자들이 저지르기 쉬운 실수, 함정 등은 아직 서적에서는 친절히 안내되어 있지 않은 듯 하다. 이번 강좌의 목적은 흔히 개발자들이 스스로 잘 안다고 생각하는 것 때문에 빠질 수 있는 함정들을 미리 짚어봄으로써 그 함정이 초래할 혼란을 최소화하는데 있다.

강좌 중간중간 퍼포먼스를 고려한 팁이 언급될 수도 있고, 클래스 설계와 관련된 첨언도 들어갈 수 있다. 이 강좌의 목적이나 성격이 하나의 개념에 대한 순차적인 설명 형식은 아니므로 기본개념 설명을 건너뛰거나, 동시에 초보 자바 개발자도 알고 있는 사항에 대해 새삼 지적하는 경우도 있을 것이다.

자신에게 닥칠 수도 있는 문제를 미리 파악해 보고, 어떠한 방법으로 예방하고 해결해야하는지 살펴보도록 하자. 이번 강좌에서 언급될 예시에서 쓰는 EJB의 스펙은 1.1버전이고, DBMS는 오라클 8i이다.

DB와의 연결은 누가, 언제, 어떻게 할까?
EJB 애플리케이션이 DB와의 연결자원을 이용하는 방법은 2가지다.

첫째, ConnectionPool 을 이용하는 방법이 있다. EJB 서버가 형성하게 되는 ‘ConnectionPool’이라는 데이터 소스를 이용하는 방법이다. 임의의 비즈니스 프로세스를 담당하는 세션빈의 메소드 내에서 DB의 레코드를 써야할 경우, ConnectionPool에서 java.sql.Connetion 객체를 받아 쓰는 경우이다.

웹로직(weblogic), 웹스피어(websphere), JBOSS 등 다양한 업체들의 EJB 서버마다 기동시 ConnetionPool을 생성시킬 수 있는 기능을 제공한다. 기동시 읽어들이는 프로퍼티 파일에 DB의 URL, SID, user name, 패스워드, ConnectionPool의 name까지 사용자가 지정해야 한다.

아래는 웹로직 서버(버전 5.1)의 weblogic.properties 파일 내의 ConnetionPool 설정정보 부분이다.


# testPool
weblogic.JBDC.connectionPool.testPool=\
url=JBDC:oracle:thin:@123.4.5.67:portNumber:sid명,\
driver=oracle.JBDC.driver.OracleDriver,\
initialCapacity=1,\
maxCapacity=10,\
CapacityIncrement=2,\
allowShrinking=true,\
shrinkPeriodMins=15,\
props=user=user명;password=패스워드


이렇게 EJB 서버 기동시 미리 ConnectionPool을 만들게 되면 애플리케이션 운영중 발생하게 되는 DBMS 와의 빈번한 요청이 있을 때마다 JBDC 드라이버로 DB와 일일이 연결할 필요없이, 만들어진 ConnetionPool에서 Connetion을 꺼내 쓰고 다시 반환하므로 시스템 퍼포먼스 측면에서 월등히 효율적이다.

둘째, 엔터티빈을 활용하는 방법이다. 엔터티빈 중에서도 컨테이너가 퍼시스턴스를 관리해주는 CMP 엔터티빈을 EJB 서버에 배치해 쓰면 개발자 입장에서는 많은 부담을 덜 수 있기 때문에 대부분의 프로젝트에서 BMP 엔터티빈 보다는 CMP 엔터티빈을 사용한다.

엔터티빈 객체 하나는 DB의 하나의 테이블을 대표하며, 엔터티빈 객체가 갖고 있는 필드들은 해당 테이블의 컬럼과 매핑된다. 엔터티빈의 인스턴스는 DB의 임의의 레코드를 객체화한 것이다. 그러므로 엔터티빈의 인스턴스를 호출하여, get, create, set, remove 등의 메소드를 쓴다면, 직접 SQL문을 DB로 날리지 않아도 동일한 결과를 얻을 수 있다. insert 문 대신 create 메소드를 쓰면 되므로 코딩이 간결해진다. 더불어 SQL문에 익숙하지 않은 개발자라도 쉽게 쓸 수 있다는 장점이 있다.

그러나 엔터티빈의 이러한, 소위 장점이라고 하는 것들에 섣불리 현혹되지 않는 것이 좋다. EJB 프로젝트에서 발생하는 문제의 원인은 대부분 엔터티빈에 있다.

DB 핸들링 주체는 세션빈
DB에 어떻게 접근하든 비즈니스 프로세스를 담당하는 주체는 세션빈이다. 그리고 분석된 업무 흐름을 애플리케이션으로 구현할 때의 최소단위는 세션빈의 메소드이다.

예를 들어 급여계산 프로그램이 있다면 세션빈 내의 calcuratorPay(String empNo, String yyyyMm)라는 메소드가 있을 것이고, 복리후생비 신청 프로그램은 submitWelfare(String empNo, String date, ...)라는 메소드를 콜한다.

개발자는 세션빈의 메소드 안에서 적절히 저 두가지 방법을 활용해 DB의 레코드를 핸들링하게 된다. 엔터티빈 인스턴스가 DB의 레코드를 표현한다고 해도, 엄청나게 다양한 조건 및 함수 등이 담겨있는 select 문의 효과를 엔터티빈 인스턴스를 사용해서 자바 코딩으로 얻을 수 있겠는가? 없다.

따라서 그러한 다양한 조회를 할 필요가 있는 경우에는 ConnectionPool 에서 connection 객체를 하나 얻어와서 쓰게 된다. 뿐만 아니라 update, delete, insert 등에서도 pk 값으로만 조회해서 그 값을 변경, 삭제, 추가하는 경우보다 추가적인 제약이 따르는 경우가 많다.

결국 전체 시스템의 모든 세션빈이 connetion 객체를 쓰는 경우가 훨씬 많기 때문에, 모든 세션빈이 콜 할 수 있는 공통 모듈로 connectionPool 에서 connection 객체를 하나 뽑아 리턴해주는 클래스의 개발 필요사항이 발생한다. 이 클래스에는 getConnetion() 및 closeConnection() 메소드가 위치하게 될 것이다.

아래 예시에서 등장하게 되는 ConnManager라는 클래스는 이러한 역할을 하는 클래스다.

예시 소스코드를 보자. 사번을 통해 사원명을 조회하는 매우 간단한 메소드이다.


public 클래스 EmpManagerEJB implements javax.ejb.세션빈{

    public EmpManagerEJB()
    {
    }

    public String getEmpName(String empNo) throws Exception
    {
      Connection con = null;
      String eName = null;

      try {
         con = ConnManager.getConnection();
         EmpDB DB = new EmpDB();
         eName = DB.getEmpName(con,empNo);

      } catch(Exception e) {
         throw new Exception(e.toString());
      } finally {
         try {
            ConnManager.closeConnection(con);
         } catch(Exception e) {
            throw new Exception(e);
         }
      }
      return eName;
    }

    // ---------------------------------------------------------------
    // 세션빈 interface implementation
    public void ejbActivate()
    {
      // to do: code goes here.

    }
.... 이하 생략,

}

위의 예시에서 보듯이 세션빈 메소드 내에서는 Connection 객체를 가지고 와서 마지막에 닫고, 자신을 호출한 클라이언트에게 사원명을 전달하는 일만 하고 있다. 직접적으로 DB에 갖다오는 일은 EmpDB라는 클래스에게 맡긴다. 다양한 업무를 반영하는 세션빈 메소드 내에서는 이와 같은 패턴이 보다 효과적이고 간결하다.

그렇다면 EmpDB라는 클래스의 getEmpName 메소드를 살펴보자.


    public String getEmpName(Connetion con , String empNo)
    throws Exception {

      String eName = null;
      PreparedStatement pstmt = null;
      ResultSet rs = null;

      /*
         SELECT
            EMP_NAME
         FROM EMP
         WHERE EMP_NO = '000001'
      */

      String sql =
         "SELECT " +
         " EMP_NAME " +
         "FROM EMP " +
         "WHERE EMP_NO = ? ";

      try {
         pstmt = con.prepareStatement(sql);
         pstmt.setString(1, empNo);
         rs = pstmt.executeQuery();

         if(rs.next()) {

            eName = rs.getString("EMP_NAME"));

         }
      } finally {
         ConnManager.closeResultSet(rs);
         ConnManager.closeStatement(pstmt);
      }
      return eName;
    }

사번 테이블에서 사원 번호로 사원명을 리턴하는 메소드이다. 이 메소드 중에서 가장 주의깊게 봐야 할 부분은 PreparedStatement를 Statement interface 대신 사용한 부분이다. 조회성 쿼리를 DB에 던지고자 할 경우 아무 생각없이 Statement interface를 사용하는 개발자가 많은데, 이렇게 만들어서는 안된다.
① 세션빈에서의 DB 접근전략 및 엔터티빈 사용시 주의사항
② 세션빈에서의 트랜잭션 관리
③ 비즈니스 프로세스 구현 최적화하기
④ 능률 높여주는 유틸리티를 사용하자

EJB 아키텍처를 기반으로 웹애플리케이션을 구축하는 프로젝트들이 2000년 이후부터 증가해왔음은 모두가 아는 사실. 더불어 EJB 관련 서적들도 다양하게 출판됐고, EJB를 사용하는 개발자들도 늘어나고 있다.

그러나 실제 EJB를 사용하면서 개발자들이 저지르기 쉬운 실수, 함정 등은 아직 서적에서는 친절히 안내되어 있지 않은 듯 하다. 이번 강좌의 목적은 흔히 개발자들이 스스로 잘 안다고 생각하는 것 때문에 빠질 수 있는 함정들을 미리 짚어봄으로써 그 함정이 초래할 혼란을 최소화하는데 있다.

강좌 중간중간 퍼포먼스를 고려한 팁이 언급될 수도 있고, 클래스 설계와 관련된 첨언도 들어갈 수 있다. 이 강좌의 목적이나 성격이 하나의 개념에 대한 순차적인 설명 형식은 아니므로 기본개념 설명을 건너뛰거나, 동시에 초보 자바 개발자도 알고 있는 사항에 대해 새삼 지적하는 경우도 있을 것이다.

자신에게 닥칠 수도 있는 문제를 미리 파악해 보고, 어떠한 방법으로 예방하고 해결해야하는지 살펴보도록 하자. 이번 강좌에서 언급될 예시에서 쓰는 EJB의 스펙은 1.1버전이고, DBMS는 오라클 8i이다.

DB와의 연결은 누가, 언제, 어떻게 할까?
EJB 애플리케이션이 DB와의 연결자원을 이용하는 방법은 2가지다.

첫째, ConnectionPool 을 이용하는 방법이 있다. EJB 서버가 형성하게 되는 ‘ConnectionPool’이라는 데이터 소스를 이용하는 방법이다. 임의의 비즈니스 프로세스를 담당하는 세션빈의 메소드 내에서 DB의 레코드를 써야할 경우, ConnectionPool에서 java.sql.Connetion 객체를 받아 쓰는 경우이다.

웹로직(weblogic), 웹스피어(websphere), JBOSS 등 다양한 업체들의 EJB 서버마다 기동시 ConnetionPool을 생성시킬 수 있는 기능을 제공한다. 기동시 읽어들이는 프로퍼티 파일에 DB의 URL, SID, user name, 패스워드, ConnectionPool의 name까지 사용자가 지정해야 한다.

아래는 웹로직 서버(버전 5.1)의 weblogic.properties 파일 내의 ConnetionPool 설정정보 부분이다.


# testPool
weblogic.JBDC.connectionPool.testPool=\
url=JBDC:oracle:thin:@123.4.5.67:portNumber:sid명,\
driver=oracle.JBDC.driver.OracleDriver,\
initialCapacity=1,\
maxCapacity=10,\
CapacityIncrement=2,\
allowShrinking=true,\
shrinkPeriodMins=15,\
props=user=user명;password=패스워드


이렇게 EJB 서버 기동시 미리 ConnectionPool을 만들게 되면 애플리케이션 운영중 발생하게 되는 DBMS 와의 빈번한 요청이 있을 때마다 JBDC 드라이버로 DB와 일일이 연결할 필요없이, 만들어진 ConnetionPool에서 Connetion을 꺼내 쓰고 다시 반환하므로 시스템 퍼포먼스 측면에서 월등히 효율적이다.

둘째, 엔터티빈을 활용하는 방법이다. 엔터티빈 중에서도 컨테이너가 퍼시스턴스를 관리해주는 CMP 엔터티빈을 EJB 서버에 배치해 쓰면 개발자 입장에서는 많은 부담을 덜 수 있기 때문에 대부분의 프로젝트에서 BMP 엔터티빈 보다는 CMP 엔터티빈을 사용한다.

엔터티빈 객체 하나는 DB의 하나의 테이블을 대표하며, 엔터티빈 객체가 갖고 있는 필드들은 해당 테이블의 컬럼과 매핑된다. 엔터티빈의 인스턴스는 DB의 임의의 레코드를 객체화한 것이다. 그러므로 엔터티빈의 인스턴스를 호출하여, get, create, set, remove 등의 메소드를 쓴다면, 직접 SQL문을 DB로 날리지 않아도 동일한 결과를 얻을 수 있다. insert 문 대신 create 메소드를 쓰면 되므로 코딩이 간결해진다. 더불어 SQL문에 익숙하지 않은 개발자라도 쉽게 쓸 수 있다는 장점이 있다.

그러나 엔터티빈의 이러한, 소위 장점이라고 하는 것들에 섣불리 현혹되지 않는 것이 좋다. EJB 프로젝트에서 발생하는 문제의 원인은 대부분 엔터티빈에 있다.

DB 핸들링 주체는 세션빈
DB에 어떻게 접근하든 비즈니스 프로세스를 담당하는 주체는 세션빈이다. 그리고 분석된 업무 흐름을 애플리케이션으로 구현할 때의 최소단위는 세션빈의 메소드이다.

예를 들어 급여계산 프로그램이 있다면 세션빈 내의 calcuratorPay(String empNo, String yyyyMm)라는 메소드가 있을 것이고, 복리후생비 신청 프로그램은 submitWelfare(String empNo, String date, ...)라는 메소드를 콜한다.

개발자는 세션빈의 메소드 안에서 적절히 저 두가지 방법을 활용해 DB의 레코드를 핸들링하게 된다. 엔터티빈 인스턴스가 DB의 레코드를 표현한다고 해도, 엄청나게 다양한 조건 및 함수 등이 담겨있는 select 문의 효과를 엔터티빈 인스턴스를 사용해서 자바 코딩으로 얻을 수 있겠는가? 없다.

따라서 그러한 다양한 조회를 할 필요가 있는 경우에는 ConnectionPool 에서 connection 객체를 하나 얻어와서 쓰게 된다. 뿐만 아니라 update, delete, insert 등에서도 pk 값으로만 조회해서 그 값을 변경, 삭제, 추가하는 경우보다 추가적인 제약이 따르는 경우가 많다.

결국 전체 시스템의 모든 세션빈이 connetion 객체를 쓰는 경우가 훨씬 많기 때문에, 모든 세션빈이 콜 할 수 있는 공통 모듈로 connectionPool 에서 connection 객체를 하나 뽑아 리턴해주는 클래스의 개발 필요사항이 발생한다. 이 클래스에는 getConnetion() 및 closeConnection() 메소드가 위치하게 될 것이다.

아래 예시에서 등장하게 되는 ConnManager라는 클래스는 이러한 역할을 하는 클래스다.

예시 소스코드를 보자. 사번을 통해 사원명을 조회하는 매우 간단한 메소드이다.


public 클래스 EmpManagerEJB implements javax.ejb.세션빈{

    public EmpManagerEJB()
    {
    }

    public String getEmpName(String empNo) throws Exception
    {
      Connection con = null;
      String eName = null;

      try {
         con = ConnManager.getConnection();
         EmpDB DB = new EmpDB();
         eName = DB.getEmpName(con,empNo);

      } catch(Exception e) {
         throw new Exception(e.toString());
      } finally {
         try {
            ConnManager.closeConnection(con);
         } catch(Exception e) {
            throw new Exception(e);
         }
      }
      return eName;
    }

    // ---------------------------------------------------------------
    // 세션빈 interface implementation
    public void ejbActivate()
    {
      // to do: code goes here.

    }
.... 이하 생략,

}

위의 예시에서 보듯이 세션빈 메소드 내에서는 Connection 객체를 가지고 와서 마지막에 닫고, 자신을 호출한 클라이언트에게 사원명을 전달하는 일만 하고 있다. 직접적으로 DB에 갖다오는 일은 EmpDB라는 클래스에게 맡긴다. 다양한 업무를 반영하는 세션빈 메소드 내에서는 이와 같은 패턴이 보다 효과적이고 간결하다.

그렇다면 EmpDB라는 클래스의 getEmpName 메소드를 살펴보자.


    public String getEmpName(Connetion con , String empNo)
    throws Exception {

      String eName = null;
      PreparedStatement pstmt = null;
      ResultSet rs = null;

      /*
         SELECT
            EMP_NAME
         FROM EMP
         WHERE EMP_NO = '000001'
      */

      String sql =
         "SELECT " +
         " EMP_NAME " +
         "FROM EMP " +
         "WHERE EMP_NO = ? ";

      try {
         pstmt = con.prepareStatement(sql);
         pstmt.setString(1, empNo);
         rs = pstmt.executeQuery();

         if(rs.next()) {

            eName = rs.getString("EMP_NAME"));

         }
      } finally {
         ConnManager.closeResultSet(rs);
         ConnManager.closeStatement(pstmt);
      }
      return eName;
    }

사번 테이블에서 사원 번호로 사원명을 리턴하는 메소드이다. 이 메소드 중에서 가장 주의깊게 봐야 할 부분은 PreparedStatement를 Statement interface 대신 사용한 부분이다. 조회성 쿼리를 DB에 던지고자 할 경우 아무 생각없이 Statement interface를 사용하는 개발자가 많은데, 이렇게 만들어서는 안된다.


꼭 PreparedStatement를 사용하자
Statement 인터페이스 보다는 PreparedStatement가 낫다. 사실 ‘좋다’ 정도가 아니라 ‘Statement를 쓰지 말고 PreparedStatement를 써야한다’고 말해도 될 정도다.

getEmpName의 메소드에서 PreparedStatement 대신 Statement의 executeQuery(String sql) 메소드를 사용해서 개발하면 어떤 일이 생기는지 알아보자.

인사업무 담당자가 사원 한 명씩 상세정보를 조회하고자 한다고 가정하자. 조회버튼을 누르면 사원번호로 getEmpName 메소드안에서 쿼리가 스트링으로 완성돼 Statement의 executeQuery(String sql) 메소드안의 SQL 값으로 들어간다. DB의 옵티마이저가 쿼리를 파싱, 실행하고, resultSet 리턴한다.

여기서 다음 사원을 조회하면? 사원번호만 다른, 그러나 방금전 조회시의 쿼리와 모두 같은 스트링이 던져진다. DB의 옵티마이저가 쿼리를 파싱, 실행한다.

계속 다른 사원을 조회하면? DB 입장에서는 조회조건만 다른 쿼리에 대해 파싱을 반복적으로 하고 있는 셈이고, 매번 파싱을 하고 결과값을 리턴해야 하기에 조회시 결과값 출력도 그만큼 늦어진다.

만일 이 시스템 전체가 Statement 인터페이스로 이뤄져 있고, 향후 시스템 성능 테스트에서도 이 부분이 발견되지 않는다면 어느날 갑자기 원인도 모르게 CPU 사용도가 올라가고 시스템 전체가 느려지는 현상이 발생하게 된다. 서버 관리자가 서버를 내렸다 올리겠지만 원인을 알 수 없으니 속이 편하진 않을 터.

나중에 다시 스트레스 테스트를 하고, 결국 파싱과 실행 수가 비슷하다는 사실이 발견된다. 책임은 개발자들에게 돌아간다.

Statement로 이뤄진 프로그램을 PreparedStatement로 바꾸라는 지시가 떨어지겠지만 이는 간단치 않은 일이다. Statement로 되어있는 프로그램을 그동안 copy & paste 해서 만든 프로그램이 몇 개인지 파악도 안되고, 파악이 되다 해도 바꾸는 작업은 쉽지 않다.

PreparedStatement는 주어진 쿼리를 미리 파싱을 해두기 때문에, 조회조건만 달라진 다른 쿼리가 들어오면 바로 실행만 시키게 된다. 시스템 성능 면에서 훨씬 더 효율적이다.

엔터티빈으로 접근한 row는 또 다른 Connetion 객체로 접근하지 말 것
세션빈에서 한 레코드에 대해 변경을 하는 작업을 엔터티빈으로 한다고 하고, 이때 요청사항으로 추가되는 어떤 컬럼에 대해 변경하는 부분이 늘어난다고 하자. 기존 소스는 아래와 같다. 아래 소스는 세션빈 내에서 사원발령이 났을 경우 발령 시작일자와 발령 마감일자를 변경하는 비즈니스 프로세스를 갖고 있다.


  public boolean setMoveEmp(String empNo,String startDt, String endDt ) throws Exception
  {

    boolean doneYn = false;
    EmpHome empHome = null;
    Emp emp = null;

    try {
      EmpPk pk = new EmpPk();

      /* HomeFactory : JNDI 이름으로 Entitybean의 HOME 객체를 리턴한다 */
      empHome = (EmpHome)HomeFactory.getHome(EmpHome.클래스);

      pk.empNo = empNo;
      emp = empHome.finDByPrimaryKey(pk);
      emp.setStartDt( startDt );
      emp.setEndDt( endDt );

    } catch(Exception e) {
      throw new Exception(e.toString());
    } finally {
          ConnManager.closeConnection(con);
      } catch(Exception e) {
          throw new Exception(e);
      }
    }
    return doneYn;
  }


여기에서 사원발령정보를 변경하기 위해 엔터티빈 리모트 인터페이스의 setStartDt, setEndDt 메소드를 사용했다. 여기서 만일 사원발령정보를 낼 때 변경 일자도 기록하라는 요청사항이 추가될 경우, 이를 기존 소스에 추가하는 작업에서 엔터티빈을 사용하지 않고 Connection 객체를 만들어서 직접 SQL문을 던지는 형식으로 레코드의 한 컬럼 값을 변경시킨다고 가정해보자.


  public boolean setMoveEmp(String empNo,String startDt, String endDt ) throws Exception
  {

    /* Connection 선언 추가한다. */
    Connection con = null;
    boolean doneYn = false;
    EmpHome empHome = null;
    Emp emp = null;

    try {

      /* Connection instance 받아온다. */
      con = ConnManager.getConnection();
      EmpDB DB = new EmpDB(); /* DB 래퍼 선언 */



      EmpPk pk = new EmpPk();

      /* HomeFactory : JNDI 이름으로 Entitybean의 HOME 객체를 리턴한다 */
      empHome = (EmpHome)HomeFactory.getHome(EmpHome.클래스);

      pk.empNo = empNo;
      emp = empHome.finDByPrimaryKey(pk);

      emp.setStartDt( startDt );


      /* 변경 일자를 기록하는 부분이 추가된다 */
      DB.setUpdateDate(con,empNo);


      emp.setEndDt( endDt );



    } catch(Exception e) {
      throw new Exception(e.toString());
    } finally {
      try {
        ConnManager.closeConnection(con);
      } catch(Exception e) {
        throw new Exception(e);
      }
    }
    return doneYn;
  }


위의 소스를 보면 일견 아무런 문제가 없어 보인다. 컴파일도 잘 되고 실행시에 아무런 에러도 나지 않는다. 하지만 결과를 보면,

기존

emp.setStartDt( startDt );
emp.setEndDt( endDt );


를 타서, 레코드에 startDt와 endDt 컬럼 부분은 모두 원하는 데로 변경이 되었으나,


/* 변경 일자를 기록하는 부분이 추가된다 */
DB.setUpdateDate(con,empNo);


부분이 실행이 되었음에도 변경 일자 컬럼에는 아무런 변화가 없음을 알 수 있다.

엔터티빈의 인스턴스를 가지고 온 후부터 DB 레코드와 엔터티빈간의 동기화가 형성되고, 세션빈 메소드가 끝났을 때 DB에 결과를 쓰게 된다. 즉, 최종적으로 기록이 남는 것은 엔터티빈으로 작업한 결과이다. 중간에 새로 생긴 업데이트 세션은 그 작업은 성공한다 해도 DB에는 전혀 반영이 되지 않는 것이다.

이 소스를 보고 ‘그렇다면 모두 엔터티빈 메소드로 실행시키면 되지 왜 굳이 Connection 객체로 접근하는가'라는 의문이 들 수도 있다. 하지만 시스템을 오픈하고 나서 테이블에 컬럼을 추가해야 하는 경우가 업무적으로 반드시 생기게 된다. 그때 추가된 컬럼에 대해서는 엔터티빈안에 추가된 컬럼의 필드와 상응하는 변수를 추가하고 엔터티빈을 서버에 다시 배치해야 한다. 하지만 이 작업이 번거로우므로 추가된 컬럼에 한해서 SQL 쿼리로 간단히 핸들링하고자 하는 계획을 세우게 된다. 그럴때 위와 같은 경우가 생기게 되고, 한 row에 대해서 엔터티빈을 콜 할 때 엔터티빈 인스턴스와 DB가 동기화 된다는 것을 염두에 두지 못한 채 Connetion 객체를 써서 업데이트하는 하는 일도 생기는 것이다.

위의 소스는 모두 Connection 객체를 써서 해당 row의 컬럼을 변경 시키든지, 아니면 모두 엔터티빈 메소드를 쓰도록 변경해야 한다.

엔터티빈 사용을 지양하자
주위에서 진행되는 프로젝트들을 살펴보면 엔터티빈을 몰라서 쓰지않는 것이 아니라, 엔터티빈의 성질을 잘 알기 때문에 안쓰는 경우를 종종 보게된다.

엔터티빈의 이점이 몇가지 있다는 것은 분명하지만 개발자 입장에서는 쿼리로 간단히 대처를 할 수 있다는 측면에서 엔터티빈을 기피하게 된다.

한편 엔터티빈 말고 Connection 객체로 DB를 핸들링한다고 해서 트랜잭션이 언제나 완결되는가 하면 그것은 또 아니다. 분명 엔터티빈보다는 훨씬 더 강력하지만 예외도 있다. 다음 이어지는 강좌에서는 그에 해당하는 예제를 들고, 이를 피하려면 어떤 전략을 취해야 하는지 소개하도록 하겠다. @

'dev' 카테고리의 다른 글

locatin 속성  (0) 2008.09.12
EUC-kr, Unicode, UTF-8  (1) 2008.08.20
jms  (0) 2008.08.04
expression pattern  (0) 2008.08.01
script class  (0) 2008.08.01
Posted by 으랏차
,