VO 와 DTO 는 둘 다 모델에 해당하는 객체로 그 차이는 단순하다.

VO는 불변객체이고, DTO는 가변객체이다.

가변객체는 그냥 getter, setter 가 포함된 bean 을 생각하면 되고

불변객체는 setter를 포함하지 않아서 변화를 허용하지 않는 걸 말한다.

이에 DTO에는 데이터를 변환하는 비즈니스 로직이 포함될 수도 있다.

 

지금까지 DTO 같은 성격의 객체는 실무에서 많이 사용했다. 

그런데 VO 성격의 객체는 거의 사용해본 적이 없다.

불변객체...대체 언제 필요할까? 솔직히 없어도 된다.

그래도 굳이 나눠지는 개념이니 한번 생각해보자.

 

필요하긴 할거다. 접근제한자로 private 도 쓰지 않는가? 

변화를 허용하지 않기 위해 private 로 불변자원을 만든다.

객체도 "내가 처음에 만들어준 데이터 바꾸지 마" 라고 지정할 수 있는거다. 

 

프로젝트에서 VO 같은 사용자 정의 객체가 사용되는 경우를 생각해보자. 

1. DB select 를 담는 경우

2. Controller 파라미터를 담는 경우

 

그 외 비즈니스 로직에서 별도로 사용자 정의 객체가 사용되는 경우도 있겠지만

있다고 하더라도 로직에 의해 값이 유동적으로 변화하는 경우가 많기 때문에 불변인 VO로 볼 수 없다.

 

1번의 경우는 가져온 데이터를 정재하는 경우가 있기 때문에 VO로 적합하지 않다.

Controller 파라미터는 VO가 가능할 거 같다. 

그렇다면 Controller 파라미터를 vo로 해서 가질 수 있는 장점은 뭘까?

장점이 있어야 쓸 거 아닌가..

 

생각나는 장점 중 하나는 해당 VO객체가 거치게 되는 메소드나 클래스가 많을 경우

불변이 보장된다면 추후 시스템 분석이나 버그 디버깅에 도움이 될 수 있다.

일반적으로 빌링 또는 커머스시스템은 굉장히 복잡한 로직을 품고 있는데

로직이 수행되는 중에 값이 변화하지 않는다는 보장만 있다면 해당 경우의 수만 없애는 것만으로도

디버깅에 굉장히 큰 도움이 된다.

 

다만 모델객체를 구현하는 방식에 따라 가변, 불변을 모두 제공할 수 있다.

즉, 일부 데이터만 포함한 생성자에 빌더패턴을 적용하고 수정을 허용할 데이터는

별도로 setter를 만들어 주면 된다.

이렇게 되면 빌더패턴에 포함된 데이터는 불변이 되고(변경하려면 객체를 새로 생성해야 함)

포함되지 않은 데이터는 가변이 된다.

 

그렇다면 또 다시 VO, DTO를 구분하지 않고 이렇게 하나의 객체로 사용할 때 

가질 수 있는 장점은 뭐가 있을까?

가장 먼저 생각나는 것은 로직당 통일된 하나의 모델을 사용할 수 있다는 것이다.

 

예를 들어 유저정보를 수정하는 기능을 아래와 같이 만든다고 생각해보자.

 

변경될 유저정보를 받아서 그 값으로 DB 테이블을 업데이트하고 

정상적으로 업데이트 되었다면 그 값을 다시 리턴하고

업데이트에 실패했다면 DB상 최종데이터를 조회해서 리턴한다.

 

VO, DTO 를 구분할 경우 변경될 유저정보를 받을 UserVO 객체가 필요하고

DB 데이터를 담을 UserDTO 라는 객체가 필요하게 된다.

이러한 VO, DTO 는 비슷한 성격이기 때문에 아무래도 중복코드가 발생할 수 밖에 없다.

하지만 일부에만 빌더패턴을 적용한 객체를 사용할 경우 

UserModel 이라는 하나의 객체로도 충분하게 된다.

 

결국 가변, 불변 기능을 모두 포함한 모델객체를 사용하는게 가장 좋아보인다.

 

테스트 삼아 한번 만들어보자. 유저에 해당하는 모델을 만들 것이다.

 

조건은 아래와 같다.

사용할 데이터는 아이디, 이름, 나이, 등급으로 하고

이 중 아이디, 이름, 나이는 불변이고 등급은 가변으로 할 것이다.

더 나아가 아이디, 이름은 필수값으로 할 것이다.

 

완성된 모델은 아래와 같다.

@Getter
@ToString(exclude={""})
@EqualsAndHashCode(of = {"id", "name", "age"})
public class UserModel {

    @NotBlank(message = "id is required")
    private String id;
    @NotBlank(message = "name is required")
    private String name;
    private int age;
    private String grade;

    @Builder
    private UserModel(String id, String name, int age, String grade){
        Assert.hasText(id, "id is required");
        Assert.hasText(name, "name is required");
        this.id = id;
        this.name = name;
        this.age = age;
        this.grade = grade;
    }

    public void setGrade(String grade) {
        this.grade = grade;
    }
}

마지막으로 이것을 쓰는 것이 단점은 뭐가 있을까?

현재로선 없어보이는데 생각나면 추가 기록하겠다.

 

'밥벌이 > Java' 카테고리의 다른 글

XML파일의 활용  (0) 2015.09.18
BufferedStream 팁  (0) 2015.09.18
Properties 파일 읽기  (0) 2015.09.18
Posted by mypiece
,

Redo와 Undo

밥벌이/Database 2015. 9. 23. 23:06

 - 둘다 복구에 사용된다. Redo의 경우 '다시실행한다' 라는 의미로 제대로 반영되지 못한
   변경사항을 다시 재현하는 역할을 한다. 이를 'roll forward단계'라 한다.
   이때 버퍼 캐시에 최종커밋되지 않은 변경사항까지 모두 복구되기 때문에 이러한 사항들을
   롤백 해야 하는데 이를 Undo라고 하며 '되돌린다' 라는 의미로 'rollback 단계'라고 한다.

 

1. Redo

1) online redo 로그 : 실제 로그버퍼에 버퍼링된 로그 엔트리를 기록, 최소 2개 이상의 파일로

                                구성되며 라운드로빈 방식사용.

2) archived redo 로그 : 라운드로빈 방식에 의해 online redo 로그파일이 재사용 되기 전에 다른 위치로

                                    백업해둔 파일.

▶ Redo로그의 사용 목적
  - Database Recovery : 물리적으로 디스크가 깨지는 등의 문제 발생시 Archived Redo로그가

                                      사용된다.
  - Cache Recovery : 모든 DBMS는 I/O 성능 향상을 위해 버퍼 캐시가 사용되는데 이는 휘발성으로

                                  디스크 상의 데이터 블록에 기록되지 않은 상태에서 인스턴스가 비정상

                                  종료되면 작업내용을 모두 잃게됨.
                                  이에 재기동시 Online Redo 로그에 저장된 기록사항을 읽어 버퍼캐시에만 있고 

                                  데이터 블록에 반영되지 못한 변경사항을 재현함(roll forward단계)
  - Fast Commit : 변경된 메모리 버퍼블록을 딧크 상의 테이터 블록에 기록하는 작업은 Random 엑세스
                          방식으로 이루지기 때문에 느리지만, 로고는 Append 방식으로 기록하므로 상대적으로

                          매우 빠르다. 

  이에 트랜잭션 발생시마다 우선 변경사항을 Append 방식으로 빠르게 로그에 기록하고 
  메모리 블록과 데이터 블록의 동기화는 적절한 수단(DBWR, Checkpoint를 이용해

  나중에 배치처리) 으로 일괄 수행한다.
  이러한 내용들이 Redo로그에 기록되며 오라클은 Redo로그를 믿고 언제든 Recovery

  가 가능한 상태가 되므로 빠르게 Commit을 완료할 수 있다.
  이러한 Fast Commit은 빠르게 트랜잭션을 처리해야 하는 모든 DBMS의 공통적인 메커

  니즘이다.

 

   ▶ Write Ahead Logging : 버퍼 캐시에 있는 블록 버퍼를 갱신하기 전에 먼저 Redo 엔트리를
                                          로그 버퍼에 기록해야 하며, DBWR이 버퍼 캐시로부터 Dirty 블록들을

                                          디스크에 기록하기 전에 먼저 LGWR이 해당 Redo 엔트리를 모두 Redo로그

                                          파일에 기록했음이 보장되어야 한다.
 
2. Undo
 - Undo 세그먼트는 구조적으로 볼 때 데이터를 저장하는 일반 세그먼트와 별반 다르지 않다.
   익스텐트 단위로 확장되고, 빠른 읽기/쓰기를 위해 Undo블록들을 버퍼 캐시에 캐싱하며,
   데이터 유실 방지를 위해 Redo로그에 로깅하는 점도 같다.
   다른 점이라면 저장되는 내용으로, 각 트랜잭션 별로 Undo세그먼트를 할당해주고
   (두 개의 이상의 트랜잭션이 하나의 Undo블록 세그먼트를 할당받아 같이 사용할 수 있음)
   그 트랜잭션이 발생시킨 테이블과 인덱스에 대한 변경사항들을 Undo 레코드 단위로
   Undo세그먼트 블록에 차곡차곡 기록된다.


 ▶ Undo 세그먼트의 사용 목적
  - Transaction Rollback : 트랜잭션에 의한 변경사항을 최종 커밋하지 않고 롤백하고자 할때
                                         Undo 데이터가 사용됨.
  - Transaction Recovery(rollback 단계) : Instance Crash 발생 후 Redo를 이용해서

                                                                'Roll forward단계'가 완료되면 최종커밋되지 않은 변경사항

                                                                까지 모두 복구되는데 이러한 트랜잭션들을 롤백하는데

                                                                사용된다.
  - Read Consistency : DB2, MS-SQL, Sybase 등은 Lock을 통해 읽기 일관성을 구현하지만 오라클은

                                    Undo데이터를 이용해서 이를 구현한다.


 ※ 상세 과정

 트랜잭션을 시작하려면 먼저 Undo 세그먼트 헤더에 있는 트랜잭션 테이블로 부터 슬롯을 할당받아야 하며, 할당받은 슬롯의 헤더에 자신이 현재 Active 상태임을 표시하고서 갱신을 시작한다.
 Active 상태의 트랜잭션이 사용하는 Undo블록과 트랜잭션 테이블 슬롯은 상태정보가 commited로 변경되기 전까지는 절대 다른 트랜잭션에 의해 재사용되지 않는다.
 이때부터 트랜잭션이 발생시키는 데이터 또는 인덱스 블록에 대한 변경사항은 Undo블록에 레코드로서 하나씩 차례대로  기록된다. Undo 세그먼트 헤더는 가장 마지막 Undo 레코드와 Last UBA(Undo Block Address)라는 일종의 포인터로 연결되어 있으며 각 Undo 레코드 간에는 체인형태로 연결되어 있다.
 v$transaction 뷰에 있는 used_ublk와 used_urec 칼럼을 통해 각각 현재 사용중인 Undo블록 개수와 Undo레코드 양을 확인할 수 있다. 인덱스가 전혀 없는 테이블이라면 한건을 갱신할때마다 used_urec 값이 하나씩 증가하지만 인덱스가 있다면 인덱스의 갱신내용까지 레코드로 추가된다.

 

 ▶ 블록 헤더 ITL 슬롯
  - Undo 세그먼트 헤더에 트랜잭션 테이블 슬롯이 있다면 각 데이터 블록과 인덱스 블록헤더에는

     ITL슬롯이 있다.
  - 특정 블록에 속한 레코드를 갱신하려면 먼저 블록 헤더로부터 ITL슬롯을 확보해야 한다.
    거기에 트랜잭션 ID를 기록하고 현재 해당 트랜잭션이 Active 상태임을 표시한 후에 블록 갱신이

    가능하다.
  - 오라클에서는 ITL슬롯 부족으로 인해 트랜잭션이 블로킹 되는 현상을 최소화할 수 있도록 3가지

     옵션을 제공한다.
    테이블을 생성할 때 initrans, maxtrans, pctfree 파라미터를 지정한다.
    각각 ITL슬롯 초기할당 개수, ITL슬롯 최대할당 개수, ITL슬롯 할당에 사용될 수 있는 예약된 데이터

    공간이다.

 

 ▶ Lock Byte
  - 블록헤더에서 블록으로 내려오면 Lock Byte에 대해 알아볼 필요가 있다.
    오라클은 레코드가 저장되는 로우마다 그 헤더에 Lock Byte를 할당해서 해당 로우를 갱신 중인

    트랜잭션의 ITL슬롯번호를 기록해둔다. 이것이 로우단위 Lock이다.
  - 레코드를 생신하려고 할 때 대상 레코드의 Lock Byte가 활성화 되어 있으면 ITL 슬롯을 찾아가고,
    ITL슬롯이 가리키는 트랜잭션 테이블 슬롯을 찾아가서 아직 Active 상태라면 트랜잭션이 완료될때까지

    대기한다.
  - ITL 슬롯에 기록되는 내용중 UBA(Undo Block Address)가 트랜잭션에 의해 발생한 변경 이전

    데이터가 저장된 Undo 블록주소를 가리키는 포인터 정보로 '문장수준 읽기 일관성' 구현을 위해

    CR Copy를 생성해서 과거버전으로 되돌리려고 할때 사용된다.   
 

'밥벌이 > Database' 카테고리의 다른 글

[Oracle] null 값 순위변경  (0) 2015.09.18
[Oracle] 제약조건 활성화/비활성화  (0) 2015.09.18
[Oracle] 외래키 옵션  (0) 2015.09.18
[Oracle] MERGE 구문  (0) 2015.09.18
[Oracle] 계층쿼리  (0) 2015.09.18
Posted by mypiece
,

어노테이션을 사용하지 않고, xml에서 autowire 지시자를 통한 의존성 주입을 위해서는
해당 클래스에 주입을 원하는 변수의 setter 메소드가 반드시 존재해야 한다.
(어노테이션을 사용할 경우 setter 메소드가 별도로 필요하지 않다.)


대부분의 서적이나 인터넷 자료들을 보면 이러한 setter메소드의 필요성은 누락하고
autowire의 옵션별 설명만을 서술한다. 이러한 서술자료를 보면 아래와 같다.


autowire 옵션
 - byName : 프로퍼티의 이름과 같은 이름을 갖는 빈 객체를 설정
 - byType : 프로퍼티의 타입과 같은 타입을 갖는 빈객체를 설정
 - constructor : 생성자 파라미터 타입과 같은 타입을 갖는 빈객체를 생성자에 전달
 - autodetect : constructor 방식을 먼저 적용하고, byType 방식을 적용하여 의존 객체를 설정


설명만 보면 간단하게 이해할 수 있다.

하지만 실제로 의존성 주입을 통한 어플리케이션을 구현하려고 하면
setter 메소드가 없다면 절대 의존성주입은 이루어지지 않는다.
더군다나 setter 메소드에 대한 설명없이는 어노테이션을 사용하지 않는 의존성주입은 설명되기 어렵다.

예를 들어 일반적으로 의존성주입을 위한 클래스를 만들라고 하면 아래와 같을 것이다.

------------------------------------------------
...java...

public class Service{

...

  protected DAO dao;
  public setDAO(DAO dao){
    this.dao = dao;
  }

...


}

...xml...
<bean id="service" class="com.Service" autowire="byName"/>
<bean id="dao" class="com.DAO"/>
--------------------------------------------------------

위와 같이 설정되었다고 한다면 Service 클래스의 맴버변수인 dao에는 com.DAO 의 인스턴스가 주입될 것이다.
autowire 설명만 본다면 당연한 결과이다.
byName : 프로퍼티의 이름과 같은 이름을 갖는 빈 객체를 설정
자 그렇다면 Service 클래스에서 맴버변수인 dao의 이름을 dao2로 바꾼다면 어떻게 될까?
dao2라는 빈이 존재하지 않기 때문에 주입이 되지 않을까?
결과는 아무런 이상이 없이 정상적으로 의존성주입이 수행된다.
단순히 autowire 옵션에 대한 설명만 본다면
이러한 현상은 어떻게 설명될 수 있을까?


어노테이션을 사용하지 않는 의존성 주입 방식의 경우
기본적으로 해당 클래스에 정의한 setter 메소드에 의해 의존성 주입이 수행된다.
결국 의존성 주입시 바라보는 것은 해당 프로퍼티의 이름이 아닌
해당 프로퍼티의 setter 메소드라는 말이다.
autowire="byName" 으로 설정할 경우 set을 접두어로 사용하는 메소드 중에서
접미어로 지정된 명칭을 기준으로 빈 객체를 찾게된다.


결국 byName 에 대한 설명은 아래와 같이 바뀌어야 한다.
byName : 프로퍼티의 setter 메소드의 set을 제외한 이름과 같은 이름을 갖는 빈 객체를 설정

byType의 경우도 마찬가지이다.
프로퍼티의 타입이 아니라 정의된 setter 메소드의 파라미터 타입에 의해
의존성 주입이 결정된다. 이러한 이유로  byType의 경우 setter메소드의 명칭은
set만 접두어로 사용되면 되고 이후 접미어는 어떤 것이 와도 상관없다.
어차피 의존성주입시에는 파라미터의 타입만 확인하기 때문이다.


이렇게 어노테이션을 사용하지 않는 의존성주입의 경우
단순히 해당 맴버변수의 명칭이나 타입이 아닌 해당 setter 메소드의
관점에서 바라봐야만 명확한 정의를 내릴 수 있다.

 

Posted by mypiece
,
Spring 에서 객체변수의 인스턴스를 얻어오는 방식은 3가지 방법이 있다.


1. 자동묶기 방식
 - 해당 bean을 컨테이너에 등록시 autowire 지시자를 이용하여 자동묶기를 한다.
   일반적으로 xml파일과 java파일에 아래와 같이 코딩한다.
   해당 java소스에는 반드시 set메소드가 존재해야 한다.
   단, constructor 방식을 사용할 경우에는 생성자가 주입을 담당하기 때문에
   굳이 set메소드가 존재하지 않아도 된다.
  
  1.1. application-context.xml
    ...생략...
    <bean id="sqlMapClientUtil" class="com.local.util.SqlMapClientUtil" autowire="byName">
    </bean>
    ...생략...

  1.2. SqlMapClientUtil.java
    ...생략...
    private SqlMapClient sqlMapClient;
   
    public void setSqlMapClient(SqlMapClient sqlMapClient) {
    this.sqlMapClient = sqlMapClient;
    }
    ...생략...

*   기타 autowire 옵션 

   1) byName : 프로퍼티의 이름과 같은 이름을 갖는 빈 객체를 설정
   2) byType : 프로퍼티의 타입과 같은 타입을 갖는 빈객체를 설정
   3) constructor : 생성자 파라미터 타입과 같은 타입을 갖는 빈객체를 생성자에 전달

   4) autodetect : constructor 방식을 먼저 적용하고, byType 방식을 적용하여 의존 객체를 설정
    

byName를 제외한 방식의 경우 2개 이상의 bean이 검색될 경우 아래와 같은 Exception을 발생시키게 된다.
org.springframework.beans.factory.UnsatisfiedDependencyException                    

이러한 경우 autowire 방식이 아닌 직접지정 방식을 사용해야 한다.
      
타입에 의한 자동와이어링은 타입이 같은 빈이 두 개 이상 존재하는 경우에는 적용되지 못한다.

이름에 의한 자동와이어링은 프로퍼티 이름과 빈의 아이디만 비교하면 되기 때문에 상대적으로 빠르다.

하지만 타입을 비교하는 것은 String으로 된 이름을 비교할 때보다 느리다. 

게다가 자동와이어링은 빈의 모든 프로퍼티에 일괄 적용되어 대상이 아니어도 비교하는 작업이 일어나게 된다.​

 



2. 직접 지정방식

 - 해당 bean을 컨테이너에 등록시 주입할 property를 아래와 같이 지정한

.

  2.1. application-context.xml
    ...생략...
    <bean id="sqlMapClientUtil" class="com.local.util.SqlMapClientUtil">
      <property name="sqlMapClient" ref="sqlMapClient"/>
    </bean>
    ...생략...

  2.2. SqlMapClientUtil.java
    ...생략...
    private SqlMapClient sqlMapClient;
   
    public void setSqlMapClient(SqlMapClient sqlMapClient) {
    this.sqlMapClient = sqlMapClient;
    }
    ...생략...
    
 * 생성자 방식을 사용할 경우 아래와 같이 지정한다.
    <constructor-arg type="java.lang.String" index="0">
        <value>config/service/serviceContext-config.xml</value>
    </constructor-arg>
    인자의 순서와 타입에 주의하도록 한다.
    java 다형성에 의해 생성자가 여러개 정의되어 있는 경우
    인자의 순서와 타입에 의해 생성자가 결정된다.

 


 

 

3. bean으로 등록되지 않은 클래스에서 동적 얻어오는 방식
 - 컨테이너에 등록된 bean에 객체를 주입할때 위와 같은 방식으로 할 수도 있지만
   bean으로 등록하지 않은 유틸클래스에서 해당 객체를 주입할 필요가 있을때가 있다.
   유틸클래스로 사용될 클래스를 모두 bean 컨테이너에 명시하고, autowire를 하던지
   직접 지정하는 것도 한 방법이다.
   하지만 지금부터 소개하는 아래의 방식이 더 깔끔한 방식 임을 알고 있으면 된다.
   아래의 방식은 application-context 자체를 객체로 얻어서 해당 객체에서 관리되는

   bean객체를 얻어올 수 있다는 점을 이용한다.
  
   그렇기 때문에 프록시 역할을 하게될 application-context 객체를 얻어오는 클래스를

   구현하고 최소한 해당 클래스는 bean으로 등록되어 있어야 한다.
  
  3.1. application-context 객체를 얻어올 클래스 구현.
    - ApplicationContextAware 인터페이스를 구현한 클래스여야 한다.
      해당 인터페이스에는 아래와 같은 1개의 메소드가 존재한다.
     
      public abstract interface ApplicationContextAware
      {
        public abstract void setApplicationContext(ApplicationContext paramApplicationContext)
          throws BeansException;
      }
     
      해당 메소드를 구현해서 application-context 객체를 구하면 된다.
      아래는 해당 인터페이스를 구현한 실제 클래스 예이다.
    
      ...생략...
      public class ApplicationContextUtil implements ApplicationContextAware{
    
        private static ApplicationContext context;
       
        public void setApplicationContext(ApplicationContext ac)throws BeansException {
        ApplicationContextUtil.context = ac;
       }
       
       public static ApplicationContext getContext(){
        return ApplicationContextUtil.context;
       }
      ...생략...

  3.2. 컨테이너 등록
    - 위에서 구현한 ApplicationContextUtil 을 bean으로 등록한다.
    
     <bean id="applicationContextUtil" class="com.local.util.ApplicationContextUtil" autowire="byName">
     </bean>
     
  3.3. 유틸 클래스에서 ApplicationContextUtil 을 통해 객체를 주입한다.
    - 아래와 같이 application-context 객체에서 getBean()메소드를 통해 컨테이너에 존재하는 bean 객체를 얻어올 수 있다.
  
      localeResolver = (LocaleResolver)ApplicationContextUtil.getContext().getBean( "localeResolver"); 

 

Posted by mypiece
,

스프링에서는 모든 서블릿에서 사용될 공유자원을 DI개념으로 사용하기 위해
일반적으로 ContextLoader를 구현한 위 두개의 클래스를 제공한다.
이는 일반적으로 web.xml에 ContextLoaderListener 또는 ContextLoaderServlet 중 하나를
선택적으로 정의해서 사용한다.


목적과 기능은 동일하나 Servlet 2.2 나 2.3 에서는 ContextLoderServlet을,

Servlet 2.4 이상부터는 ContextLoaderListener를 사용하는 것이 권장된다.

백워드 호환을 위해 ContextLoaderServlet를 사용하는게 좋다고 하지만
상황에 따라 명시적으로 선택해서 사용하는 것이 좋을 듯 하다.

WAS에서 지원하는 Servlet버전을 확인하여 그에 맞게 사용하도록 하자.

참고로 Tomcat5.5에서 제공하는 Servlet버전은 2.4 이므로

ContextLoaderListener를 사용하면 되겠다.


각각 선언방법은 아래와 같다.

ContextLoaderListener

 <listener>
  <listener-class>
   org.springframework.web.context.ContextLoaderListener
  </listener-class>
 </listener>


ContextLoderServlet

 <servlet>
  <servlet-name>ContextLoaderServlet</servlet-name>
  <servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
 </servlet>


주의할 점은 ContextLoaderServlet를 선언하여 사용할 경우에는
반드시 <load-on-startup>을 기타 명시적인 서블릿보다 적게 지정하여
제일먼저 로드되도록 해야 한다. ContextLoaderListener는 listener로 지정되기 때문에

굳이 지정하지 않아도 서버가 구동될때 제일먼저 로드된다.


ContextLoader에서 사용될 자원의 경우 일반적인 Servlet과 마찬가지로
web.xml에서 param 지정을 통해 명시할 수 있다.
일반적인 param이 아닌 Context param으로 아래와 같이 명시한다.


<context-param>
 <param-name>contextConfigLocation</param-name>
 <param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>


위에서 <param-value>/WEB-INF/applicationContext.xml</param-value> 절은

생략할수도 있는데 이럴 경우 /WEB-INF/applicationContext.xml파일을

기본 설정파일로 사용하게 된다.

<param-value>을 지정할 경우 xml파일의 경로는 물론이고, 파일명도 원하는대로

선언할 수 있다. 프로젝트 성격에 따라 경로는 달라질 수도 있고, 추후 유지보수를 위해서도

위와 같이 명시적으로 선언하는 것을 추천한다.


이렇게 구현된 ContextLoader는 기타 DispatcherServlet의 부모 역할을 하게 되며
ContextLoader의 모든 자원은 DispatcherServlet에서 활용이 가능하게 된다.
단, ContextLoader에서 자식인 DispatcherServlet의 자원을 이용하는 것은 불가능하다.

Servlet에서의 initParam 자원 취득방식은 다음과 같다.
기본적으로 해당 DispatcherServlet에서 자원을 검색하게 되고
없다면 부모인 ContextLoader에서 자원을 검색하게 된다.
만약 ContextLoader에도 해당 자원이 없다면 Exception을 발생시키게 된다.

Posted by mypiece
,

XML파일의 활용

밥벌이/Java 2015. 9. 18. 22:21

1. xml 파일 생성하기

- 일반적으로 xml 파일은 정적인 설정정보를 저장하기 위한 용도로 사용되며 

어플리케이션에서 해당 정보를 읽어서 사용한다. 

이러한 이점으로 인해 자주 변경되야 하는 설정정보의 경우 xml파일로 작성하여 

수정이 있을 때 별도의 소스 컴파일 과정없이 확장이 가능하게 된다.

다수의 설정정보를 저장하는 만큼 파일 내에서 필요한 데이터를 

효율적으로 파싱하기 위해 기본적인 구조는 트리구조를 가지고 있다.

 

기본적인 작성형식은 아래와 같다.

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

<movieList>

<movie>

<name>신의한수</name>

<creator>조범구</creator>

<genre>액션</genre>

</movie>

<movie name = “트랜스포머”>

<option name = “creator">마이클베이</option>

<option name = “genre”>SF</option>

</movie>

</movieList>

  

- <?xml version=”1.0” encoding=”UTF-8”?> 의 경우 해당 파일이 xml 파일임을 

명시하는 것으로 반드시 최상단에 들어가야 한다. encoding 값 설정의 경우 선택적이긴 

하지만 한글을 사용할 것이라면 위와 같이 UTF-8로 지정해 주는 것이 좋고, 대부분 그렇게 사용한다.

* 주의할 점은 XML 파일에서는 대소문자를 철저히 구분하므로 

위 예시처럼 대소문자를 가리지 않거나 “”를 생략한다면 에러가 발생한다.

 

- 아랫 줄 부터는 실제 사용할 정보를 기술한다. 

<movieList></movieList>와 같이 반드시 종료태그를 기술해야 한다.

 

- <movieList> 와 같이 가장 바깥쪽에 있는 태그를 Root Element라고 하며 

반드시 1개만 존재할 수 있다. 1개 이상 존재한다면 읽어올 때 에러가 발생한다.

 

-  아래로 보이는 <movie> 태그로 2개의 영화에 대한 정보를 기술하였다.

정보 기술의 경우 첫번째 영화(신의한수)처럼 단순노드만으로도 구성이 가능하며 

두번째 영화(트랜스포머)처럼 속성을 포함한 노드로도 구성이 가능하다.

 

- 처음에 말했듯이 XML은 기본적으로 트리구조를 가지고 있기 때문에 

각 정보에 접근하는 방식은 node와 element로서 접근하게 된다. 

node는 단순히 해당 경로의 접점을 의미하며 element의 경우 경로와 

요소를 포함한 객체를 의미한다. 

이러한 이유로 위 xml에서 '신의한수' 라는 정보를 얻고자 한다면 

<move> -> <name> 와 같이 node만으로 접근하고 해당 텍스트를 얻어오면 되지만 

'트랜스포머'라는 정보를 얻고자 한다면 <movie> -> <option> node로 접근한 뒤 

element로 형변환한뒤에 name 속성에 해당하는 을 얻어와야 한다.

 

 



2. xml 파일 읽기
- 기본적으로 XML 파일을 읽기 위해서는 아래의 과정을 거친다.

 모든 방식의 접근방식을 기술한 것이 아니라 본인이 활용하거나 습득한 정보만

나열되어 있기 때문에 하나의 예시로써 활용하고 추가로 필요한 부분은 검색 또는 API를 참고해야 할 것 같다.


읽어올 XML 파일의 내용은 아래와 같다.

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

<confList>

<confResource name = “ExcelCompare”>

<option name = “basePath”>d:/업무/요청자료/</option>

<option name = “target_fileName”>전체 사용자목록.xls</option>

<option name = “standard_fileName”>A팀 현재 사용자목록.xls</option>

<option name = “result_fileName”>A팀 정상 사용자목록.xls</option>

</confResource>

<confResource name = “...”>

...

</confResource>

</confList>


해당 설정파일의 경우 ‘A팀 현재 사용자목록’ 중 ‘전체 사용자목록’에 포함되지 않는 사용자들을 비교하여

삭제한 뒤 결과 엑셀파일을 새로 생성하는 자바어플리케이션 개발시 사용되었다.
추후 <confResource> 태그를 하단에 추가하여 name 속성에 각각의 프로세스명을 기입하여

다른 자바어플리케이션 개발시에도 함께 활용할 계획이었다.



2.1. Document 객체 생성


DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();


// classpath 상에 존재하는 설정파일을 읽어올 때
InputStream is = getClass().getResourceAsStream(“/conf/DestinyConf.xml”);
Document d = db.parse(is);


/*
classpath 상에 존재하지 않는 설정파일을 읽어올 때
File confFile = new File(“d:/conf/DestinyConf.xml”);
Document d = db.parse(confFile);
보이는 것 처럼 DocumentBuild의 parse() 메소드는 오버로딩 되어있어 InputStream, File 중 아무거나 받을 수 있다.
*/


d.getDocumentElement().normalize();



2.2. Root Element 값 얻기


d.getDocumentElement().getNodeName();



2.3. 설정파일 리스트 얻어오기


NodeList confList = d. getElementsByTagName (“confResource”);

- confResource 라는 태그명을 가지는 모든 node 리스트를 얻어온다.



2.4. 설정파일 리스트에서 현재 프로세스에서 사용할 설정파일에 맞는 자원 찾기

for(int i = 0; i < confList.getLength(); i++){
/* confResource라는 태그명을 가진 node를 순차적으로 돌면서 해당 프로세스의 설정정보로 지정된 node를 찾는다. */


 Element confResource = (Element)confList.item(i);
 /* 단순히 node로 접근하는 것이 아닌 name속성을 체크해야 하므로 Element 로 변환한다. */


 if(confResource.getAttribute(“name”).equals(“ExcelCompare”)){
 /* name 속성이 해당 프로세스인 것을 찾는다. */


  NodeList confResourceList = confResource.getElementsByTagName(“option”);
  /* 해당 노드 안에는 프로세스에서 사용할 option 값들이 여러 노드로 분배되어 있기 때문에

      option 태그를 가지는 node들로 리스트를 생성한다. */


  for(int j = 0; j < confResourceList.getLength(); j++){
   /* 해당 node 리스트를 돌면서 필요한 설정값을 읽어서 설정한다.*/


   Element option = (Element)confResourceList.item( j);
   String optionName = option.getAttribute( "name");


   if(optionName.equals( "basePath")){
    basePath = option.getTextContent(); //해당 node의 텍스트 자원을 얻어온다.
   }else if(optionName.equals("target_fileName")){
    target_fileName = option.getTextContent();
   }else if(optionName.equals( "standard_fileName")){
    standard_fileName = option.getTextContent();
   }else if(optionName.equals("result_fileName")){
    result_fileName = option.getTextContent();
   }
  }
 }
}

'밥벌이 > Java' 카테고리의 다른 글

VO, DTO 구분해서 사용해야 할까?  (0) 2020.07.16
BufferedStream 팁  (0) 2015.09.18
Properties 파일 읽기  (0) 2015.09.18
Posted by mypiece
,

BufferedStream 팁

밥벌이/Java 2015. 9. 18. 22:20

BufferedStream 의 경우 입출력 결과를 버퍼에 담아놨다가 입출력을 수행하기 때문에
버퍼링 기능을 하게 되고 건건이 입출력하는 것 보다 좋은 성능을 보여준다.

그런 이유로 JAVA에서 Stream을 사용할 경우 그대로 사용하지 않고

BufferedStream과 같은 보조스트림을 구현해서 사용하게 된다.

이때 불필요한 코딩을 줄일 수 있는 팁이 있다.

 

1. BufferedStream을 사용하고 close를 통해 Stream을 닫을 경우

기반이 되었던 Stream은 별도로 close 해줄 필요가 없다.

BufferedStream의 close()메소드에서 내부적으로 기반스트림에 대한

close()메소드를 수행하기 때문이다.

 

2. BufferedStream 의 경우 버퍼가 가득찼을 때만 출력소스에 출력을 하기 때문에

출력이 끝난 뒤 반드시 flush() 해주어 마지막에 버퍼에 있는 모든 내용이 출력될 수 있도록 해야한다.

하지만 출력뒤 바로 Stream을 닫아버리는 상황이라면 close()만 수행하면 되며,
별도로 flush()를 수행할 필요는 없다.
BufferedStream의 close()의 경우 조상인 File Stream의 close()를 그대로 상속받고
File Stream의 close()는 내부적으로 flush()를 수행한다음 close()를 수행하기 때문에
flush()를 수행하는 시점이 Stream을 끝내는 상황이라면 close()만으로
충분하다는 것이다. 

 

'밥벌이 > Java' 카테고리의 다른 글

VO, DTO 구분해서 사용해야 할까?  (0) 2020.07.16
XML파일의 활용  (0) 2015.09.18
Properties 파일 읽기  (0) 2015.09.18
Posted by mypiece
,

1. classpath 상에 파일 불러오기

  

test.properties 파일에는 아래와 같은 내용이 들어있다고 가정함.

key=/com/test/key

 

//step1 : Stream 연결          
InputStream is = getClass().getResourceAsStream("/com/test/test.properties");

/* or Reader

Reader reader = new InputStreamReader(getClass().getResourceAsStream("/com/test/key") );

*/

 

//step2 : 연결된 Stream을 통해 Proerties 객체 load

Properties prop = new Properties();
prop.load(is);


//step3 : 파일에 key 에 값을 가져온다

prop.get("key");

 

※ 동일한 Properties파일이 JAR파일과 Classes에 각각 존재한다면 Classes에 있는 파일을 읽어들인다.

 


2. classpath 가 아닌 외부폴더에서 읽어오기

 

File propFile = new File(“d://com/test/test.properties”);
BufferedInputStream bi = new BufferedInputStream(new FileInputStream(propFile));
Properties prop = new Properties();
prop.load(bi);
prop.get("key");​

 

'밥벌이 > Java' 카테고리의 다른 글

VO, DTO 구분해서 사용해야 할까?  (0) 2020.07.16
XML파일의 활용  (0) 2015.09.18
BufferedStream 팁  (0) 2015.09.18
Posted by mypiece
,

Oracle에서는 정렬시 null값을 제일 마지막에 위치시킨다.(ms-sql은 반대) 
null 값을 제일 큰 값으로 인식한다는 말이다.

즉, 아래와 같다.


select *
from
(
 select 'aaa' col_1 from dual
 union all
 select 'bbb' from dual
 union all
 select null from dual
)
order by col_1;




​-----------------------------------------------------------------


하지만 order by에 옵션을 주어 이러한 null값에 순위를 바꿔줄 수가 있다. 


select *
from
(
 select 'aaa' col_1 from dual
 union all
 select 'bbb' from dual
 union all
 select null from dual
)
order by col_1 nulls first;


위와 같이 설정하게 되면 null 값이 제일 위에 위치하게 된다.
주의할 점은 해당 옵션은 null이 아닌 나머지 데이터 정렬에는 어떠한 영향도 주지 않는다.
단지 null값의 순위를 '처음' 또는 '끝'으로 바꿔준다. 

'밥벌이 > Database' 카테고리의 다른 글

Redo와 Undo  (0) 2015.09.23
[Oracle] 제약조건 활성화/비활성화  (0) 2015.09.18
[Oracle] 외래키 옵션  (0) 2015.09.18
[Oracle] MERGE 구문  (0) 2015.09.18
[Oracle] 계층쿼리  (0) 2015.09.18
Posted by mypiece
,
1. alter table 테이블명 disable constraint 제약조건명;
2. alter table 테이블명 enable validate constraint 제약조건명;
3. alter table 테이블명 enable novalidate constraint 제약조건명;
 
1번의 경우 제약조건 비활성화한다.

2, 3번의 경우 제약조건을 활성화시키라는 건데

기존데이터의 제약조건 위배 여부를 체크할지 말지에 따라 다르게 사용된다.

2번의 경우 제약조건 활성화시 기존에 있던 데이터의 제약조건 위배사항도 함께 체크하여
위배되는 데이터가 있을시 활성화되지 않는다.
3번의 경우 기존에 있던 데이터의 제약조건 위배사항은 체크하지 않기 때문에
활성화하려는 제약조건에 위배되는 기존데이터가 있더라도 정상적으로 활성화가 가능하다.


일반적으로 정합성을 지키기 위해서는 2번을 사용해야 할 것 같지만
3번을 사용하는 경우는 언제인지 모르겠다...

disable도 기존데이터 체크여부를 지정할 수 있지만 novalidate만 가능하다.
아무것도 지정하지 않으면 novalidate가 기본값이기 때문에 명시적으로 선언이 가능하나
validate의 경우 구문에러를 발생시킨다. 

'밥벌이 > Database' 카테고리의 다른 글

Redo와 Undo  (0) 2015.09.23
[Oracle] null 값 순위변경  (0) 2015.09.18
[Oracle] 외래키 옵션  (0) 2015.09.18
[Oracle] MERGE 구문  (0) 2015.09.18
[Oracle] 계층쿼리  (0) 2015.09.18
Posted by mypiece
,