2007년 7월 27일 금요일

Java Persistence와 함께 Generics 사용하기

Java Persistence와 함께 Generics 사용하기

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 문서에 이를 표시한다.
요청은 다음과 같이 처리된다.

사용자 삽입 이미지


  • 기본 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;
    }


ttapr2007jpa-generics.zip

관련 소스 파일




출처 : SDN Korea

Java static inner class란?

Java에서 사용되는 static inner class와 inner class 차이를 알아보았다.

A클래스 내에 B라는 static innter class가 존재한다고 했을 때,
사실 B클래스는 A클래스의 inner class가 아니다.
그러므로, 일반적인 형태처럼 new 연산자를 사용하면 된다.
A.B name = new A.B();
static으로 메모리에 담기는 것과 static 없이 메모리에 담기는 것의 차이는
'static inner 클래스는 inner 클래스가 아니다'라는 점과 관련이 있는데,
일반적인 inner 클래스는 인스턴스 내부에 레퍼런스를 가지고 있다.
그래서 outer 클래스를 인스턴스화할 때, 내부클래스 레퍼런스의 선언이 이루어지고, 여기에 내부클래스를
new 연산자로 할당하는 것이다. (A클래스의 인스턴스 없이는 인스턴스 멤버필드의 선언도 접근도 불가능하다는 것과 마찬가지 이다.)
반면 static으로 선언된 내부클래스는 외부클래스에 클래스 레퍼런스를 가지고 있다.
A.B name = new A.B();
라는 문장은 'A클래스에 존재하는 B클래스'라고 해석하면 된다. 일반적인 내부클래스가 'A 클래스 객체안에 존재하는 B 클래스 객체'라는 것과는 대조적이다.
즉, 실제적으로 static inner 클래스는 단순히 A라는 클래스를 통해 참조하는 것만 다를 뿐 다른 점은 일반적인 클래스와 다를바가 전혀 없다.
그래서, 내부클래스만이 갖는 외부클래스와의 특별한 관계도 성립하지 않는다. 따라서 static inner 클래스는 문법적으로는 inner 클래스지만, 실제적으로는 문법적인 의미를 제외하면 inner 클래스라고 할 수 없다.

2007년 7월 16일 월요일

Spring MVC를 이용하기 위한 Web.xml에 대한 설정

web.xml에 대한 설정을 통하여 Spring MVC를 지원하게끔 한다.

<?xml version="1.0" encoding="UTF-8"?>

<web-app version="2.4"

       xmlns="http://java.sun.com/xml/ns/j2ee"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee

       http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

      

       <!-- Servlet configuration for Spring -->

    <context-param>

        <param-name>contextConfigLocation</param-name>

        <param-value>

              /WEB-INF/action-servlet.xml

        </param-value>

    </context-param>

   

    <servlet>

             <servlet-name>action</servlet-name>

             <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

             <load-on-startup>1</load-on-startup>

       </servlet>

   

    <!-- Servlet Mapping -->

       <servlet-mapping>

             <servlet-name>action</servlet-name>

             <url-pattern>*.do</url-pattern>

       </servlet-mapping>

</web-app>

Spring MVC에서 요청에 대한 생명주기

  1. Spring MVC 또한 클라이언트의 요청이 처음으로 진입되는 지점은 DispatcherServlet이다.
  2. 클라이언트로부터 요청이 들어오면 DispatcherServlet은 빈 설정 파일에 정의되여 있는 HandlerMapping을 이용하여 요청 URL에 해당하는 Controller 객체를 얻게 된다.
  3. DispatcherServlet은 HandlerMapping으로부터 Controller를 얻게 되면 요청에 대한 모든 작업을 Controller에게 위임하게 된다.
  4. Controller는 비즈니스 계층과의 통신을 완료한 다음 비즈니스 계층에서 전달된 모델 데이타와 클라이언트에게 보여줄 뷰화면에 대한 정보를 ModelAndView 클래스에 담아서 DispatcherServlet에 반환하게 된다.
  5. DispatcherServlet은 View객체를 이용하여 클라이언트에 화면을 출력하게 된다.
  6. 만약 ModelAndView에 저장되여 있는 View정보가 논리적인 View이름일 경우에는 빈 설정 파일에 정의되어 있는 ViewResolver 클래스를 이용하여 클라이언트에게 출력할 View객체를 얻게 된다.

2007년 7월 13일 금요일

Spring Struts Webwork2 비교분석

Spring, Struts, Webwork2 모두다 MVC기반하에 짜여진 프레임웍이다.

Struts의 단점은 프레임웍자체가 무겁다는것이다. 예를 들면 V(뷰)층에서 Struts는 필히 ActionForm이라는 클래스를 상속해야하고 C(컨트롤)층에서는 Action/DispatchAction을 상속해야만 한다.
이에 비해 Webwork2는 Struts보다 훨씬 가볍다. 화면을 구성하기 위하여 단순히 태그로만 모든것을 구현가능하다. Model층도 강제적으로 다른 클래스를 상속받을 필요가 없다.
Spring과 Webwork2는 사용면에서 그 무게가 비슷하지만 기능면에서 Spring이 우세를 차지한다.
화면에서 넘어온 데이터를 바인딩하는면에서 볼 때 Struts는 ActionForm을 이용하여 자동 바인딩을 한다. 하지만 이런 “자동” 바인딩을 하기 위하여서는 필히 태그라이브러리 아니면 뷰층의 HTML코드에서 선언된 필드네임과 ActionForm에서 각 속성네임이 매칭이 되여져야 한다는것이다.
Webwork2는 modelDriven된 getModel()을 이용하여 바인딩을 한다. Spring은 Controller에서 Command객체를 선언한다.
또한 Struts의 지정된 Action을 사용하기 위하여서는 그에 관련된 ActionForm즉 POJO가 미리 선언이 되여져있어야 하는데 비하여 Spring이나 Webwork2는 좀 더 가볍다. Webwork2의 Action은 ModelDriven 인터페이스를 구현하는것으로만 데이터 바인딩을 마치고 Spring도 SimpleFormController의 선언중에 단순히 Command객체만 설정하는것만으로 데이터 바인딩과정을 끝마친다. 실제 Webwork2나 Spring 에서 데이터바인딩을 하는 과정을 봤을대 아주 흡사하다.
데이터 인증면에서 볼 때 Struts는 M(Form) 층에서 ValidateForm 클래스를 상속하여 인증을 구현하지만 Webwork2나 Spring은 Action에서 인증을 구현한다.

2007년 7월 8일 일요일

Eclipse Europa 및 My Eclipse 6.0 릴리즈!!!

Eclipse Europa에 적응할 때가 왔다?!


사용자 삽입 이미지

Eclipse Europa 로고

Eclipse가 3.2 버전까지의 버전 이름을 버리고 이번에 Europa라는 이름으로 정식 배포되었다. Eclipse는 새로운 버전이 나올 때마다 뭔가 새로운 기능을 활용하기 위하여 노력했던 기억이 있다. 그러나 버전이 바뀌면서 그렇게 많은 큰 변화가 없었기 때문에 적응하는데 큰 문제가 없었다.
사실 Europa 버전을 설치하고 실행해보기 전까지 큰 변화를 느끼지 못한 것이 사실이다. 그러나 Eclipse Europa 버전을 다운 받았을 때 가장 다르다고 느낀 것은 작아진 Eclipse 파일 사이즈였다.
자바 개발자용, C, C++ 개발자용, RCP, 플러그인 개발자용, Cusomizing을 위한 용도..각각의 용도에 맞도록 최적화된 상태로 배포본을 만들었기에 파일 사이즈가 작아진 것이였다. 이와 같은 전략이 가능한 이유는 Eclipse가 가지고 있는 플러그인 방식의 개발 방법 때문일 것이다.
어차피 나와 같은 경우는 J2EE용만 다운로드 하면 그만이다. 하지만 어차피 MyEclipse 플러그인을 설치하기 위해서 아예 My Eclipse까지 통합된 버젼을 다운로드 받는것이 더욱 편하다.
Eclipse 3.2.2까지 대응되는 My Eclipse 버젼이 5.x대에 머물러 있었다.
하지만 Eclipse Europa부터 아예 My Eclipse  버젼이 6.0으로 껑충 뛰여 올랐다.

Eclipse를 시작하면서 다르다고 느낀 점은 Eclipse가 더 이상 버전을 사용하지 않고 Europa를 직접적으로 사용하고 있다는 것이다. Eclipse의 시작시 버전이 보이지 않는다는 것을 확인할 수 있다.
사용자 삽입 이미지
자바 개발자용 Eclipse 버전에서 가장 크게 달라진 점은 기본 플러그인으로 PDE가 빠지고 Mylyn이 포함되었다는 것이다. Mylyn은 Eclipse Europa가 나오기 전 Mylar라는 이름으로 알려진 Task Management를 위한 플러그인이였다. Bugzilla, Jira와 별반 차이가 없다고 느낄지 모르겠지만 사실 브라우저를 통하여 Bugzilla와 Jira에 접근하는 것이 아니라 Eclipse로 통합되여져 사용이 되여진것이다.
Mylyn기능에 대해서는 아래의 데모를 통하여 어렴풋이 나마 알아볼수 있다.
데모보기

태스크를 중심으로 한 프로그래밍 - Mylyn...
Integrated task management for Eclipse
Automated context management for Eclipse