Tag를 만들어 쓰면 좋은 이유

1. 유지 보수가 편해집니다.
=> 한 곳(Tag를 정의한 곳)에서 수정을 하면 싹~ 바뀌기 때문에 편리합니다. 물론 화면에 특화 시켜서 Tag를 잘 정의 해 놓았기 때문에 가능한 일일 것입니다.

2. 웹 표준을 지키는데 유리해 집니다.
=> 웹 표준 담당자가 Tag 정의 한 부분을 손봐 주시고 다른 개발자는 신경쓰지 않고 개발을 해도 됩니다. Seperation of Concern이 이루어 지는거죠.

3. 성능에는 문제가 없습니다.
=> 처음에 자바 파일을 컴파일 해야 하기 때문에 조금 느리지 그 이후로는 오히려 컨테이너에서 캐슁하고 있기 때문에 HTML 태그를 쓰는 것보다 빠르다고 합니다.

4. 코드의 가독성이 높아집니다.
=> 화면에 특화 시켜서 나눠 놓았기 때문에 눈에 훨씬 잘 들어오겠죠.

5. 기타 등등등…
=> 좋은 것 같아요.

1-8. Hibernate Application 에서 사용되는 객체의 상태

bk90.bmp
Hibernate In Action 4.1.1 Transient Object 처음에 나오는 그림입니다. Hibernate에서 사용되는 객체의 상태 변화를 보여주고 있습니다.

  • Transient는 DB와 아무 연관이 없는 상태입니다.
  • Persistent는 DB와 동기화 되어 있는 상태입니다.
  • Detached는 DB와 동기화 된 적이 있는 상태입니다.

소스코드와 연관 시켜 보겠습니다.

        Pizza pizza = new Pizza();
        pizza.setName(“ines’s pizza”);
        pizza.setPrice(10000);
        pizza.setSize(“Large”);
        pizza.setToping(“Kimchi”);

        s.save(pizza);

        pizza.setPrice(15000);

        tx.commit();
        s.close();

        pizza.setPrice(20000);

위 소스크드와 그림을 보시면서 Pizza 객체의 상태를 추적해 보겠습니다. 먼저 new를 하여 객체를 생성하게 되면 이 객체는 transient 상태가 됩니다. 그 후에 s.save(pizza);를 하게 되면 Persistent 상태가 되는데 그렇기 때문에 이 전 글에서 s.update(pizza); 를 호출하지 않아도 DB와 동기화 되어 있는 상태이기 때문에 update가 된 것 같습니다. 그 뒤 s.close()를 호출하면 그 뒤 부터는 DB와 동기화 된 적은 있지만 현재는 그렇지 않은 Detached 상태가 됩니다.

new를 만나면 Transient => save() 이후 부터는 Persistent => close() 이후 부터는 Detached 상태 입니다.

Transient 상태와 Detached 상태의 차이점은 Transient 상태에서 Pizza의 pizzaId라는 속성은 null인 상태입니다. 하지만 Detached 상태는 DB와 동기화 된 적이 있기 때문에 pizzaId 속성에 값을 가지고 있습니다.

1-7. 레코드 update 하기

PizzaApp의 내용을 다음과 같이 수정합니다.

Pizza pizza = new Pizza();
        pizza.setName(“yuonghoe’s pizza”);
        pizza.setPrice(10000);
        pizza.setSize(“Large”);
        pizza.setToping(“Pepperoni”);

        s.save(pizza);

        pizza.setPrice(15000);
        s.update(pizza);

처음에 가격을 만원으로 했다가 너무 낮아서 만오천원으로 올리고 s.update(pizza); 메소드를 사용하여 update를 합니다.

실행시키면 콘솔 창에 다음과 같이 출력됩니다.

Hibernate: select nextval (‘Pizza_PizzaId_Seq’)
Hibernate: insert into O_Pizza (toping, price, name, size, pizzaId) values (?, ?, ?, ?, ?)
Hibernate: update O_Pizza set toping=?, price=?, name=?, size=? where pizzaId=?

bk87.bmp
DB를 확인해 본 결과 15000원으로 update된 것을 확인 할 수 있습니다.

PizzaApp에서 작성한 코드에서 s.update(pizza);를 삭제하고 다음과 같이 입력해 보겠습니다.

        Pizza pizza = new Pizza();
        pizza.setName(“chanwook’s pizza”);
        pizza.setPrice(10000);
        pizza.setSize(“Large”);
        pizza.setToping(“Kimchi”);

        s.save(pizza);

        pizza.setPrice(15000);

s.update(pizza); 가 빠진것을 제외하면 다른 부분은 거의 동일합니다. 이 것을 실행시키면 콘솔창에 다음과 같이 출력이 되고 DB에서 확인을 해도 역시 update 된 것을 확인할 수 있습니다.

Hibernate: select nextval (‘Pizza_PizzaId_Seq’)
Hibernate: insert into O_Pizza (toping, price, name, size, pizzaId) values (?, ?, ?, ?, ?)
Hibernate: update O_Pizza set toping=?, price=?, name=?, size=? where pizzaId=?

bk88.bmp
왜 이럴지는 HIA 4장에 나오는 그림을 보며 생각해 보겠습니다.

1-6. 모델 클래스 수정하기

Pizza 클래스에 새로운 필드를 추가해 봅니다.

toping이라는 멤버를 추가하고 역시 어노테이션을 사용하여 @Column으로 지정해 줍니다. 그리고 getter, setter를 만들어 준뒤 PizzaApp 클래스에 적당한 값들로 세팅해 주고 save를 해봅시다.
[#M_ more.. | less.. | //Pizza.java


private String toping;

@Column
   public String getToping() {
       return toping;
   }

   public void setToping(String toping) {
       this.toping = toping;
   }

//PizzaApp.java


Pizza pizza = new Pizza();
       pizza.setName(“seal’s pizza”);
       pizza.setPrice(500);
       pizza.setSize(“Family”);
       pizza.setToping(“tuna”);

       s.save(pizza);
…_M#]
그리고 PizzaApp를 실행 시키면 다음과 같이 새로운 필드가 생기고 기존에 있던 data들은 새로운 필드에 null값이 들어간 상태로 바뀝니다.

Hibernate: select nextval (‘Pizza_PizzaId_Seq’)
Hibernate: insert into O_Pizza (toping, price, name, size, pizzaId) values (?, ?, ?, ?, ?)

bk83.bmp
여기서 궁금한건..만약에 새로 만든 필드의 Column 속성에서 nullable을 false로 하면 어떻게 될까요? 해보면 되겠죠?

pgAdmin에서 table을 전부 날려버리고 Pizza 클래스에 추가했던 toping 멤버를 삭제하고 게터와 세터로 삭제합니다. 그리고 name, size, price만 넣어 줍니다.
bk84.bmp
이 상태에서 이제 다시 Pizza 클래스에 Toping을 추가하고 이번에는 @Column(nullable=false)로 설정을 해줍니다. 그리고 PizzaApp에서는 새로운 레코드를 추가합니다.
[#M_ more.. | less.. | //Pizza.java

    private String toping;

@Column(nullable = false)
    public String getToping() {
        return toping;
    }

    public void setToping(String toping) {
        this.toping = toping;
    }

//PizzaApp.java

Pizza pizza = new Pizza();
        pizza.setName(“seal’s pizza”);
        pizza.setPrice(200);
        pizza.setSize(“Family”);
        pizza.setToping(“Chees”);

        s.save(pizza);_M#]그리고 실행을 하면 다음과 같은 메시지가 콘솔창에 출력됩니다.

Hibernate: select nextval (‘Pizza_PizzaId_Seq’)
Hibernate: insert into O_Pizza (toping, price, name, size, pizzaId) values (?, ?, ?, ?, ?)

엇… DB로 확인해 봤더니 toping이라는 컬럼이 추가 되었고 새로운 레코드도 입력 되었습니다. 하지만 기존에 있던 레코드는 새로 생긴 컬럼이 null로 되어있습니다.

bk85.bmp
오호~~~table을 생성하는 쿼리를 살펴봤더니 toping 컬럼을 만들 때 not null을 넣지 않았군요. Hibernate 가 마치 ‘이정도 쯤이야’ 하며 으쓱해 하는 기분이군요. 새로 추가되는 필드는 nullable=false로 하더라도 무시 됩니다. 그리고 그렇게 처리하는 것이 논리적인 것 같습니다.

bk86.bmp

1-5. 모델 사용하기

앞에서 만든 모델을 사용하는 간단한 어플리케이션을 만들기 위해 src밑에 app라는 패키지를 만들고 그안에 PizzaApp 클래스를 만듭니다.

package app;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class PizzaApp {
    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext(
                new String[] { “applicationContext-dao.xml”,
                        “applicationContext-jdbc-datasource.xml” });
        SessionFactory sf = (SessionFactory) ac.getBean(“sessionFactory”);

        Session s = sf.openSession();
        Transaction tx = s.beginTransaction();

        //TODO 할일

        tx.commit();
        s.close();
    }
}

녹색 부분은 Spring을 사용하여 session 팩토리를 받아옵니다. 역시 전 Session Factory도 처음 보는 것이기 때문에 이 부분도 공부가 필요합니다.
파란색 부분은 세션 팩토리를 사용하여 세션을 열고 닫는 부분입니다.
보라색 부분은 세션으로 부터 트랜잭션을 시작하고 완료 시키는 부분입니다.

세션을 열고 트랜잭션 시작한 뒤에~ 하고 싶은 일을 하면 됩니다. 먼저 Pizza 객체를 생성하여 DB에 저장을 해봅니다.

        Pizza pizza = new Pizza();
        pizza.setName(“keesun’s pizza”);
        pizza.setPrice(200);
        pizza.setSize(“BIG”);

위와 같이 코딩을 하고 저장을 하겠다는 의미로

    s.save(pizza);

이렇게 하면 DB에 들어가게 될 것입니다.

기대를 품고 실행을 하면 다음과 같은 에러를 볼 수 있습니다.
[#M_ more.. | less.. | Exception in thread “main” org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘sessionFactory’ defined in class path resource [applicationContext-dao.xml]: Cannot resolve reference to bean ‘dataSource’ while setting bean property ‘dataSource’; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘dataSource’ defined in class path resource [applicationContext-jdbc-datasource.xml]: Error setting property values; nested exception is org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessExceptions (1) are:
이하 생략…
_M#]
postgres JDBC를 빌드 경로에 추가하지 않아서 발생한 에러입니다. 적절한 조취를 취한 뒤[footnote]postges설치 경로 / 8.1 / jdbc / postgresql-8.1-405.jdbc3.jar 를 lib에 복사한 뒤 빌드 경로에 추가 해줌[/footnote] 다시 실행시키면~
[#M_ more.. | less.. | Exception in thread “main” org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘sessionFactory’ defined in class path resource [applicationContext-dao.xml]: Error setting property values; nested exception is org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessExceptions (1) are:
PropertyAccessException 1: org.springframework.beans.TypeMismatchException: Failed to convert property value of type [java.util.ArrayList] to required type [java.lang.Class[]] for property ‘annotatedClasses’; nested exception is java.lang.IllegalArgumentException: Cannot find class [model.Member]. Root cause: java.lang.ClassNotFoundException:
_M#]
1-3. 기본 설정 하기
에서 유심히 보신 분은 눈치 채셨겠지만 applicationContext-dao.xml 의  sessionBean 설정에서 ‘뭔가 들어가야 할 부분’에 다음의 코드를 넣습니다.

<property name=”annotatedClasses”>
            <list>
                <value>model.Pizza</value>
            </list>
</property>

어노테이션이 사용된 클래스를 등록해 주는 것 같습니다. 그리고 이제 프로그램을 실행시키면 커맨드 창에서 다음의 메시지를 확인 할 수 있습니다.

Hibernate: select nextval (‘Pizza_PizzaId_Seq’)
Hibernate: insert into O_Pizza (price, name, size, pizzaId) values (?, ?, ?, ?)

그리고 DB에서 확인하면 다음과 같이 Table이 생기고 data가 들어간 것을 확인 할 수 있습니다.

bk82.bmp
정말 마술 같은 쑈입니다. 🙂 DB와 SQL에 손도 안대고 table을 만들고 데이타를 넣다니..허허허;;;