보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

** 본 요약은 한빛미디어 '하이버네이트 프로그래밍' 책을 기준으로 작성되어져 있다.

** 본 설명은 바로 따라하기 하기엔 어려움이 있다. 책을 보지 않은 사람에게는 간단히 어떤 기능을 제공하는지 이해하는데 목적을 둔다.

** 하이버네이트 3.2.5.ga 버전, Ant 빌드를 기본적으로 사용한다.


5장 다양한 연관

이번 장에서는 5개의 주제를 가지고 설명한다. 앞장에서 다룬 연관을 조금 더 세밀하게 설정하는 방법을 배운다. 간단히 요약하면 다음과 같다. 

  1) 연관의 초기화 형태 : 느슨한(Lazy), 열성적인(Eager) 

  2) 순서가 있는 컬렉션 사용하기

  3) 순서가 있는 컬렉션 설정 보강하기

  4) 순서있는 연관 컬랙션 삭제에 따른 자동처리

  5) 재귀 연관

  

연관의 초기화 형태 : 느슨한(Lazy), 열성적인(Eager) 


객체내에 다른 객체를 참조하는 값을 가질 수 있는데, 참조되는 객체를 처음 생성때부터 초기화 할것인지(eager), 아니면 사용되기 전까지 초기화를 미룰 것인지(lazy) 설정하는 방법을 설명한다.


하이버네이트3부터는 연관 객체에 대한 생성은 늦은 초기화가 기본적으로 사용된다. 즉, 실제로 데이터를 사용하기 전까지 데이터베이스에서 로드하지 않는다는 것이다. 그러나 주의할 것은 하이버네이트 세션을 닫게 되면 연관 객체의 늦은 초기화가 작동하지 않아 LazyInitializationException을 던진다. 즉, Lazy를 사용하려면 하이버네이트 세션을 닫으면 안된다는 것이다. 


그러면 기본적인 늦은 초기화를 사용하지 않으려면 어떻게 하는지 알아보면 다음 코드 처럼 작성하면 된다.


<set name="association_Obj" table="AAA_BBB" lazy="false">

  <key column="AAA_ID"/>

  <many-to-many class="com.billy.sample.BBB" column="BBB_ID"/>

</set>


위와 같이 lazy="false"를 추가해 빠른(eagerly) 초기화를 설정할 수 있다.


다양한 연관에서 위와 같이 늦은 초기화를 사용안하게 할 수 있지만 특정 클래스 참조의 대한 부분 모두를 빠른 초기화 설정을 하는 방법은 매핑문서의 class 태그에 lazy="false"를 직접 사용하면 된다.



순서가 있는 컬렉션 설정 방법


만약 다른 연관된 테이블 데이터에 순서가 있을때, 순서를 지정하기 위한 방법을 다룬다. 일단 List 컬랙션과 맵핑해야 하고, 리스트의 순서를 설정하는 list_index 태그를 추가해야 한다. 그리고 순서 조정 값을 갖는 컬럼을 데이터 베이스에 추가해야 한다. 코드를 보면 다음과 같다.


<list name="bbbList" table="AAA_BBB" lazy="false">

  <key column="AAA_ID"/>

  <list-index column="LIST_POS"/>

  <many-to-many class="com.billy.sample.BBB" column="BBB_ID"/>

</list>


하이버네이트에서는 LIST_POS를 관리하여 나중에 데이터베이스에서 꺼낸 리스트 내용으 우리가 저장한 컬럼과 동일한 순서로 나오게 된다. 


순서가 있는 컬렉션 설정 보강하기


* 데이터베이스 스키마에 테이블을 넣는 방법

1) 테이블의 컬럼에 자바 객체의 매핑 프로퍼티를 명시하는 것

2) 컬렉션(컬렉션의 값 또는 연관)을 정의하고, 그 컬렉션을 관리하는데 사용할 테이블과 컬럼을 지정하는 것


* 엔티티와 컴포넌트

- 엔티티 : 다른 객체에 대해 독립적으로 생성, 질의, 삭제될 수 있는 것

- 컴포넌트 : 다른 엔티티의 종속적인 부분으로서 데이터베이스에 저장되거나 데이터베이스에서 검색 될 수 있는 것


* 컴포넌트 맵핑

<list name="tracks" table="ALBUM_TRACK">

  <meata attribute="use-in-tostring">true</meta>

  <key column="ALBUM_ID"/>

  <index column="LIST_POS"/>

  <composite-element class="com.oreilly.hh.data.AlbumTrack">

    <many-to-one class="com.oreilly.hh.data.Track" name="track" cascade="all">

      <meta attribute="use-in-tostring">true</meta>

      <column name="TRACK_ID" />

    </many-to-one>

    <property name="disc" type="integer" />

    <property name="positionOnDisc" type="integer" />

  </composite-element>

</list>


위 컴포넌트 맵핑은 새로운 AlbumTrack라는 클래스를 소개한다. 3장에서 소개한 N:N 맵핑의 경우 조인 테이블을 사용한다고 했는데, 그 조인 테이블을 사용하는 클래스가 없다고 설명했다. 그런데 이와 같이 N:N 조인테이블에 부가정보를 추가해 새로운 관리 클래스를 생성할 수 있다. 이렇게 생성된 코드를 살펴보면 다음과 같다.


public class AlbumTrack  implements java.io.Serializable {

  private Track track;

  private Integer disc;

  private Integer positionOnDisc;


  public AlbumTrack() {

  }


  public AlbumTrack(Track track, Integer disc, Integer positionOnDisc) {

    this.track = track;

    this.disc = disc;

    this.positionOnDisc = positionOnDisc;

  }

 

  public Track getTrack() {

    return this.track;

  }

  

  public void setTrack(Track track) {

    this.track = track;

  }

  public Integer getDisc() {

    return this.disc;

  }

  

  public void setDisc(Integer disc) {

    this.disc = disc;

  }

  public Integer getPositionOnDisc() {

    return this.positionOnDisc;

  }

  

  public void setPositionOnDisc(Integer positionOnDisc) {

    this.positionOnDisc = positionOnDisc;

  }


  /**

   * toString

   * @return String

   */

  public String toString() {

 StringBuffer buffer = new StringBuffer();


    buffer.append(getClass().getName()).append("@").append(Integer.toHexString(hashCode())).append(" [");

    buffer.append("track").append("='").append(getTrack()).append("' ");

    buffer.append("]");

     

    return buffer.toString();

  }

}


위 코드는 id 프로퍼티가 빠져있다. 컴퍼넌트 클래스는 식별 필드나 특별한 인터페이스 구현이 필요 없다. 


순서있는 연관 컬랙션 삭제에 따른 자동처리


- 부모 객체와 자식 객체의 종속성 설정 : 하이버네이트에서는 "부모"객체가 실행되면 "자식" 혹은 "의존" 객체까지 전이되는 연산을 cascade 어트리뷰트로 처리할 수 있다. 이 기능은 모든 종류의 컬렉션과 연관에 적용된다. 

- 사용 방법 : 객체 맵핑 프로퍼티에 <cascade> 태그를 추가한다.

  -> cascade 값 : none, save-update, delete, all(save-update+delete)

  -> 전체 default cascade 변경 : <hibernate-mapping> 태그에 default-cascade 어트리뷰트 추가

  -> 부모가 삭제되면 자식도 동시에 삭제 가능하도록 설정 가능

  -> 중간에 order관련 값들을 자동으로 변경해줌

  

재귀 연관


객체나 테이블에서 자체적으로 연관을 맺을 수 있다. 트리같은 객체 구조를 생성할 수 있다는 것이다. 이는 단순하다. 

Artist 클래스 맵핑 문서야 다음과 같이 자기 자신을 참조하는 필드를 추가하면 된다. 


<many-to-one name="actualArtist" class="com.oreilly.hh.data.Artist">

 <meta attribute="use-in-tostring">true</meta>

</many-to-one>


  

  

  

Posted by 빌리 :

자바에서 transient 필드는 뭐하는 것일까?


객체내에 변수를 사용할때 transient로 지정하게 되면 직렬화(serializing), 또는 역직렬화(deserializing)시에 해당 변수는 직렬화 하지 않는 다는 것을 가리키는 것이다. 한마디로 하면 Object를 파일로 직접 저장할 때 직렬화 기능을 이용하는데, transient로 지정되지 않은 변수 값만 저장한다는 것이다.


간단히 테스트 코드를 작성 후 실행하면 다음 결과가 나온다.


package com.billyvme.serialize.sample;


import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;

import java.io.Serializable;


public class TransientSample {


  public static final String FILENAME = "data.ser";


  /**

   * @param args

   * @throws IOException

   * @throws ClassNotFoundException

   */

  public static void main(String[] args) throws IOException,

      ClassNotFoundException {

    Point before = new Point();

    before.x = 7;

    before.y = 20;

    before.rho = (float) 3.14;

    before.theta = (float) 12.15;


    writeObject(before);


    Object after = readObject();

    System.out.print("Before : ");

    System.out.println(before);

    System.out.print("After  : ");

    System.out.println(after);

  }


  private static void writeObject(Object obj) throws IOException {

    FileOutputStream fos = new FileOutputStream(FILENAME);

    ObjectOutputStream oos = new ObjectOutputStream(fos);

    oos.writeObject(obj);

    oos.close();

    fos.close();

  }


  private static Object readObject() throws IOException, ClassNotFoundException {

    FileInputStream fis = new FileInputStream(FILENAME);

    ObjectInputStream ois = new ObjectInputStream(fis);

    Object result = ois.readObject();

    ois.close();

    fis.close();


    return result;

  }

}


class Point implements Serializable {

  int x, y;

  transient float rho, theta;


  @Override

  public String toString() {

    return String.format("x=%d, y=%d, rho=%f, theta=%f", x, y, rho, theta);

  }

}


위 코드를 실행해 보면 다음과 같은 결과가 출력 된다.


Before : x=7, y=20, rho=3.140000, theta=12.150000

After  : x=7, y=20, rho=0.000000, theta=0.000000


결과에서 보듯이 transient로 지정되지 않은 x,y 값만 다시 출력되는 것을 확인 할 수 있다. 

클래스 내에 암호 값이나 키 값을 속성 내에서 사용하는데 전송되거나 저장되면 안되는 값을 transient로 지정하면 좋을 것 같다. 

그런데 실제로 이 지시어를 써서 프로그래밍을 한 적이 없다. 매번 어플리케이션만 개발해서 그런걸까??

'개발자 > Java' 카테고리의 다른 글

자바 테스트를 위한 질문 정리  (0) 2013.09.24
Clone 메소드를 이용한 List의 객체 복사  (0) 2013.06.17
Posted by 빌리 :

** 본 요약은 한빛미디어 '하이버네이트 프로그래밍' 책을 기준으로 작성되어져 있다.

** 본 설명은 바로 따라하기 하기엔 어려움이 있다. 책을 보지 않은 사람에게는 간단히 어떤 기능을 제공하는지 이해하는데 목적을 둔다.

** 하이버네이트 3.2.5.ga 버전, Ant 빌드를 기본적으로 사용한다.


4장 컬렉션과 연관

본 장에서는 객체간 관계나 그룹이 처리되는 방법은 알아본다.


테이블 간의 관계 표현하기


두개의 클래스 용 맵핑 문서를 만들고, 그 안에 'set'이라는 태그를 이용해 연결 정보를 추가하여 사용한다.


N:N 연결 관계는 다음 코드를 연결할 양쪽 클래스 맵핑 문서에 다음 내용을 추가해 구현한다.


<set name="[필드명]" table="[조인 테이블명]">

  <key column="[현재 테이블의 아이디 키]"/>

  <many-to-many class="[연결되는 대상 클래스]" column="[연결되는 테이블의 아이디 키]"/>

</set>


양쪽에 <many-to-many> 태그를 사용하면, 하이버네이트에서는 자동으로 현재 테이블의 아이디 키와 연결되는 테이블의 아이디 키가 컬럼을 가진 조인 테이블을 생성한다. 이 테이블과 실제로 맵핑되는 객체는 존재하지 않고 N:N 관계를 유지하기 위한 테이블인 조인 테이블로 사용하게 된다.


실제 전체 코드를 보면 다음과 같이 작성될 수 있다.


AAA 클래스용 매핑 문서

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"

"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>

<class name="com.billy.sample.data.AAA" table="AAA">

<meta attribute="class-description">

A 샘플 클래스 매핑 예제

</meta>

<id name="id" type="int" column="AAA_ID">

<meta attribute="scope-set">protected</meta>

<generator class="native"/>

</id>

    

<property name="name_a" type="string" not-null="true"/>

    

    <set name="bset" table="AAA_BBB" inverse="true">

      <meta attribute="field-description">연관된 B 클래스</meta>

      <key column="AAA_ID"/>

      <many-to-many class="com.billy.sample.data.BBB" column="BBB_ID"/>

    </set>

  </class>

</hibernate-mapping>


BBB 클래스용 매핑 문서

<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"

"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>

<class name="com.billy.sample.data.BBB" table="BBB">

<meta attribute="class-description">

B 샘플 클래스 매핑 예제

</meta>

<id name="id" type="int" column="BBB_ID">

<meta attribute="scope-set">protected</meta>

<generator class="native"/>

</id>

    

<property name="name_b" type="string" not-null="true"/>

    

    <set name="aset" table="AAA_BBB">

      <meta attribute="field-description">연관된 A 클래스</meta>

      <key column="BBB_ID"/>

      <many-to-many class="com.billy.sample.data.AAA" column="AAA_ID"/>

    </set>

  </class>

</hibernate-mapping>


위 코드 중에 AAA 클래스용 매핑 문서의 <set> 태그 안에 inverse(역매핑)이 설정 되어 있다. 이는 AAA클래스의 bset이 변경이 일어나면 AAA_BBB 테이블이 변경되지 않지만, BBB클래스의 aset이 변경이 일어났을때 AAA_BBB 테이블이 자동 갱신되도록 설정한다.


이렇게 맵핑 문서를 작성하고 난 후 Hibernate tool을 이용해 소스코드를 생성하면 해당 객체에 다음과 같은 코드가 추가 된다.


public class AAA implements java.io.Serializable {

  ...

  private Set bset = new HashSet(0);

  

  public Set getBset() {

    return this.bset;

  }

  

  public void setBset(Set bset) {

    this.bset = bset;

  }

  ...

}


그런데 위의 코드는 Java 5 버전에서는 Typesefe 경고가 발생한다. 이를 처리하기 위해서는 자바 코드 생성시 다음과 같은 코드를 추가해준다.(Ant task기준)


<hbm2java jdk5="true">

위 코드를 추가 후에 자바코드를 추가 하면 


private Set<BBB> bset = new HashSet<BBB>(0); 

과 같이 타입이 명시된 제너릭 코드가 생성된다.


 

객체에서 관계된 테이블(컬렉션) 데이터 입력하고, 데이터 읽어오기


위 단계에서 N:N 설정된 데이터를 사용하는 방법은 간단한다. 자동생성된 클래스의 Set 부분에 해당 값을 추가 해서 저장하면 자동으로 조인테이블에 데이터가 생성된다. 데이터 읽어오는 방법도 기존의 앞에서 해당 객체를 불러오는 방법과 동일하게 사용하면 데이터를 가져올 수 있다.



1:N 관계 표현하기


N:N 관계 설정하는 방법과 비슷하지만 조금 더 간단한다. N:N에서 설정한 맵핑 파일에 주석을 단다고 가정하면 다음과 같이 맵핑 코드를 작성할 수 있다.


<set name="comments" table="AAA_COMMENTS">

  <key column="AAA_ID"/>

  <element column="COMMENT" type="string"/>

</set>


그러면 AAA_COMMENTS 테이블이 생성되고, 자바 코드상에도 Set<String> comments가 추가되어 사용할 수 있게 된다.



Posted by 빌리 :

** 본 요약은 한빛미디어 '하이버네이트 프로그래밍' 책을 기준으로 작성되어져 있다.

** 본 설명은 바로 따라하기 하기엔 어려움이 있다. 책을 보지 않은 사람에게는 간단히 어떤 기능을 제공하는지 이해하는데 목적을 둔다.

** 하이버네이트 3.2.5.ga 버전, Ant 빌드를 기본적으로 사용한다.


3장 하이버네이트 활용


하이버네이트 설정(Configuration)을 properties 파일에서 cfg.xml 파일로 대체하기


기본 DB 접속 설정을 properties 파일, xml 파일, 또는 Program에서 직접 설정할 수 있다. 이 단락에서는 xml 파일을 이용한 설정 방법을 알아본다.


- 설정 방법

  1) hibernate.cfg.xml 파일 생성 (샘플코드 참조) : src 최상위 디렉토리에 저장한다.

<?xml version='1.0' encoding='utf-8'?>


<hibernate-configuration

        xmlns="http://www.hibernate.org/xsd/hibernate-configuration"

        xsi:schemaLocation="http://www.hibernate.org/xsd/hibernate-configuration hibernate-configuration-4.0.xsd"

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

  <session-factory>

    <!-- Database connection settings -->

    <property name="connection.driver_class">org.hsqldb.jdbcDriver</property>

    <property name="connection.url">jdbc:hsqldb:hsql://localhost</property>

    <property name="connection.username">sa</property>

    <property name="connection.password"></property>


    <!-- JDBC connection pool (use the built-in) -->

    <property name="connection.pool_size">1</property>


    <!-- SQL dialect -->

    <property name="dialect">org.hibernate.dialect.HSQLDialect</property>


    <!-- Enable Hibernate's automatic session context management -->

    <property name="current_session_context_class">thread</property>


    <!-- Disable the second-level cache  -->

    <property name="cache.provider_class">org.hibernate.cache.internal.NoCacheProvider</property>


    <!-- Echo all executed SQL to stdout -->

    <property name="show_sql">true</property>


    <!-- Drop and re-create the database schema on startup -->

    <!-- 아래 기능은 3.2버전에서 동작하는지 확인해야한다. -->

    <!--

    <property name="hbm2ddl.auto">update</property>

    -->

    

    <!-- 사용 중인 매핑 문서 나열 -->

    <mapping resource="org/hibernate/tutorial/domain/Event.hbm.xml"/>

  </session-factory>

</hibernate-configuration>

  

  2) Ant build.xml 파일에 XML 설정을 사용할 수 있도록 지정

    -> Hibernate Tool Task를 사용하는 Target의 '<hibernatetool>' 태그 안에 다음 줄을 추가한다.    

<configuration configurationfile="${source.root}/hibernate.cfg.xml"/>


자바 객체를 DB에 저장하기


실제로 자바 코드를 이용해 어떻게 데이터를 생성하는지 알아본다. 전체 흐름을 간단히 요약하고, 코드를 보면 다음과 같다.


1) 기본 설정 부르기 

2) 세션 팩토리 가져오기 

3) 세션 열기 

4) 트랜잭션 열기 

5) 객체 생성 저장하기

6) 트랜잭션 반영 

7) 세션 닫기


void sample() {

  Configuration config = new Configuration()  // (1)

  config.configure();

  

  SessionFactory sessionFactory = config.buildSessionFactory(); // (2)

  

  Session session = sessionFactory.openSession()  // (3)

  Transaction tx = null;

  try {

    tx = session.beginTransaction();  // (4)

    

    // 맵핑 객체 생성 

    MyObject obj = new MyObject("name", "value");

    session.save(obj);  // (5)

    

    obj = new MyObject("name2", "value2");

    session.save(obj);

    

    tx.commit();  // (6)

  } catch (Exception e) {

    if (tx != null) {

      tx.rollback();

    }

    throw new Exception("Transaction failed", e);

  } finally {

    session.close();  // (7)

  }

  sessionFactory.close();  // (7)

}


객체는 session.save(obj)가 호출 된 이후에 영속 객체가 된다. 즉 DB 데이터로 저장되어 진다.


SessionFactory 객체는 스레드세이프 하기 때문에 전체 애플리케이션에서 하나만 사용하지만, Session 객체는 스레드세이프 하지 않기 때문에 하나의 작업을 마치고 다음 작업으로 넘어가면 SessionFactory에서 새 세션을 가져와 사용하도록 한다. 



DB 데이터를 자바 객체로 가져오기


저장된 DB 데이터를 가져오는데 하이버네이트에서는 여러가지 방법을 제공한다. 

  - HQL(하이버네이트의 sQL기반 쿼리언어)를 이용한 검색

  - Named Query/Named parameter 이용한 검색

  - Native SQL Query 이요한 검색 

  

이번 단락에서는 HQL와 Named Query/Parameter를 사용하는 간단한 예제를 설명한다.


첫째로 HQL 방법은 다음 코드와 같이 session객체의 createQuery([쿼리]) 메소드를 이용한다.


Query query = session.createQuery("from Track as track " + 

                                  "Where track.playTime <= ?");

query.setParameter(0, value, Hibernate.[TYPE]);

query.list();


HQL은 JDBC의 PreparedStatement와 유사한 파라미터를 지원한다. 이런 방법은 SQL 주입 공격을 막지만 조금더 편리한 방법을 제공한다. 그리고 쿼리의 내용을 보면 객체와 테이블의 이름이 같고 컬럼과 프로퍼티의 이름을 동일하게 사용되어진다. 기본적으로 동일한 이름을 가지고 DB 스키마가 생성되기 때문이지만, 추후 명시적으로 다른 이름을 사용할 수 있도록 지정하는 방법을 알아본다.


다음으로는 위 코드에서 좀 더 편하게 파라미터를 입력할 수 있는 네임트 파라미터를 설명한다. 코드를 먼저 보면 다음과 같다.


Query query = session.createQuery("from Track as track " + 

                                  "Where track.playTime <= :value");

query.setString("value", value);

query.list();


네임드 파라미터는 쿼리 본문에 접두어 콜런(:)으로 구분 한다. Query 객체는 setter 메소드를 자바 타입별로 모두 갖쳐줘 있어서, query.setInteger(), query.setString()등의 메소드를 이용해 적절한 타입으로 설정할 수 있다.


이는 쿼리 상의 파라미터 값을 명시적으로 맵핑 시켜주기에 오류 가능성을 줄여준다. 혹시 파라미터를 수많은 '?'로 지정하고, 그 뒤에 코드로 파라미터 값을 맵핑해주는 코드를 짜본적 있다면 네임드 파라미터 방법의 편리성은 피부로 느낄 수 있다. 제일 큰건 순서 제약이 없어진다는 것이다. 테이블이 변경되어 수많은 파라미터 중에 중간 값이 없어지고, 새로운 값이 추가된다면, 이때 코드를 신경써서 작성하지 않는다면 오류가 발생할 확률은 커지게 마련인데 이를 개선시킨 것이다.


그럼 쿼리문을 자바 소스에서 분리 할 수 있는 방법을 설명한다. 분리는 다음 단계로 진행 된다. 

  1) 하이버네이트 매핑 문서(hbm.xml)의 <hibernate-mapping> 태그 내에 매핑 쿼리 작성하기

<query name="com.billy.getData">

  <![CDATA[

    from Track as track

    where track.playTime <= :value

  ]]>

</query>


  2) session 객체에서 getNamedQuery() 메소드를 이용해 쿼리를 호출

Query query = session.getNamedQuery("com.billy.getData");

query.setString("value", value);

query.list();


물론 하이버네이트 매핑 문서(hbm.xml)는 하이버네이트 설정 파일에 추가되어 있어야 한다.



Posted by 빌리 :

POJO 란?


각종 프레임웍 책에서, 그리고 기술 서적에서 POJO라는 단어를 통해 처음 접하게 된 용어이다. 그런데 막상 책에 나온 설명을 읽고, 다시 인터넷을 찾아봐도 이해가 되지 않았다. 평범한 객체!? 명백(Plain) 오래(Old) 자바(Java) 객체(Object)라는데 이게 무슨 말일까? 


실제적으로 POJO를 이해하기 위해선 Enterprize Java Bean(EJB)를 이해해야 한다. EJB에서 나오는 빈을 사용하는데 단점들을 극복하기 위해서 시작되었다. EJB에서 복잡한 3-티어(tier) 애플리케이션 환경을 이해해야 하는 어려움이 있었다. 단순히 빈을 만들기 위해서 다양한 부모 클래스를 알아야 했고, 인터페이스를 구현해야 했던 것이다. 이러한 제약들을 없애고 '단순한 자바 객체'를 이용해 개발하자는 것이었다. 이후에는 데이터베이스와 직접 상호 작용하기 위해 POJO 기반의 하이버네이트가 개발되어고, 간단하게 작업을 ORM 기능을 구현할 수 있게 되었다.(하이버네이트를 공부해보니, mybatis보다 쉽게 데이터베이스 작업을 할 수 있다. 한번 공부해놓으면 좋은 기술이다.)


개념적으로는 자바 언어 명세에서 강제적으로 제한되어진 것을 제외하고 어떠한 제약이 없는 자바 객체를 말한다. 구체적으로 예를 들면 다음과 같다. 


1. 부모 클래스를 확장하지 않는다.

public class Foo extends javax.servlet.http.HttpServlet { ...


2. 인터페이스 클래스를 구현하지 않는다.

public class Bar implements javax.ejb.EntityBean { ...


3. 어노테이션(Annotation)을 포함하지 않는다.

@javax.persistence.Entity public class Baz { ...


하지만, 여러 이유 때문에 많은 소프트웨어 제품이나 프레임워크에서 영속성(Persistence)과 같은 기능이 제대로 작동할 수 있도록 어노테이션을 사용하도록 제한을 두고 있다.


POJO는 기존의 복잡한 것들을 버리고, 간단한 자바 객체를 가지고 일을 처리하자는 철학을 가지고 있다.

클래스를 간단히 설계하고, 이 클래스를 여기저기에 배치하기 쉽게 의존성을 줄인 것이다.



POJO는 어떻게 쓰이나?


1. JavaBeans

자바빈 = 직렬화된 POJO. 특징을 살펴 보면 생성자는 어떠한 매개변수도 갖지 않으며, 간단한 이름 명명 규칙([get|set] + 변수명)을 따르는 getter/setter 메소드를 이용해 속성(Property)에 접근할 수 있다. (이러한 규칙을 활용해 코드를 작성하지 않고, 어떻게 사용할 것인가 모델을 명시하면, 프로그램을 작성되는 솔루션도 있다.)  


다음은 POJO 속성들과 양방향 연결을 가진  JSF 컴포넌트의 예를 보여 준다.

<h:inputText value="#{myBean.someProperty}"/>

POJO 클래스는 아래와 같이 정의 될 수 있다.

public class MyBean {
 
    private String someProperty;
 
    public String getSomeProperty() {
         return someProperty;
    }
 
    public void setSomeProperty(String someProperty) {
        this.someProperty = someProperty;
    }
}


자바빈에서 속성 값 "someProperty"을 자동으로 "getSomeProperty()" 메소드를 연결해 값을 가져오고, "setSomeProperty(String)" 메소드를 연결해 값을 설정할 수 있기 때문에 위와 같이 사용할 수 있다.


2. Framwork

POJO를 사용하는 설계들이 점점 더 일반적으로 사용면서, 프레임워크내에서도 POJO의 역할이 커졌다. 이런 모델에서 개발자는 POJO 이외의 것은 생성할 필요가 없어졌다. POJO는 프레임워크 내에 어떠한 의존성을 가지지 않으면서, 순수하게 비즈니스 로직에 초점을 맞출수 있었다. 결국 개발자는 프레임워크를 이용해 개발할 때 비즈니스 로직을 포함한 단순한 클래스를 생성하고, 이를 맵핑해주는 설정 파일만 작성하면 할 일이 끝난다. 


이러한 생각들을 제일 먼저 구현한게 Spring이고, 현재 가장 인기있는 프레임워크가 되었다. 다른 것들에는 다음과 같은 것들이 있다.


- Enterprise JavaBeans

  2.0 버전에서는 JavaBeans 구현하기 위해서 EnterpriseBean을 상속받는 클래스로 구현해야 했지만, EJB3부터 POJO를 지원하기 시작했다. 뒤늦게 POJO의 영향력이 커지며 이를 반영했다.


- JPA (including Hibernate)

  Java persistence API로 자바를 이용해 관계형 데이터를 관리하는 프레임워크이다. 흔히 쓰는 mybatis 같은 O/R 맵핑 프레임워크라고 생각하면 된다. 대신 좀 더 간단하게 쓰기 쉽게 사용할 수 있고, JPA를 기반으로 개발되어진게 Hibernate이다.


- CDI (http://jcp.org/en/jsr/detail?id=299)

  이건 뭐지? 조금 찾아봐야겠다. 2009년에 마지막 릴리즈 이후 추가 개발 활동은 없고, 커뮤니티 가도 2011년 글이 끝이고, 소리 소문 없이 사장되고 있나보다.

  


실제로 POJO를 사용하는 프레임워크를 사용하려면 POJO클래스를 어디서 사용해야 할지 설정이 필요하다. 초기에는 이를 주로 XML을 이용해 설정하다, 이후 어노테이션(Annotation)을 이용하기 시작됐고, 대부분의 프레임워크가 둘 다 지원하게 되었다.


만약 아무것도 배우지 않고 프레임워크를 접하게 된다면, 아무것도 할 수 있는게 없다. 마치 디자인 패턴을 배우지 않은 사람이 디자인 패턴으로 도배된 코드를 보면서 헤메는 것과 같다고나 할까? 프레임워크를 사용하기 위해서는 사용설명서를 반드시 읽어야 한다는 것이다.


프레임워크에서 POJO를 사용하면서 다양한 기술을 사용할 수 있게 되었다. 그게 AOP(Aspect-oriented programming)를 가능하게 만든게 아닐까 하는 생각이 든다. 



정리


POJO를 처음 접하게 되면, 인터넷을 찾아보고, POJO에 대한 글을 읽으며 혼란을 느꼈었다. 뭐 단순한 클래스라고 하는데, 무슨 말이야? 라는 의문이 제일 먼저 들었다. 그런데 시간이 지나면서 프레임워크에 대한 지식도 쌓이고, 설계도 관심을 가지게 되면서 POJO의 역할을 생각해보면, POJO란 하나의 철학이 아닐까 한다. 그 철할을 활용한 것이 프레임워크가 된 것이고 말이다. 처음 접하는 사람은 너무 복잡하게 생각하지 말고, 아~ 이런게 있구나 라고 넘어가도 될 것 같다.



참고 사이트


http://en.wikipedia.org/wiki/Plain_Old_Java_Object

http://blog.naver.com/PostView.nhn?blogId=iamtheman&logNo=130127393725

http://gissmmo.tistory.com/436

http://keymaker83.blogspot.kr/2010/05/pojo%EB%9E%80%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80.html

http://srzero.tistory.com/m/post/view/id/86


* 생각이 조금 다른 부분이 있다면 댓글을 남겨주세요. 위에 글은 개인적인 정리글이라 틀린 부분이 있을 수 있습니다.

'개발자 > Java Enterprise' 카테고리의 다른 글

Java Message Service API  (2) 2013.05.15
Posted by 빌리 :

컴퓨터 프로그램을 개발하면 흔히 MVC 구조, MVC 패턴,  MVC 아키텍처라는 단어를 많이 듣게 된다. 그리고 그 뒤에 간단한 설명이 뒤 따른다.

 

MVC에서 모델은 애플리케이션의 정보(데이터)를 나타내며, 뷰는 텍스트, 체크박스 항목 등과 같은 사용자 인터페이스 요소를 나타내고, 컨트롤러는 데이터와 비즈니스 로직 사이의 상호동작을 관리한다. (출처-위키피디아)

 

뭐 간단히 정리하면 프로그램을 구성하는 요소들을 데이터, 화면, 컨트롤로 나누어서 각자의 역할을 부여해고, 각자 일에만 충실히 수행하자는 말이된다. 결국 각각이 가진 특징들을 이해해, 구성 요소들이 할일만 구현하면, 다른 요소를 수정하지 않으면서 유지보수가 편해진다고 한다.

 

그런데 직접 개발을 하면서 드는 의구심은 이렇다.

 

- 정말 다른건 수정안해도 되나?
- 각각의 역할이 정확히 뭐지??
- 구성 요소들을 알맞게 분류한게 맞을까?
- 컨트롤이 하는 역할은 어디까지 해야하나?
- 컨트롤하는 역할은 너무 많은데..? 이게 정말 맞는거야?

 

이렇게 수많은 궁금증들이 떠오르곤 한다. 수많은 책에서 MVC 구조라고 말하며, MVC의 구성요소들을 설명하면서 스스로 반성을 해보면 스스로 MVC가 무엇이야? 그리고 어떻게 해야 MVC 구조인거야? 라고 묻지만 단순하게 MVC 구성요소와 그 역할들만 설명할 수 있을 거 같다. 그래서 MVC의 각 구성 요소에 대한 가이드 라인정리 하고자 한다.

 

실제로 MVC 아키텍처로 개발된 Framework를 사용한다며, 소스들의 적절한 위치 및 역할을 지키지 않고 개발된다면, 실제 MVC 구성 요소들의 힘이 제대로 발휘되지 않을 것이다. 결국 다시 코드들을 찾아서 죄다 수정하는 평소로 되돌아 갈테니 말이다.

 

정리에 대한 MVC에 가이드라인은 Eclipse Graphical Editing Framework(GEF)에 대한 개념 설명 글을 바탕으로 작성함을 밝힌다.(GEF의 이해[한글][영문])

 

모델(Model)


데이터를 가진 객체를 모델이라 지칭한다.  데이터는 내부에 상태에 대한 정보를 가질 수도 있고, 모델을 표현하는 이름 속성으로 가질 수 있다.

 

모델은 다음 규칙을 가지고 있음을 이해해야 한다.

1) 사용자가 편집하길 원하는 모든 데이터를 가지고 있어야만 한다.

즉, 화면에 네모 박스 안에 글자가 표현 된다면, 네모 박스의 화면 위치 정보, 네모 박스의 크기 정보, 글자 내용, 글자의 위치, 글자의 포멧 정보 등을 가지고 있어야 한다.

 

2) 뷰(View)나 컨트롤러(Controller)에 대해서 어떤 정보도 알지 말이야 한다.

 데이터 변경이 일어 났을때, 모델에서 화면 UI(View)를 직접 조정해서 수정할 수 있도록 뷰를 참조하는 내부 속성값을 가지면 안된다는 말이다.

 

3) 변경이 일어나면, 변경 통지에 대한 처리 방법을 구현해야만 한다.

모델의 속성 중 텍스트 정보가 변경되면, 이벤트를 발생시켜 누군가에게 전달해야 하며, 누군가 모델을 변경하도록 요청하는 이벤트를 보냈을때 이를 수신할 수 있는 처리 방법을 구현해야 한다. 이는 모델이 변경되는 방법을 다른 구성 요소들에게 알려주게 되는 방법이다.

 

 

뷰 (View)


화면에 표시되는 글자, 체크박스, 윈도우와 같은 UI라는 시각적 요소를 지칭한다.

 

뷰에서도 다음 규칙을 이해하고, 사용해야 한다.

1) 모델이 가지고 있는 정보를 따로 저장해서는 안된다.

화면에 글자를 표시 하기 위해, 모델 정보를 전달 받게 될텐데, 그 정보를 유지 하기 위해서 임의로 뷰 내부에 저장하면 안된다는 말이다. 단순히 네모 박스를 그리라는 명령을 받으면, 화면에 표시하기만 하고, 그 화면을 그릴때 필요한 정보들은 저장하지 않는 다는 것이다.

 

2) 모델이나 컨트롤러과 같이 다른 구성 요소를 몰라야 된다.

모델과 같이 자기 자신의 빼고는 다른 요소는 참조하거나 어떻게 동작하는지 알아서는 안된다. 그냥 뷰는 데이터를 받으면, 화면에 표시해주는 역할만 가진다고 보면된다. 각자 이기적이라고 보면 된다. '난 화면에만 그리고 그때 쓴 데이터는 쓰레기 통에 버릴꺼야' 하는 이기적인 녀석 말이다.

 

3) 변경이 일어나면, 변경 통지에 대한 처리 방법을 구현해야만 한다.

모델과 같이 변경이 일어났을때 이에 누군가에게 변경을 알려줘야하는 방법을 구현해야 한다. 뷰에서는 화면에서 사용자가 화면에 표시된 내용을 변경하게 되면 이를 모델에게 전달해서, 모델을 변경해야 할 것이다. 그 작업을 하기 위해 변경 통지를 구현한다.

 

뭐 이쯤 읽었으면 이에 대한 처리 방법을 왜 구현해야 하는지 짐작할 수도 있을것이다. 이는 모델이나 뷰에서 변경이 일어났을때, 이를 컨트럴로에게 알리고, 컨트롤러는 어떻게 처리할지 결정해 다시 다른 구성 요소에서 변경을 알려주는 중재자 같은 역할을 위해 변경 통지 관련 처리 방법을 구현하는 이유이다.

 

 

컨트롤러 (Controller)


모델과 뷰를 연결해 주는 역할을 한다. 주로 비즈니스 로직(문제를 해결하기 위한 과정)이 이 컨트롤러에서 구현되어 있는 것이다. 음식 재료와 음식을 예를 들면, 음식 재료가 있고 이를 조리법을 이용해 음식을 완성한다고 했을때, 음식 재료들은 데이터가 될터이고, 조리법은 컨트롤러, 그리고 완성된 음식은 뷰로 표현할 수도 있을것 같은데... 예를 들고 보니 조금은 부적절하다는 생각도 들지만, 필자의 지식 수준이 여기까지라고 생각하고 글을 읽어주길 바란다.

 

컨트롤러는 다음 규칙을 이해해야 합니다.

1) 모델이나 뷰에 대해서 알고 있어야 한다.

모델이나 뷰는 서로의 존재를 모르고, 변경을 외부로 알리고, 수신하는 방법만 가지고 있는데, 이를 컨트롤러가 중재하기 위해 모델과 그에 관련된 뷰에 대해서 알고 있어야 합니다.

 

2) 모델이나 뷰의 변경을 모니터링 해야 한다.

모델이나 뷰의 변경을 통지 받으면, 이를 해석해서 각각의 구성 요소에게 통지를 해야 하는것입니다.

 

끝맺음

몇 가지 기준을 세우고, MVC 패턴을 이해하고 사용한다면, 왠만한 Framework를 익히는 기초가 될꺼라 생각됩니다. 사실 '좋은 말 한마디가 천금보다 낫다'는 말처럼 개발자에게는 긴 글 보다 한줄의 코드로 이해하기 쉬울 수도 있을텐데....사실 이 글은 스스로에게 알려주는 글이다 보니, 태생적 한계가 존재하게 되었습니다.

 

요즘 기초적인 부분을 정리하면서 드는 생각들은, 수많은 개발 기술들이 세상에 나오고, 활용 되는 것을 보면, 그 내용들은 기초에서 파생되어 발전된 내용이라는 것을 알게됩니다. 기초는 모르고 써도 되지만, 결국 한계에 다다르지요.

 

'개발자' 카테고리의 다른 글

SK Planet CodeSprint 2013 대회  (0) 2013.06.30
SQL Query의 기본  (0) 2013.06.21
POI에서 shiftrow 사용시 hyperlink가 복사 되지 않는 문제  (0) 2013.04.26
Posted by 빌리 :

Hibernate 책 학습

1장 

기본 환경 설정

  - Ant를 이용한 환경 설정

  - Maven-ant task를 이용한 라이브러리 자동 다운로드 

  

2장

1) 자바 클래스와 DB 테이블 사이의 맵핑 문서 작성하기

2) 맵핑 코드를 이용해 자바 객체 클래스 생성 방법

3) 맵핑 코드를 이용해 DB 테이블 스키마 생성 방법

  - hibernate.properties 파일을 이용한 기본 DB접속 설정

  

+ hibernate를 properties 파일을 이용해 설정하는 방법

+ log4j를 properties 파일을 이용해 설정하는 방법

-> 기본적으로 해당 프로그램들이 properties 파일을 클래스패스의 최상의 디렉토리를 검색하기 때문에 클래스패스 최상위에 파일을 넣는다.


+ Ant를 이용한 자바 빌드 방법

+ Ant를 이용한 파일 복사 방법


3장

1) 기본 설정을 hibernate.properties 대신 hibernate.cfg.xml 파일로 대체

  - hibernate.cfg.xml 파일 생성

  - 기존 ant build.xml 파일에 해당 xml 파일 참조하도록 수정

  

2) 자바 객체를 데이터베이스 레코드로 저장하기

3) 데이터베이스 레코드를 자바 객체로 가져오기(1)

  - HQL(하이버네이트의 sQL기반 쿼리언어)를 이용한 데이터베이스 조회

  

4) 데이터베이스 레코드를 자바 객체로 가져오기(2)

  - 네임드 쿼리/네임드 파라미터

  - 자바 소스에서 쿼리 분리 하기(맵핑 XML 파일에 넣기)


4장
1) 테이블 간의 조인 관계를 표현하기 (다 대 다 관계) - 컬렉션(Collection)
  * 다대다 조인 테이블을 하이버네이트에서 자동으로 생성관리함
  * 다른 테이블 데이터는 객체 내에 컬랙션으로 표현되어 추가됨
  - Column에 대한 상세 설정 방법
    -> Index 설정, not null 설정, toString 메소드 설정
    
  - 자바5의 타입세이프티 설정 방법
  
2) 객체에서 컬랙션 데이터 입력하기(연관 테이블에 데이터 입력)
3) 객체에서 컬랙션 데이터 읽어오기(연관 테이블에 데이터 검색)
4) 다대다 관계에 대한 양방향 맵핑 기능 이용(이론)
5) 단순한 조인 관계 표현하기(1 대 다 관계) 
  

5장
1) 느슨한 연관
  - 객체를 실제로 참조하기 전까지 연관된 객체를 로드하지 않는 방식 지원
    -> 하이버네이트 세션이 열려져 있는 상태에서만 작동
2) 순서가 있는 컬렉션 설정 방법

3) 다시 읽어보기!!!

4) 순서있는 연관 컬랙션 삭제에 따른 자동처리
  - 부모 객체와 자식 객체의 종속성 설정
    -> 부모가 삭제되면 자식도 동시에 삭제 가능하도록 설정 가능
    -> 중간에 order관련 값들을 자동으로 변경해줌
  
5) 재귀 연관
  - 트리와 같은 구성
  

6장 사용자 정의 값 맵핑 시키기
: 객체의 하나의 값(property)이 enum 또는 특정 클래스랑 직접 맵핑되어 사용되어 질때
즉 컬럼 하나의 값 또는 두개 이상의 값이 enum 또는 클래스와 맵핑 될 수 있는 방법을 알아본다.

1) 컬럼 값 하나를 enum값과 맵핑 시키기 - UserType
2) 테이블의 여러 컬럼을 클래스와 맵핑 시키기 - 복합사용자타입(CompositeUserType)

- 사용자 정의 값 맵핑은 다음 단계로 개발된다.
  .1-enum 값 또는 클래스 정의하기
  .2-(단일 타입)org.hibernate.usertype.UserType 인터페이스를 구현하는 클래스 작성하기
     (복합 타입)org.hibernate.usertype.CompositeUserType 인터페이스를 구현하는 클래스 작성하기
    -> 구현 클래스는 enum 또는 클래스를 Hibernate에서 어떻게 다룰지 구현
  .3-Class 맵핑 문서(ex:Track.hbm.xml)에 프로퍼티 추가하기
  .4-사용자 정의 클래스 컴파일 하기
  .5-Hibernate Tool을 이용해 코드 생성하기
  .6-모든 소스 컴파일 하기
  .7-Hibernate Tool을 이용해 Schema 생성하기
  
  ** Hibernate Tool이 사용자 정의 클래스를 인식하기 위해선 먼저 사용자 정의 클래스가 컴파일 되어 있어야만 한다.


7장 설정 파일 대신 어노테이션(annotation)을 이용해 설정하기
: 이 경우에는 이전에 만들어 놓았던 legacy 코드가 있을때 이를 어떻게 설정파일 없이
하이버네이트를 적용하는지에 대해 설명 한다. 

- 어노테이션을 이용한 하이버네이트 적용 방법
  .1- annotation 관련 하이버네이트 라이브러리를 얻어온다.
  .2- configuration이 어노테이션을 사용한다고 명시한다.
    -> ant라면 <hibernatetool> 하위에 <configuration> 대신 <annotationconfiguration> 태그를 이용해 config 파일을 지정한다.
  .3- 컴파일 순서를 다음으로 진행 되도록 변경 한다. (스키마를 생성하기 전에 전체 클래스들이 컴파일 되어 있어야 하이버네이트가 클래스를 인식한다.)
      클래스 파일 경로에 xml등 설정 파일 복사 -> 소스코드 컴파일 -> 스키마 생성작업
  .4- hibernate.cfg.xml에 맵핑할 클랙스 목록을 추가한다.
  .5- 맵핑한 클래스에 어노테이션을 추가한다.
  .6- 스키마 생성
  .7- 실행 프로그램에서 config 정보를 읽어오는 클래스를 AnnotationConfiguration 클래스를 사용하도록 변경


8장 크리테리아(Criteria) 쿼리 
: Where 절을 객체의 메소드 호출로 대체 하자. 기존 외부 XML 파일에 쿼리를 빼 놓으면, 쿼리가 실행 되기 전까지 쿼리의 오류를 확인할 수 없는 문제가 발생한다. 이를 해결하기 위해 코드상에 Where 조건을 만들 수 있는 기능을 추가해놓았다. 

- 기본 크리테리아 사용 방법 
  -> 기본 조회
  -> 정렬 
  -> 기본 사용 방법
    .1- Session 에서 createCriteria()를 이용해 criteria 객체 생성
    .2- criteria 객체에 조건 추가
    .3- criteria의 list() 호출

- And / Or 조건 추가하기
  -> OR : Restrictions.disjunction()에 생성되는 객체에 조건 추가
  
- 테이블의 특정 컬럼 값만 얻기
  -> 하나의 컬럼 조회 : hibernate는 String 리스트를 리턴
  -> 여러 컬럼 조회 : 각 컬럼의 타입에 맡는 Object배열 리스트를 리턴
  
- 테이블의 통계 정보 얻기

- 연관 테이블의 조건 걸기
  .1- Session 에서 createCriteria()를 이용해 criteria 객체 생성
  .2- criteria 객체를 이용해 sub criteria 객체 생성하기(생성시 조건을 연결할 연관 클래스를 넘겨줘야됨)
  
- example객체를 이용해 Criteria 객체 생성하기
: criteria를 직접 생성하지 말고, 기존 vo객체를 이용해 검색 하고 싶을때 사용한다.
  .1- VO객체에 검색할 속성 값을 설정해 Example 객체를 생성하기
    ex) VOobj vo = new VOobj();
        vo.setProperty("test");
        Example example = new Example(vo);
  .2- 생성된 example 객체를 critera에 조건으로 추가하기
  
- Criteria 객체 사용할때 조건 추가하는 방법
  .1- Restrictions 팩토리 클래스 이용하기
    ex) criteria.add(Restrictions.le("property", value));
  .2- Property 클래스 이용하기
    ex) criteria.add(Property.forName("property").le(value));
    

9장 HQL 
- HQL 작성방법
- 테이블의 모든 데이터 조회하기
- 프로퍼티와 일부분 선택하기
- HQL을 이용해 정렬 하기
- HQL을 이용해 통계 정보 가져오기
- 네이티브 SQL 이용하기
  -> 기존 쿼리를 Hibernate로 마이그레이션 할때 사용하기 

Posted by 빌리 :

1. Java Message Service(JMS) 소개

Java Message Service(JMS) API는 두개 또는 그 이상의 클라이언트 간에 메세지를 주고 받을 수 있도록 하는 Java Message Oriented Middleware(MOM) API 이다. 


JMS는 Java Platform, Enterprise Edition의 한 부분이며, Java Enterprise Edition(JEE) 기반의 애플리캐이션 컴퍼넌트들 간의 메세지를 생성하고, 전송하고, 전송받고, 읽는 기능을 사용할 수 있게 한다. 분산 처리 시스템에서 다른 컴포넌트 간의 느슨한 결합(Loosely coupled)과 신뢰성있으며, 그리고 비동기적으로 통신할 수 있게 해준다.


메세지 서비스를 제공하는 MOM 시스템에 JMS API를 이용해 접근할 수 있으며, 이를 통해 컴퍼넌트간에 통신을 할 수 있게 만들 수 있는 것이다. 즉, 소프트웨어 애플리케이션 또는 소프트웨어 컴포넌트들 간의 통신 방법을 정의해 놓은 것이다.


Component <-> JMS <-> Component


JMS는 서로 연결된 다양한 컴포넌트 사이에서 다른 컴포넌트가 갖고 있는 자원을 사용할 수도 있고, 네트워크가 연결되지 않은 상태에서도 메세지를 이용하여 필요한 업무를 요청할 수 있다.

2. JMS 사용시 고려 사항

어떤 상황에서 JMS를 사용할 수 있을까? 다음 상황을 고려해 보자.


- 다른 컴포넌트의 인터페이스 정보에 의존하지 않고 컴포넌트가 쉽게 변경되는 서비스를 원할 때

-> RMI와 같은 서비스를 이용해 컴포넌트끼리 통신하려면 인터페이스 정보를 알아야 한다. 또한 인터페이스가 변경 되면 모두 재컴파일 해야 하는 문제가 있다.

- 동시에 모든 애플리케이션이 작동하고 있는 상황에서 특정 애플리케이션이 작동하기를 원할 때

-> 특정 애플리케이션에게만 통신을 하려고 할때이다.

- 애플리케이션 비즈니스 모델을 사용하는 컴포넌트가 다른 곳으로 정보를 전달하고, 바로 응답을 받지 않은 상태에서도 계속 작동하는 것을 원할 때

-> 메소드를 이용해 다른 컴포넌트를 호출하면 반환값을 확인할 때까지 멈춰 있어야만 하는데, 다른 컴포넌트에 메세지만 보내고, 처리 상황에 대해 알지 않아도 될때를 말한다.


3. 구성 요소

- JMS provider

Message Oriented Middleware(MOM)을 위한 JMS 인터페이스 구현체이다. JAVS JMS 구현체 또는 non-java MOM을 구현하고 있다.

- JMS client

- JMS producer/publisher

- JMS consumer/subscriber

- JMS message

- JMS queue

- JMS topic

여러 구독자들(subscribers)에게 메세지를 전달할 수 있는 분산 메카니즘

4. Models

- Point-to-point model

메세지를 지정된 큐(Queue)로 전달하고 큐로부터 메세지를 전달받는 형식이다. 각각의 메세지는 오직 하나의 소비자(consumer)에게 전달된다. 


- Publish/subscribe model

메세지를 지정된 토픽(Topic)으로 전달하고 해당 토픽을 구독하고 있는 독자들에게 메세지가 전달받는 형식이다. 토픽인 큐처럼 클라이언트 에게서 메세지를 전달받으면 가지고 있다가 다른 여러 클라이언트에게 메세지를 전달할 수 있다. 


이 모델은 여러 게시판이 있고, 그 게시판에 글을 쓰면 다양한 사람이 글을 읽을 수 있는 게시판 구조랑 비슷하다. 각각의 게시판은 토픽(Topic)이 되고 게시글들은 메세지(message)가 된다.

-> JMS api는 각 모델에 상관없이 메시지를 보낸 쪽과 받는 쪽에서 모두 같은 코드를 사용할 수 있도록 공통 인터페이스를 가지고 있다.


5. JMS Object

- Administered Object

1) Connection Factory

클라이언트가 JMS 서비스에 연결하기 위한 커넥션을 생성할 때 사용하는 객체

2) Destination

생성된 메세지가 저장되는 저장소로 그리고 메세지가 소비되는 곳으로 사용되는 객체


- Connection

JMS Provider를 이용한 가상의 커넥션을 캡슐화한 객체. 사용하고 나면 close 메소드를 호출해 닫아야 한다. 커넥션은 클라이언트와 Provider 사이에 통신을 하며, 개발자는 커낵션을 이용해 세션(Session)을 생성할 수 있다.

- Session

메세지를 생성하고 소비하는 단일 스레드 단위이며, 트랜젝션 기능을 제공한다.


- Message Producer

목적지에 메시지를 전달하는 역할을 하는 객체로, 세션 객체를 통해 생성한다.

- Message Consumer

목적지에 전달된 메시지를 전달받는 역할을 하는 객체로, 메세지 생산자(Message producer)와 같이 세션 객체를 통해 생성한다.

1) Message Listener

이벤트 핸들러로 비동기식으로 메시지를 전달 받을 때 사용하는 객체로, 메세지 소비자(Message Consumer)에 등록해서 메세지가 전송됐을 때만 처리할 수 있게 해준다.

2) Message Selector

전달 받은 메세지를 필터링 할 필요가 있을때 사용하는 객체로, 메세지 소비자를 생성할때 매개 변수로 메세지 셀렉터를 지정하여 사용한다. 셀렉터를 사용하면 메세지의 헤더(Header)와 프로퍼티(Property)가 일치하는 메세지만 전달 받을 수 있다.


- Message

다른 클라이언트에서 메세지를 보내고, 받아 사용할 수 있도록 사용하는 객체로, non-java MOM에서도 사용할 수 있는 구조를 가지고 있다. 메세지는 헤더, 프로퍼티, 보디로 구성되며, 헤더는 필수로 지정되어야 한다.

1) Message Header

JMS클라이언트와 JMS 제공자(Provider)가 메세지를 확인하고 전달하기 위해 사용하는 값을 가지고 있다.

2) Message Property

헤더에서 제공하는 정보 외에 추가로 필요한 정보가 있을때 사용한다.

3) Message Body

다양한 데이터 타입을 지정해 보낼 때 사용한다. 메시지는 다음 종류가 있다.

-> Text message

-> Map Message

-> Bytes Message

-> Stream Message

-> Object Message


-Exception Handling


6. 예제

-> 추후 작성 


7. 글쓴이 요약

JMS API는 MOM 서비스(Message를 저장하고 보내주는 서비스)에 연결하는 도구이며, 실제 JMS를 쓰기 위해서는 MOM이 시스템 JDNI에 등록되어 있어야 하고, 이를 검색해서 JMS API를 이용해 컴포넌트 간에 메세지를 보내고, 받을 수 있는 식이다.

현재 프로젝트에서 쓰고 있는 예를 보니 이메일 알림 기능을 JMS를 이용해 구현해 사용하고 있다. MOM의 경우 Active MQ를 사용하고, MOM을 스프링 프레임웍에 접근이 가능하도록 등록 한후 내부적으로 JMS API를 메세지를 처리하는 방식으로 사용하고 있다.


8. Reference

Java Message Service
http://en.wikipedia.org/wiki/Java_Message_Service

Java Message Service Tutorial
http://docs.oracle.com/javaee/1.3/jms/tutorial/index.html


9. 별첨

Message-oriented middleware(MOM)은 분산 시스템 사이에 메시지를 보내거나 받을 수 있는 소프트웨어 또는 하드웨어이다.

예를 들어 특정 서비스를 제공하는 소프트웨어가 여러 플랫폼을 개발되었다고 하자. 하나는 안드로이드 어플리케이션(Java), 또 하나는 iOS(Object C) 어플리케이션이다. 그리고 서버는 C언어를 이용해 개발 되었다면, 각각의 어플리케이션은 서버와 통신을 해야만 한다. 이때 서로 통신하기 위해 사용하는 것중에 하나가 MOM인 것이다. 

MOM 프로그램에는 다음과 같은 종류가 있다. 

- SAP Process Integration ESB

- Apache ActiveMQ

- BEA Weblogic (part of the Fusion Middleware suite) and Oracle AQ from Oracle

- EMS from TIBCO

- FFMQ, GNU LGPL licensed

- JBoss Messaging and HornetQ from JBoss

- JORAM, from the OW2 Consortium

- Open Message Queue, from Oracle

- OpenJMS, from The OpenJMS Group

- Solace JMS from Solace Systems

- SonicMQ from Progress Software

- StormMQ, using AMQP

- SwiftMQ

- Tervela

- Ultra Messaging from 29 West (acquired by Informatica)

- webMethods from Software AG

- WebSphere Application Server from IBM, which provides an inbuilt default messaging provider known as the Service Integration Bus (SIBus), or which can connect to WebSphere MQ as a JMS provider[5]

- WebSphere MQ (formerly MQSeries) from IBM


'개발자 > Java Enterprise' 카테고리의 다른 글

Plain Old Java Object(POJO)  (0) 2013.05.30
Posted by 빌리 :

자바 프로그램으로 엑셀이나 워드 문서를 만들기 위해서 보통 POI 라이브러리를 사용한다. 그런데 특정 메소드를 쓰는데 버그가 있길래 이렇게 남겨 둔다. 


환경

poi 3.7 버전


버그 증상

Sheet.shiftRows() 메소드 사용시 Row에 Hyperlink가 포함되어 있어도 해당 정보가 복사되지 않는 문제


임시 해결책

shiftRows 메소드를 사용하기 전에 Hyperlink를 긁어와서 저장한 후 Shift된 위치로 넣어주기


여담

현재 홈페이지에 가보니 3.9버전까지 나오긴 했는데, 라이브러리 버전을 올려주면 다른 곳도 수정할 필요가 생길 수도 있기때문에 그냥 복사 코드만 추가 했다. 

다른 쪽 코드를 보면 라이브러리를 수정하라는데.. 사실 그게 깔끔하긴 한데, 내가 그것까지 손 볼 필요는 없네.


코드

// Copy hyperlink, because it do not support link copy.
Map<Integer, Hyperlink> linkmap = new HashMap<Integer, Hyperlink>();
for (int rr=START_ROW; rr<=sheet.getLastRowNum(); rr++) {
	Row row = sheet.getRow(rr);
	Cell cell = row.getCell(HYPERLINK_COLUMN_INDEX);
	Hyperlink link = cell.getHyperlink();
	if (null != link) {
		linkmap.put(rr, link);
	}
}

// shift row code
sheet.shiftRows(START_ROW, sheet.getLastRowNum(), defectSize);

// Set hyperlink, because it do not support link copy.
for (Integer key : linkmap.keySet()) {
	int rowindex = key + defectSize;
	Row row = sheet.getRow(rowindex);
	Cell cell = row.getCell(HYPERLINK_COLUMN_INDEX);
	Hyperlink link = linkmap.get(key);
	if (null != link) {
		cell.setHyperlink(link);
	}
}

'개발자' 카테고리의 다른 글

SK Planet CodeSprint 2013 대회  (0) 2013.06.30
SQL Query의 기본  (0) 2013.06.21
모델-뷰-컨트롤러(Model-View-Controller MVC)  (2) 2013.05.22
Posted by 빌리 :