Java Persistence와 함께 Generics 사용하기
저자 Sean Brydon 및 Inderjeet Singh  

Java Persistence API(또 는 간단히 Java Persistence)는 Java EE 5 애플리케이션에 대한 POJO 기반 도메인 모델을 제공한다. 이것은 관계형 데이터가 Java 개체에 매핑되는 방식에 대한 모든 세부사항을 다루며 개체 관계 매핑을 표준화한다. Java Persistence를 사용하는 애플리케이션은 개체 컬렉션을 반환하는 쿼리를 실행하는 경우가 많다. Java Platform, Standard Edition (Java SE) 5에서는 컬렉션에서 개체 유형을 지정할 수 있게 하는 새로운 기능인 generics가 소개되었다. 애플리케이션에서 Java Persistence를 사용하는 경우 generics를 이용하면 스펠링 체크 등의 추가 기능을 통해 코드를 작성할 수 있다. 이 팁에서는 generics를 사용하도록 애플리케이션을 리팩터링하는 방법을 알아보겠다.

샘플 웹 애플리케이션 패키지가 이 팁에 제공된다. 이 팁의 코드 예제는 해당 패키지에 포함된 샘플의 소스 코드에서 따온 것이다.

Generics의 기본 원리

generics는 여러 기능을 가지고 있지만 무엇보다도 컬렉션에서 개체 유형을 전달하는 방법을 제공한다. generics를 사용하면 클래스를 나타내는 매개변수를 통해 컬렉션을 지정할 수 있다. 예를 들어, 다음은 getAllItems 메소드가 Item 유형의 개체 컬렉션을 반환한다고 선언한다.

   public List<Item> getAllItems(){...}

generics를 사용하면 다음과 같은 장점이 있다.

  • 컴파일러가 스펠링 체크를 수행하고 코드에서 잠재적 버그를 잡아낼 수 있다. generics를 사용하지 않는다면 애플리케이션 실행 전까지 해당 버그를 찾아낼 수 없다.
  • 배포 전에 버그를 식별할 수 있다.
  • 코드의 가독성을 높일 수 있다. 다른 개발자가 내 코드를 읽을 때 컬렉션에서 어떤 개체 유형이 기대되는지 알 수 있다.
  • generics가 없으면 클라이언트 코드는 컬렉션에서 얻는 각 개체를 타입캐스트해야 한다.

Persistence 예제

Java Persistence와 함께 generics를 사용하는 방법을 알아보기 위해 간단한 Java Persistence 예제를 살펴보자. 이 예제에서는 JSP 페이지가 데이터베이스의 모든 항목에 대한 검색 요청을 하고 XML 문서에 이를 표시한다.

요청은 다음과 같이 처리된다.

Java Persistence 예제
  • 기본 JSP 페이지(index.jsp)가 서블릿에 요청을 보낸다.
  • 서블릿이 해당 요청을 파사드에 보낸다.
  • 파사드가 Java Persistence Query Language를 사용하여 데이터베이스의 모든 항목을 검색하는 쿼리를 생성한다. 파사드는 서블릿 또는 세션 빈 등의 웹 구성요소가 될 수 있다.
  • Java Persistence Runtime이 쿼리를 실행하고, 개체 관계 매핑을 수행하고, Items 목록을 작성하고, 기타 일부 작업을 수행한다.
  • 데이터베이스가 SQL 쿼리를 실행하고 결과 목록을 반환한다. 각 결과는 데이터베이스의 각 항목(즉, 각 행)을 의미한다.
  • 파사드가 List를 서블릿에 전달한다.
  • 서블릿이 List를 JSP 페이지(itemsxml.jsp)에 반환하고 이 페이지에서 모든 항목을 XML로 표시한다.

파사드를 먼저 살펴보자. 다음은 파사드에서 요청에 대한 코드의 모양을 보여준다.

   public List getAllItemsInPlainCollection(){
      EntityManager em = emf.createEntityManager();
      Query query = em.createQuery(
             "SELECT OBJECT(i) FROM Item i");
      List items = query.getResultList();
      em.close();
      return items;
   }

코드를 "generics화"하려면 다음과 같이 두 가지만 추가하면 된다.

   public List<Item> getAllItemsInTypedCollection(){
      EntityManager em = emf.createEntityManager();
      Query query = em.createQuery(
             "SELECT OBJECT(i) FROM Item i");
      List<Itemgt; items = query.getResultList();
      em.close();
      return items;
   }

변경된 코드는 반환 값에 Item 유형의 개체를 포함한다고 지정한다.

이제 서블릿에서 관련 코드를 살펴보자.

   public void doGet(HttpServletRequest request,
          HttpServletResponse response)
       ...
       if (selectedURL.equals("findallitems.do")) {
           List items = cf.getAllItems();
           request.setAttribute("items", items);
    }

이제 itemsxml.jsp 페이지는 다음과 같다.

   ...
        <Items>
           <% List items = (List)request.getAttribute("items");
           for (Object o : items) {
           Item item = (Item) o; %>  
           <Item>
               <Name><%=item.getName()%> </Name>
               <ListPrice>$<%=item.getListPrice()%> </ListPrice>
               <Description><%=item.getDescription()%>
               </Description>              
           </Item>            
           <% } %>
        </Items>
   ...  

항목 목록의 각 항목이 적절한 유형으로 어떻게 변환되어야 하는지에 유의한다. generics를 사용하면 다음 코드에서 보는 바와 같이 유형 변환의 필요가 없다.

   ...  
       <Items>
           <% List<Item> items = (List<Item>)
                   request.getAttribute("items");
           for (Item item : items) { %>
           <Item>
               <Name><%= item.getName() %></Name>
               <ListPrice>$<%=item.getListPrice()%></ListPrice>
               <Description><%=item.getDescription()%>
               </Description>
           </Item>
           <% } %>
        </Items>
     ...

마지막으로 다음은 간단한 persistence 개체인 Item 클래스에 대한 코드의 일부이다. 앞에서 언급한 바와 같이, 이 예제의 쿼리는 이러한 Item 개체의 목록을 반환하고 각 개체는 데이터베이스의 각 행을 가리킨다.

   import javax.persistence.*;
   @Entity      
   public class Item implements java.io.Serializable {   
     
       private int itemID;
       private String name;
       ...other fields listed here
     
       public Item() { }

     
       @Id
       public int getItemID() {
           return itemID;
       }  
       public String getName() {
           return name;
       }
     
       public void setItemID(int itemID) {
           this.itemID = itemID;
       }
       public void setName(String name) {
           this.name = name;
       }
       //...other getters and setters methods
   }

Generics 컬렉션은 다형적이 아님

generics를 사용할 경우 generics 컬렉션은 다형적이 아님을 알아야 한다. generics를 처음 사용하는 경우, List<Item> 유형의 개체를 List<Object> 유형의 개체 참조에 지정할 수 있다고 생각하기가 쉽다. 직관적인 생각과는 약간 배치되지만 이 가정은 성립되지 않는다. 예를 들어, 다음 코드는 컴파일할 때 오류가 발생한다.

   public List<Object> getAllItems() {
     // .... initialize the query object appropriately
     List<Item> results = query.getResultList();
     // the following return value will generate
     // a compile-time error
     return query;
   }

컬렉션의 유형 무결성을 보호하기 위해 이 코드는 컴파일할 때 오류가 발생한다. 컬렉션은 Item 개체의 목록이어야만 한다는 것에 유념한다. 위의 코드가 유효하다면 List<Object>의 add 메소드를 통해 Address 개체 등의 기타 개체 유형을 컬렉션에 추가할 수 있을 것이다.

동일한 의도를 합법적으로 표현할 수 있는 한 가지 대안은 generics를 와일드카드와 함께 사용하는 것이다. 예제 코드는 다음과 같다.

   public List<?> getAllItemsasObjects() {
     // .... initialize the query object appropriately
     List<Item> results = query.getResultList();
     return query;
   }  

여기서 반환 값은 임의 개체의 컬렉션이 아님에 유의한다. 이 값은 알 수 없는 개체의 컬렉션으로서 해당 유형에 대한 가정이 불가능하다. 사실, 특정 유형 결과가 필요한 Collections의 모든 메소드(예: add 메소드)는 컴파일 타임 오류가 발생한다. 사용할 수 있는 유일한 메소드는 유형을 java.lang.Object로 가정하는 메소드이다.

불필요한 경고 방지하기

기 억해 두어야 할 또 한 가지 사항은 Java Persistence API의 현재 버전(1.0)에서는 generics를 이용하여 스펠링 체크하는 기능을 보장할 수 없다는 것이다. 사용자 코드에서 generics를 사용한다면 불필요한 경고가 발생할 수 있다. 다음 코드는 해당 문제점을 보여준다.

   public List<Item> getAllItemsInTypedCollection(){
     EntityManager em = emf.createEntityManager();
     Query query = em.createQuery(
            "SELECT OBJECT(i) FROM Item i");
     List<Item> items = query.getResultList();
     em.close();
     return items;
   }

-Xlint:unchecked 옵션을 사용하여 코드를 컴파일하면 다음과 같은 경고가 발생한다.

   warning: [unchecked] unchecked conversion
   found : java.util.List
   required:
     java.util.List<com.sun.javaee.blueprints.autoid.model.Item>
   List<Item> items = query.getResultList();

-Xlint:unchecked 옵션에 익숙하지 않은 경우 이 옵션은 컴파일러에게 Java 언어 규격에서 지정한 체크되지 않은 변환 경고에 대한 세부 정보를 제공하도록 요구한다.

query.getResultList()는 비generic 버전의 List를 반환하므로 경고가 발생한다. 하지만 items 변수는 List<Item> 유형이다. 실제 쿼리 결과는 Item 개체의 List이므로 이 경고는 불필요하다. 컴파일러는 런타임에 대한 generics 유형 정보를 보존하지 않으므로 List<Item>의 타입캐스트를 사용해도 이 문제는 해결되지 않는다. Java Persistence API의 이후 버전에서는 generics를 더 잘 지원하도록 javax.persistence.Query 클래스가 변경될 것으로 보인다. 그 때까지는 불필요한 경고가 발생하지 않도록 하는 최선의 방법은 다음 코드 예제에 표시된 대로 @SuppressWarnings 주석을 사용하는 것이다.

    @SuppressWarnings("unchecked")  
    public List<Item> getAllItemsInTypedCollection(){
      EntityManager em = emf.createEntityManager();
      Query query = em.createQuery(
           "SELECT OBJECT(i) FROM Item i");
      List<Item> items = query.getResultList();
      em.close();
      return items;
    }

자세한 정보

generics 및 Java Persistence API에 대한 자세한 내용은 다음을 참조하십시오.

샘플 코드 실행하기

이 팁에 대한 샘플 코드는 NetBeans 프로젝트로서 사용 가능하다. NetBeans IDE를 사용하여 샘플 코드를 빌드하고 실행할 수 있다. 명령줄로부터 샘플 코드를 빌드하고 실행할 수도 있다. 명령줄로부터 샘플 코드를 빌드하고 실행하는 방법은 이 팁에 대한 샘플 패키지에서 README 파일에 나와 있다.

다음과 같이 NetBeans IDE를 사용하여 샘플 코드를 빌드하고 실행할 수 있다.

  1. NetBeans IDE가 없으면 NetBeans IDE를 다운로드하여 설치한다. NetBeans IDE 5.5(NetBeans Enterprise Pack 5.5 포함)는 Java EE 5 SDK Update 2에서 사용 가능하며 Java EE Downloads 페이지에서 다운로드할 수 있다.

  2. 팁에 대한 샘플 패키지를 다운로드하고 해당 컨텐츠의 압축을 푼다. 이제 압축이 풀린 디렉토리가 <sample_install_dir>/bp-persistence-webonly와 같이 나타난다. 여기서 <sample_install_dir>는 샘플 패키지를 설치한 디렉토리이다. 예를 들어, Windows 시스템의 C:\ 경로에 해당 컨텐츠의 압축을 풀었다면 새로 생성된 디렉토리는 C:\bp-persistence-webonly이 되어야 한다.

  3. NetBeans IDE를 시작한다.

  4. bp-persistence-webonly 프로젝트를 연다. javadb.root에 대한 누락된 참조를 해결하라는 메시지가 나타난다. 메시지 창을 닫는다. 누락된 참조를 다음과 같이 해결한다.

    • Projects 창에서 bp-persistence-webonly 노드를 마우스 오른쪽 버튼으로 클릭한다.
    • "Resolve Reference Problems ..."를 선택한다.
    • Resolve ... 버튼을 클릭한다.
    • Sun Application Server의 설치 디렉토리 아래에 있는 javadb 디렉토리로 이동한다.
    • Close 버튼을 클릭한다.

  5. 다음과 같이 Java DB 데이터베이스를 시작한다.

    • Tools 메뉴에서 "Java DB Database"를 선택한다.
    • "Start Java DB Server"를 선택한다.

  6. 다음과 같이 프로젝트를 빌드한다.

    • Projects 창에서 bp-persistence-webonly 노드를 마우스 오른쪽 버튼으로 클릭한다.
    • "Clean and Build Project"를 선택한다.

  7. 다음과 같이 프로젝트를 실행한다.

    • Projects 창에서 bp-persistence-webonly 노드를 마우스 오른쪽 버튼으로 클릭한다.
    • "Run Project"를 선택한다.

URL(http://localhost:8080/bp-persistence-webonly/)에서 프로젝트를 실행하면 브라우저에 Java Persistence Sample Application의 시작 페이지가 표시되어야 한다.

Java Persistence Sample Application

저자 정보

Sean Brydon은 Sun Microsystems에서 Java BluePrints 프로그램에 대한 기술 팀장을 맡고 있는 엔지니어입니다. 그는 Java BluePrints의 시초부터 관련 업무를 맡아 왔습니다. 그는 Addison-Wesley Java 시리즈 도서인 "Designing Enterprise Applications with the Java 2 Platform, Enterprise Edition" 및 "Designing Web Services with the J2EE 1.4 Platform"의 저자입니다. 그는 엔터프라이즈 애플리케이션 설계에 대해 정기적으로 발표합니다. 또한 Sean은 Java Pet Store 2.0 참조 애플리케이션 및 Java BluePrints Solutions Catalog의 설계 및 개발에 관여해 왔습니다.

Inderjeet Singh는 Google, Inc.의 소프트웨어 엔지니어입니다. Google에 입사하기 전에 Inderjeet는 10년 동안 Sun Microsystems에서 주임 엔지니어로 일했고 Java EE SDK, Java Application Platform SDK 및 Java BluePrints 프로그램을 설계했습니다

"Java EE" 카테고리의 다른 글

2007/06/21 13:36 2007/06/21 13:36

TRACKBACK :: http://blog.sdnkorea.com/blog/trackback/397

댓글을 달아 주세요

  1. 한재진  수정/삭제  댓글쓰기

    좋은 정보 감사합니다...
    generics를 왜 사용하는지 잘 몰랐는데
    어느정도 이해가 돼네요 감사합니다...^^

    2007/09/04 10:06
  2. 고진구  수정/삭제  댓글쓰기

    쉬운 예제와 설명 고맙습니다.
    이제 자바를 배워보고 싶은 욕심이 생기네요

    2007/09/09 23:03
  3. 신경남  수정/삭제  댓글쓰기

    꼭 배우고싶었던 정보입니다.. 우와.

    2007/09/18 10:42
  4. 박정숙  수정/삭제  댓글쓰기

    좋은 정보 감사해요~

    2007/09/19 03:26
  5. 김형국  수정/삭제  댓글쓰기

    요즘 persistence란 단어를 많이 보게되네요..
    대세인듯..ㅋㅋ 제가 db관련된 문서에두 자주 나오드라구용..
    자세히는 보지 못했지만 나중을 위해 참고할수 있어서 좋습니다.

    2007/09/19 15:56
[로그인][오픈아이디란?]

◀ Prev 1  ... 244 245 246 247 248 249 250 251 252  ... 624  Next ▶