메뉴 바로가기 검색 및 카테고리 바로가기 본문 바로가기

한빛출판네트워크

IT/모바일

하이버네이트가 당신의 id를 관리하지 못하도록 하라

한빛미디어

|

2007-03-02

|

by HANBIT

13,910

제공 : 한빛 네트워크
저자 : James Brundege
역자 : 박찬욱
원문 :http://www.onjava.com/pub/a/onjava/2006/09/13/dont-let-hibernate-steal-your-identity.html?page=1

기업용 자바 애플리케이션은 흔히 데이터가 자바 객체와 관계형 데이터베이스 사이로 빈번하게 이동한다. 이것을 가능하게 하는 몇 가지 방법으로, 직접 작성하는 SQL 코드부터 하이버네이트와 같은 정교한 객체-관계 매핑(object-relational mapping : ORM) 솔루션까지 넓은 범위에 퍼져있다. 어떤 기술을 사용하든지에 관계없이, 일단 자바 객체에 데이터베이스 객체 동일성(identity)을 주어 영속성을 부여하기 시작하면 이 문제는 복잡하고, 관리하기 힘든 주제가 된다. 이런 문제의 가능성은 데이터베이스에서 같은 행을 참조하는 두 개의 다른 객체를 생성할 때 나타난다. 이 객체들을 다루기 위해서, 당신은 올바르게 equals()와 hsahCode() 메소드를 당신의 영속 객체(persistent objects)에 구현해야 하지만, 이 메소드들의 올바른 구현은 처음 생각만큼 만만치가 않다. 설상가상으로, 전통적인 지식으로는 새로운 프로젝트에서 대부분 실질적인 해결책을 끌어낼 수 없다.

이 문제는 가상 머신(virtual machine : VM)에서의 객체 동일성과 데이터베이스에서의 객체 동일성 사이의 차이에서 충돌한다. 가상머신에서 당신은 객체를 위한 ID를 얻어올 수 없고, 단순히 객체에 대한 직접적인 참조만을 가지고 있다. 이 이면에서 가상 머신은 객체를 참조하기 위해서 실제로 8바이트의 ID를 할당하고 있다. 여기서 문제는 당신이 데이터베이스의 객체를 영속화 할 때 시작한다. Person 객체를 만들고 이 객체를 데이터베이스(person1)에 저장해보자. 당신의 코드 다른 곳에서 Person의 데이터를 읽어 들이고, 새로운 Person 객체를 생성했다.(person2) 지금 당신은 메모리상에서 두 개의 객체를 가지고 있는데, 이 객체는 데이터베이스에서 같은 행으로 매핑 되어 있다. 한 객체 참조는 단순히 하나이거나 혹은 다른 것임을 가리키고 있지만, 우리는 이 객체들이 실제로는 같은 엔터티라는 것을 보여줄 방법이 필요하다.

자바에서 객체 동일성은 equals() 메소드에 의해(그리고 hashCode() 메소드와 연계돼서) 정의된다. equals() 메소드는 같은 인스턴스인 것에 개의치 않고, 두 개의 객체가 같은 엔터티를 나타내는지를 결정한다. 또한 hashCode() 메소드는 동일한 모든 객체가 동일한 해시코드를 리턴하기 때문에 관련이 있다. 기본적으로 equals() 는 객체의 참조를 비교한다. 객체는 자기 자신과 동일하지만, 다른 인스턴스와는 동일하지 않다. 영속 객체가 되려면, 데이터베이스에서 같은 행을 의미하는 객체들이 항상 동일하게 고려되기 위해, 이 두 메소드를 오버라이드 하는 것이 매우 중요하다. 이것은 특히 자바 콜렉션(Sets, Maps, Lists)의 정확한 작동을 위해 중요하다.

equals(), hashCode() 구현하기 위한 다른 방법을 보여주기 위해서, 데이터베이스에서 영속성을 가지기를 원하는 간단한 Person 객체를 생각해보자.
public class Person {
    private Long id;
    private Integer version;

    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public Integer getVersion() {
        return version;
    }
    public void setVersion(Integer version) {
        this.version = version;
    }
    // person-specific properties and behavior
}
이 예제에서, 우리는 id 필드와 version 필드를 모두 가지고 있는 가장 좋은 사례를 따랐다. id는 데이터베이스에서 주키로서 사용되는 값을 가지고 있고, version은 0으로 시작되며, 객체가 업데이트 될 때마다 증가하게 된다.(version은 동시 업데이트 문제를 피하는데 도움을 준다.) 더 명확하게 이해하기 위해, 이 객체를 데이터베이스에 저장해 영속성을 부여하기 위한 하이버네이트 매핑 파일을 보자.



  

    
      
        PERSON_SEQ
      
    

    

    

  

하이버네이트 매핑 파일은 Person의 id 필드가 데이터베이스의 ID인 것을 가리키고 있다.(즉, PERSON 테이블에 주키) id 태그 내에 있는 속성인 unsaved-value="null" 은 하이버네이트가 Person 객체가 이전에 저장되었는지 아닌지를 결정하기 위해서 id 필드를 사용한다는 것을 알려준다. ORM 프레임워크는 반드시 SQL INSERT나 UPDATE을 이용해 객체가 저장되었는지를 알기 위해서 구별할 수 있도록 만들어야 한다. 이 경우 하이버네이트는 새로 생성된 객체를 null로 할당해서 시작하고, 처음 저장할 때 id 값이 할당 된다. 또한 generator 태그는 하이버네이트가 처음 저장되는 객체에 할당할 id를 어디서 받아오는지를 의미한다. 이 경우, 하이버네이트는 유일한 ID들의 출처로 데이터베이스 시퀀스를 사용하고 있다. 마지막으로 version 태그는 하이버네이트가 Person 객체의 version 필드를 동시성 운영을 위해서 사용한다는 것을 의미한다. 하이버네이트는 객체의 version 숫자를 데이터베이스의 version 숫자와 체크함으로써 optimistic locking scheme를 강화할 것이다.

우리의 Person 객체에서 놓친 것은 equals()와 hashCode()의 구현이다. 이것은 영속 객체이기 때문에, 우리는 데이터베이스에서 같은 행을 나타내는 두 개의 다른 인스턴스를 구별할 수 없는 두 메소드의 기본 구현에 의존하길 원치 않는다. 이 메소드들을 구현하는 간단하고 명백한 방법은 equals() 비교와 hashCode() 생성을 위해서 id 필드를 사용하는 것이다.
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || !(o instanceof Person))
        return false;

    Person other = (Person)o;

    if (id == other.getId()) return true;
    if (id == null) return false;

    // equivalence by id
    return id.equals(other.getId());
}

public int hashCode() {
    if (id != null) {
        return id.hashCode();
    } else {
        return super.hashCode();
    }
}
불행하게도, 이 구현에는 문제가 있다. 처음으로 Person 객체를 생성해 id 필드가 null일 경우, 아직 이 객체가 저장되지 않았다면 어느 두 개의 객체라도 동일하게 취급될 것임을 의미한다. 우리가 Person 객체를 만들고 Set 콜렉션에 이 객체를 넣고, 완벽하게 다른 새로운 Person 객체를 만들고 같은 Set에 다시 넣으면, 두 번째 Person 객체는 Set에 추가될 수 없다. Set은 모든 저장되지 않은 객체를 같은 것으로 결정하기 때문이다.

당신은 id가 값을 가지고 있는 경우에만 한해서 사용하는 equals() 구현을 유혹받게 될 것이다. 하지만 만약 두 개의 객체가 아직 저장되지 않았더라도, 이 객체들이 데이터베이스에 저장될 경우 두 개의 다른 주키를 할당받게 될 것이기 때문에 다른 객체라고 가정할 수 있다.
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || !(o instanceof Person))
        return false;

    Person other = (Person)o;

    // unsaved objects are never equal
    if (id == null || other.getId() == null)
        return false;

    return id.equals(other.getId());
}
여기서는 문제가 숨겨졌다. 자바 콜렉션 프레임워크는 콜렉션의 생명 주기 동안 불변하는 필드에 기반을 둔 equals()와 hashCode()를 필요로 한다. 다시 말하자면, 객체가 콜렉션 내에 있는 동안에 equals()와 hashCode()의 값이 변할 수 없다는 것이다. 예를 들어, 다음 프로그램을 보면 :
Person p = new Person();
Set set = new HashSet();
set.add(p);
System.out.println(set.contains(p));
p.setId(new Long(5));
System.out.println(set.contains(p));

결과 : true false
set.contains(p)의 두 번째 호출은 Set에 더 이상 p를 찾을 수 없기 때문에 false를 리턴 한다. Set은 우리의 객체를 완전히 잃어버린 것이다! 이것은 객체가 Set 내에 있는 동안에 hashCode()에서 반환되는 값이 변경되었기 때문이다.

이것은 Sets, Maps, Lists에 다른 도메인 객체를 유지하기 위해서 도메인 객체 생성을 원하는 경우에 문제가 된다. 이것을 위해서 당신은 반드시 각 객체를 위해 저장이 된 전후에 모두 유효하고, 메모리에 객체가 잡혀 있는 동안에는 변하지 않게 equals()와 hashCode()의 구현을 제공해야 한다. 하이버네이트 레퍼런스 문서(v.3)에는 이런 제안을 제공한다 :

“결코 동치성(equality) 구현을 위해서 데이터베이스 식별자(identifier)를 사용하지마라! 항상 불변하고, 유일한 속성의 조합인 비즈니스 키를 사용해라. 데이터베이스 식별자는 transient 객체(주 : 새로 생성되고 영속성을 부여받지 못한 객체)가 영속성을 받으면 변할 것이다. 만약 transient 인스턴스가 Set 콜렉션에 할당되면, hashcode가 변경되어 Set의 계약이 깨질 것이다. 비즈니스키를 위한 속성은 데이터베이스의 주키만큼 영구성을 가져야 하는 것은 아니며, 단지 객체들이 동일한 Set 콜렉션 내에 있는 정도만큼의 영구성을 보장하면 된다.” (하이버네이트 레퍼런스 문서 v.3.1.1 - http://www.hibernate.org/hib_docs/v3/reference/en/html/transactions.html#transactions-basics-identity)

"우리는 비즈니스 키 동치성을 사용한 equals()와 hashCode()의 구현을 추천한다. 비즈니스 키 동치성은 equals() 메소드가 단지 실제 세계에서 우리의 성질을 정의한 비즈니스 키(natural candidate key)로 구성된 속성을 비교하는 것을 의미한다. (하이버네이트 레퍼른스 문서 v.3.1.1 - http://www.hibernate.org/hib_docs/v3/reference/en/html/persistent-classes.html#persistent-classes-equalshashcode )

다시 정리하면, equals()와 hashCode()를 위해서 고유키(natural key)를 사용해라. 그리고 객체의 id를 위해서 하이버네이트에서 생성된 대표키(surrogate key)를 사용해라. 이것은 당신이 각 객체들이 상대적으로 불변인 고유키를 얼마나 가지고 있는지에 따라 작동될 것이다. 그렇다고 모든 객체 타입들이 그러한 키를 갖고 있어야 한다는 것은 아니며, 드물지만 변경되는 필드를 사용하는 것에 혹할 수도 있다. 이것은 비즈니스 키는 데이터베이스 주키 만큼 영속성을 가질 필요가 없다는 생각과 일치한다. 만약 키 값이 객체가 포함되어 있는 콜렉션의 생명 주기 동안에 변하지 않는다면 이것으로 “충분하다”. 이것은 당신의 애플리케이션을 망칠 수 있는 것을 의미할 만큼 위험한 조건이지만, 누군가 적절한 상황에서 올바른 필드로 업데이트 한다면 위험한 상황을 타게 할 수 있다. 이 방법은 실질적으로 더 나은 해결책이 될 것이다.

하이버네이트가 당신의 id를 관리하지 못하도록 하라.

지금까지 논의된 모든 문제들은 객체와 데이터베이스 행의 동일성의 정의를 구별하고, 유지하기 위해 시도하는 것에서 기인한다. 이 문제들은 우리가 모든 동일성의 구성을 단일화 한다면 없어질 것이다. 데이터베이스 중심의 ID(database-centric ID)를 갖는 대신에 데이터 엔터티를 의미하며, 데이터가 처음으로 들어갈 때 생성되는 유일하며, 공통적으로 엔터티에 특화된 ID(entity-specific ID)를 만들 것이다. 이 공통의 ID는 데이터베이스에 저장되어 있거나, 메모리에 객체로 있거나, 혹은 다른 형태, 매개물이거나에 관계없이 유일한 데이터 엔티티를 통해 동일성을 확인 할 수 있다. 데이터 엔터티가 처음으로 생성될 때 할당되는 엔터티 ID의 사용으로 우리는 이 간단하게 id를 사용해서 equals()와 hashCode()의 독자적인 정의를 반환할 수 있다.
public class Person {
    // assign an id as soon as possible
    private String id = IdGenerator.createId();
    private Integer version;

    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }

    public Integer getVersion() {
        return version;
    }
    public void setVersion(Integer version) {
        this.version = version;
    }

    // Person-specific fields and behavior here

    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || !(o instanceof Person))
            return false;

        Person other = (Person)o;

        if (id == null) return false;
        return id.equals(other.getId());
    }

    public int hashCode() {
        if (id != null) {
            return id.hashCode();
        } else {
            return super.hashCode();
        }
    }
}
이 예는 equals()의 정의와 hashCode()를 얻기 위해서 객체 id를 사용한다. 매우 간단하다. 그렇지만 이것을 만들기 위해서는 우리가 필요한 것이 두 가지 있다. 첫 번째는 모든 객체가 저장되기 전에도 id를 가지고 있다는 것을 보증할 방법이 필요하다. 이 예에서는 id 변수를 선언하자마자 id 값을 할당하고 있다. 두 번째는 이 객체가 새로 생성된 객체인지, 혹은 이전에 저장되었던 객체인지를 결정할 방법이 필요하다. 우리의 최초 예에서는 하이버네이트가 객체가 생성 될지 안 될지 결정하기 위해 id 필드 값의 null 여부를 확인했다. 분명한 것은 이제 우리의 객체 id는 더 이상 null이 될 수가 없다. 우리는 id 필드 보다는 version 필드가 null인지 확인하도록 하이버네이트를 설정함으로서 쉽게 해결할 수 있다. version 필드는 이 객체가 이전에 저장되었었는지를 가리키는데 더 적절하다.

개선된 Person 클래스를 위한 하이버네이트 매핑 문서를 살펴보자.



  

    
      
    

    

    

  

id에 있는 generator 태그는 class="assigend" 속성을 가지고 있어야 함을 기억해라. 이것은 하이버네이트에게 우리가 데이터베이스에 의해 id 값이 할당되게 하는 것이 아니라, 우리 코드 내에서 id 값을 할당 할 것이라 말해준다. 하이버네이트는 저장되지 않은 객체라도 id 값이 있기를 당연히 기대한다. 또한 추가했다. version 태그에 새로운 속성인 unsaved-value="null"을 추가했다. 이것은 하이버네이트에게 객체가 새로 생성 되었는지를 식별하기 위해 null version(null id가 아닌)을 찾으라고 말해준다. Integer값 대신에 version 필드를 위해 int를 사용하는 것이 더 유용하며, 저장되지 않은지를 알기 위한 척도로서 음의 값을 찾으라고 쉽게 말할 수 있다.

우리는 id를 갖는 퓨어 객체로 이동함으로써 몇 가지 이익을 얻었다. 우리의 equals()와 hashCode()의 구현은 매우 단순해졌고, 가독성이 높아졌다. 이 메소드들은 더 이상 에러를 쉽게 발생시키지 않으며, 객체가 저장된 전후에도 콜렉션에서 올바르게 작동되는 것을 보장한다. 또한 하이버네이트에서는 새로운 객체를 저장하기 전에 데이터베이스에서 시퀀스 값을 읽어 들일 필요가 더 이상 없기 때문에 더 빨라졌다. 더욱이 equals()와 hashCode()의 새로운 정의는 객체 id를 가지고 있는 모든 객체에 보편적으로 적용된다. 이것은 메소드를 추상 상위 클래스로 이동할 수 있다는 것을 의미한다. 우리는 더 이상 모든 객체에 대한 equals()와 hashCode()의 재-구현을 필요로 하지 않으며, 더 이상 각 클래스를 위한 유일하고 불변하는 필드의 조합을 고려할 필요 없다. 물론, 유연성 유지를 위해서 인터페이스를 정의할 것이기 때문에, 우리는 도메인 객체가 상위 클래스로 확장되는 것을 원치 않는다.
public interface PersistentObject {
    public String getId();
    public void setId(String id);

    public Integer getVersion();
    public void setVersion(Integer version);
}


public abstract class AbstractPersistentObject
        implements PersistentObject {

    private String id = IdGenerator.createId();
    private Integer version;

    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }

    public Integer getVersion() {
        return version;
    }
    public void setVersion(Integer version) {
        this.version = version;
    }

    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null ||
            !(o instanceof PersistentObject)) {

            return false;
        }

        PersistentObject other
            = (PersistentObject)o;

        // if the id is missing, return false
        if (id == null) return false;

        // equivalence by id
        return id.equals(other.getId());
    }

    public int hashCode() {
        if (id != null) {
            return id.hashCode();
        } else {
            return super.hashCode();
        }
    }

    public String toString() {
        return this.getClass().getName()
            + "[id=" + id + "]";
    }
}
우리는 지금 도메인 객체를 생성하기 위한 단순하고 효과적인 방법을 가지고 있다. AbstractPersistentObject를 상속하면, 처음으로 생성될 때 id 값을 자동으로 주고, equals()와 hashCode()도 적절하게 구현할 수 있다. 또한 선택적인 오버라이드를 사용해서 적당한 toString()의 구현을 얻을 수 있다. 만약 테스트 객체, 혹은 연습을 위한 쿼리에 해당하는 연습용 객체라면, id는 변경되지 않을 수 있고, null로 값이 들어 갈 수 있다. 다른 방법으로는 그것을 변경하지 않게 하는 것이다. 만약 도메인 객체를 만들기 위해서 다른 클래스를 상속해야 한다면, 추상 클래스를 상속하는 것 보다는 PersistentObject 인터페이스를 구현할 수 있다.
이제 우리의 Person 클래스는 매우 단순해졌다 :
public class Person
    extends AbstractPersistentObject {

    // Person-specific fields and behavior here

}
하이버네이트 매핑 문서는 이 예에서 변경되지 않는다. 하이버네이트에게 추상 상위 클래스에 대해서 말해 성가시게 하지 않는 대신, 모든 PersistentObject 매핑 파일이 id("assigned" generator 태그를 포함하고 있는)와 unsaved-value="null"로 설정된 version 태그가 포함되어 있는지 확인할 것이다. 눈치 빠른 독자는 id가 영속 객체가 생성될 때마다 할당됨을 알아차렸을 것이다. 이것은 하이버네이트가 데이터베이스에서 이미 존재하는 객체를 읽어서 이 객체의 인스턴스를 메모리에 생성할 때마다 새로운 id를 얻는다는 것을 의미한다. 괜찮다. 하이버네이트는 이어서 객체에 저장되어 있는 id를 새로 할당된 id로 교체하기 위해서 setId()를 호출할 것이다. 이 추가적인 id 생성은 id 생성알고리즘이 비용이 적게 들어가는(즉, 데이터베이스와의 접촉이 필요 없다는 뜻) 동안에는 문제가 되지 않는다.

여기까지 매우 좋았지만, 우리는 여기서 한 가지 매우 사소한 문제를 남겨두고 있다. IdGenerator.createId() 를 어떻게 구현할 것인가? 우리는 이상적인 키 생성 알고리즘을 위한 몇 가지 조건을 정의 할 수 있다.
  • 데이터베이스와의 접촉 없이 매우 저비용으로 생성되어야 함
  • 다른 가상 머신, 다른 서버(machine)일 경우에도 유일성을 보장해야 함
  • 생성된 키 값은 적어도 다른 프로그램이나 프로그래밍 언어, 데이터베이스와 호환되어야 한다.
우리는 범용 유일 식별자(universally unique identifier : UUID)를 필요로 한다. UUID는 표준 포맷에 매우 충실한 16 바이트(128비트) 숫자다. UUID의 String 버전은 다음과 같다.
2cdb8cee-9134-453f-9d7a-14c0ae8184c6
이 문자는 숫자의 위치 차이는 ‘-’로 구분해서 바이트를 16진법으로 간단하게 표현한다. 이 포맷은 작업하기 매우 간단하고 쉽지만, 이것의 길이는 36개의 문자열이다. ‘-’는 항상 같은 위치에 위치하며, 32개 문자의 크기를 줄이기 위해서 제거 될 수 있다. 조금 더 정확한 표현을 위해서 byte[16] 배열로 표현되거나, 두 개의 8byte 길이로 표현된 숫자를 갖는 객체를 생성 할 수 있다. 만약 Java 1.5나 그 이후 버전을 사용한다면, 메모리 포맷에 최적화된 방법은 아니지만, UUID 클래스를 직접 사용할 수 있다. 더 많은 정보를 찾길 원하며, 위키피디아 UUID 목록(http://en.wikipedia.org/wiki/UUID)을 보거나 UUID 클래스를 위한 자바 문서 목록(http://java.sun.com/j2se/1.5.0/docs/api/java/util/UUID.html)을 봐라. UUID 생성 알고리즘 구현에는 몇 가지 방법이 있다. 최종적인 UUID는 표준 포맷이기 때문에, 우리만의 IdGenerator 클래스를 사용한다고 문제가 되지는 않는다. 어떤 알고리즘이든지 각 id는 유일한 값이 보장되기 때문에, 우리는 언제나 구현한 것을 변경하거나 조합하거나 다른 구현들과 맞춰볼 수도 있다. 자바 1.5나 그 이후 버전을 사용한다면, 매우 편리한 구현인 java.util.UUID가 있다.
public class IdGenerator {
    public static String createId() {
        UUID uuid = java.util.UUID.randomUUID();
        return uuid.toString();
    }
}
자바 1.5 이상을 사용하지 않는 경우에, UUID를 구현한, 자바의 이전 버전에 적합한 두 개의 외부 라이브러리인 Apacke Commons ID(http://jakarta.apache.org/commons/sandbox/id/uuid.html) project와 Java UUID Generator(JUG - http://jug.safehaus.org/)가 있다. 둘 다 아파치 라인센스로 사용할 수 있다.

여기서 JUG 라이브러리를 사용한 IdGenerator 구현의 예를 보자.
import org.safehaus.uuid.UUIDGenerator;

public class IdGenerator {

    public static final UUIDGenerator uuidGen
        = UUIDGenerator.getInstance();

    public static String createId() {
        UUID uuid
            = uuidGen.generateRandomBasedUUID();
        return uuid.toString();
    }
}
UUID 생성 알고리즘에 대해서 하이버네이트는 어떤 구조를 구성했을까? 객체 동일성을 위해서 UUID를 얻는 방법이 적당해보이는가? 객체 동일성을 객체의 영속성과 독립시키기를 원치 않는다. 하이버네이트가 당신을 위해 UUID를 생성하는 옵션을 가지고 있다면, 이는 객체가 생성 시기에 ID를 받지 못하고, 저장될 때까지 기다려야 하는 이전과 동일한 문제로 돌아가게 된다. 데이터베이스 주키로 UUID를 사용하는 가장 큰 단점은 인덱스들과 외래키의 증가를 더욱 악화시키는데 있다. 여기에 상충 관계(trade-off)가 있다. 문자열로 표현되는 데이터베이스 주키는 32 혹은 36 바이트다. 이 숫자는 또한 바이트로 바로 저장이 될 수 있고, 반으로 잘린 크기로 저장될 수도 있지만, 이는 데이터베이스에 쿼리를 수행 할 때 이해를 더 어렵게 한다. 이것은 당신의 프로젝트에서 요구사항에 맞추어서 실행하면 된다.

주키로 UUID를 사용하는 것이 당신의 데이터베이스에 적합하지 않다면, 데이터베이스 시퀀스를 사용할 것을 고려하게 될 것이지만, 하이버네이트가 당신의 ID를 관리하게 하지 말고 항상 객체 생성 시 새로운 객체에 ID를 할당해야 한다. 이런 경우에는 새로운 도메인 객체로 생성된 당신의 비즈니스 객체는 데이터베이스 시퀀스에 의해 id를 받기 위해서 데이터 접근 객체(data access object : DAO)를 사용하는 서비스를 호출할 것이다. 객체의 id를 위해 Long 데이터 타입을 사용한다면, 단일 데이터베이스 시퀀스로도 당신의 도메인 객체를 위해서 충분할 것이다.

결론

객체 동일성은 객체가 데이터베이스에 영속화 될 때 오해를 불러일으킬 수 있는 매우 구현이 어려운 문제다. 그렇지만 이 문제는 객체가 저장되기 전에 id 없이 존재하는 객체 허용을 완전하게 막아준다. 우리는 이 문제를 객체 ID를 할당하는 책임을 하이버네이트와 같은 ORM 프레임워크에 책임을 넘김으로서 완전히 해결할 수 있다. 대신 객체 ID는 객체가 생성되자마자 할당되어야 한다. 이것은 객체 동일성을 간단하게 해주며, 에러에서 좀 더 자유롭고, 도메인 모델에 필요한 코드의 양을 줄여준다.

참고 자료
역자 박찬욱님은 현재 학생으로, 오픈 소스에 많은 관심을 가지고 있습니다. Agile Java Network에서 커뮤니티 활동을 열심히 하고 있고, 블로그(chanwook.tistory.com)를 재미있게 운영하고 있습니다.
TAG :
댓글 입력
자료실

최근 본 책0