Object-Relational Mapping Strategies

Table of Contents

시작하기전 기초지식 살펴보기

ORM을 이용해 더 좋은 작업을 하기위해서는 object modeling과 relational modeling, 두 modeling의 유사점과 차이점을 이해해야만 합니다.

Object Modeling

object modeling은 OOP를 기반으로 생성된 시스템을 표현합니다.
이러한 object-modeling에는 identity, state, behavior, encapsulation등의 많은 개념이 포함되어있습니다.

    • 기본 개념
      • Identity
      • State
      • Behavior
      • Encapsulation
    • 높은 레벨의 개념
      • Type
      • Associations
      • Class
      • Inheritance

Relational Modeling

relatipnal modeling은 서술어와 비슷한 truth statemet로 정보를 표현합니다.
개념들을 소개하면 다음과 같습니다.

    • 기본개념
      • Relation
      • Attribute
      • Domain
      • Tuple
      • Attribute Value
      • Relation Value
      • Relation Variable
      • Daatabase
      • Base Relation Values
      • Derived Relation Values
      • Coupling between relations, variables, and values
몇 가지 개념 설명
  • Tuple
    <Person SSN#="123-45-6789" Name="ryu sung hee" City="seoul">

    이중에서 SSN#="123-45-6789", Name="ryu sung hee", City="seoul" 각각이 하나의 tuple입니다.
    tuple의 정렬은 아무 의미가 없습니다.

  • Attribute Value
    attribute value는 각각의 tuple에 포함된 attribute의 value입니다.
    • Database와 Relational modeling의 기본적인 개념비교
      Common DatabaseRelational
      tablerelation valiable
      rowtuple
      columnattribute
      column valueattribute value
      databasedatabase

보다 자세한 설명은 http://www.chimu.com/publications/objectRelational/ 웹문서를 참조하십시오.

Object-Relational Mapping이란?

Object-Relational Mapping(이하 ORM)은 object와 relational modeling 구현물 사이, 그리고 이 구현물을 서포트하는 시스템간의 변환 프로세스입니다. 조금 더 쉽게 설명하자면 객체와 테이블, 시스템(RDBMSs)을 변형 및 연결해주는 작업이라 말 할 수 있습니다. ORM을 이용한 개발은 객체와 데이터베이스의 변형에 유연하게 대처할 수 있도록 해줍니다.

  • Transparent Persistence

    ORM에서, object programming language를 사용한 relational database에 저장된 데이터를 직접적으로 조작하는 능력을 Transparent persistence라고 합니다. Transparent persistent는 ODBC 또는 JDBC등을 사용하는 database sub-language와는 다릅니다.

Why ORM?

  • (과장되게 표현하여)ORM을 이용하면 CRUD를 위해 긴 SQL문장을 작성할 필요가 없습니다. 쿼리 작성은 여전히 필요하지만, ORM 툴(HQL 등)을 이용하면 한충 쉽게 만들 수 있습니다. 또 JDBC와 관련된 복잡한 코드 작업으로부터 해방될 수 있습니다.
  • ORM을 이용하면 관계형 모델과 관련된 성능 오버헤드를 수반하지 않고도 요구사항에 적합한 도메인 모델을 생성할 수 있으며, 로우와 컬럼이 아닌 오브젝트의 관점에서 작업을 수행하는 것이 가능합니다.
  • ORM은 변경 사항을 자동으로 감지하므로, 전체 개발 라이프사이클에 걸쳐 에러의 가능성을 줄일 수 있습니다.
  • ORM은 데이타베이스 벤더 별로 제공되는 SQL 구문에 대한 종속성을 줄이고 호환성을 향상시켜 줍니다. SQL 구문은 ORM 툴에 의해 추상화가 가능합니다.

ORM사용 선택을 위한 몇 가지 가이드라인

적절하게 활용되는 경우, ORM은 개발 작업에 투자되는 인력을 절감하고 관리 능력을 개선하는 효과를 제공합니다. JDBC 대신 ORM 솔루션을 적용함으로써 작성되어야 하는 Java 코드의 양을 30~50% 절감하는 경우도 드물지 않습니다. 이렇게 절감된 인력을 비즈니스 기능의 구현에 투자할 수 있다는 점에서, ORM 솔루션은 분명한 효과가 있습니다. 적절한 환경, 적절한 애플리케이션에 ORM을 적용함으로써 얻을 수 있는 잠재적인 효과는 무조건적으로 무시하거나 외면하는것은 합리적인 태도가 아닙니다.

ORM 툴에 대해 부정적인 시각을 가진 이들은, 적절하지 않은 환경에 ORM 툴을 적용한 경우가 많습니다. 맞지 않는 환경에 ORM을 억지로 적용시키는 경우만 아니라면, ORM은 훌륭한 결과를 보여줍니다. 몇 가지 기본적인 가이드라인이 아래와 같습니다:

  • 타겟 데이타베이스를 이해하라. ORM을 적용할 때 SQL 및 데이타베이스의 락킹(locking) 모델을 무시해도 된다고 생각하면 오산입니다. O-R 매핑은 작업을 쉽게 해 주는 툴이지, 구현되는 환경에 대한 이해를 불필요하게 만드는 툴은 아닙니다. (ORM 적용 환경에서 발생하는 많은 문제가 데이타베이스와 SQL의 문제를 간과함으로써 발생합니다.)
  • 필요한 경우 SQL을 사용하는 것을 두려워하지 말라. 많은 시나리오에서 이 방법으로 효과를 볼 수 있습니다. Hibernate, TopLink와 같은 ORM 제품은 SQL 쿼리 작성 기능을 제공합니다. 하지만 경우에 SQL문을 직접 작성해야 할 경우도 있습니다.
  • O-R 매핑 제품을 선택하기 전에 충분히 검토하라. 모든 ORM 제품이 동일한 수준의 기능을 제공하는 것은 아닙니다. 요구사항을 반영하는 환경을 구축하고 2~3 가지 제품을 비교 테스트해 보아야 합니다. 이 과정을 통해 ORM이 성능 기준을 만족하는지 검증할 수 있습니다. 엔터프라이즈 개발 과정의 다른 요소들과 마찬가지로, 프로젝트 라이프사이클의 초기 단계에서 성능과 관련한 리스크를 최소화하는 것이 중요합니다. 또 ORM 툴의 매핑 기능에 과도한 오버헤드가 수반되지 않는지 확인해야 합니다.
  • ORM이 적절하게 사용될 수 있는 상황을 이해하라. ORM은 엔티티를 개별적으로 업데이트하고 간헐적으로 셋 기반 작업을 수행하는 OLTP 애플리케이션에 특히 적합합니다. 고객 레코드 및 주문 내역을 개별적으로 업데이트하는 애플리케이션이 좋은 예입니다.
  • ORM이 적절하지 않은 경우를 이해하라. ORM은 만병통치약이 아닙니다. ORM이 적합하지 않은 경우가 아래와 같습니다:
    • 많은 수의 레코드에 대해 잦은 빈도로 벌크 업데이트를 수행하는 애플리케이션
    • OLAP 애플리케이션
      데이터마이닝을 위해 사용되는 어플리케이션에서는 이미 데이터를 본래의 엔티티 상태로 사용하기 어렵습니다.
      OLAP에 대해서는 http://home.pusan.ac.kr/~pnustat/info/DataMining/3-2.htm를 참조하십시오.
    • 데이타의 인출 및 업데이트를 위해 핸드코딩으로 작성된 SQL 및 저장 프로시저를 이용하는 데이타베이스 환경.
      이 경우 JDBC 기반의 접근 방법이 최선의 선택이 될 수 있습니다. iBATIS SQL Maps 역시 이러한 환경에서 빛을 발합니다. (하지만 몇몇 ORM 제품은 기존 스키마 및 저장 프로시저와 효과적으로 연동될 수 있으므로, 이러한 환경에서도 ORM의 사용을 고려할 가치가 있습니다.)
    • 순수 SQL 기반 접근 방법을 적용하는 것이 적절한 애플리케이션.
      비 즈니스 로직의 대부분이 데이타베이스에 이미 구현되어 있거나, 데이타베이스 무결성 제약(integrity constraint)이 적용되어 있는 경우 등을 그 예로 들 수 있습니다. 이러한 애플리케이션에서는 오브젝트 또는 ORM의 활용 여지가 매우 적으며, 데이타베이스 테이블을 도메인 오브젝트로 모델링 함으로써 기대할 수 있는 효과가 거의 없습니다.

Mapping Basic

자바 클래스는 RDBMS에 매핑될 수 있으며, 우리는 여기에서 persistent class와 table 사이의 mapping에 대해 알아볼 것입니다.

  1. one-to-one

    persistent class와 테이블 사이의 가장 심플한 매핑은 one-to-one 방식입니다. 이 방식에서 모든 persistent class의 attribute들은 테이블의 모든 컬럼들에 의해 다시 표현됩니다. ( attribute:column = 1:1 ) 이러한 매칭 방식은, business class의 각 instence을 매핑되는 테이블의 하나의 row에 저장시킵니다.

  2. class-to-table

    비록 매핑 타입이 적절하더라도, 존재하는 object와 entity-relation model 사이에서는 충돌이 발생하곤 합니다. 이러한 실세계와 object modeld의 차이를 극복하기위해 calss-to-table mapping은 다음의 두가지 방법으로 이를 지원합니다.

    • SUBSET Mapping
      persistent class의 attribute들은 테이블의 특정 컬럼으로 표현되거나, persistent class와 매핑되는 테이블의 모든 컬럼으로 표현됩니다. subset mapping은 상속관계의 class를 매핑할 때에도 사용됩니다. ("상속 트리에서의 class 매핑"에서 좀더 자세히 설명하도록 하겠습니다.)
    • SUPERSET Mapping
      superset mappping된 persistent class는 multiple tables의 컬럼으로부터 파생된 attributes를 포함합니다. 이러한 매핑 타입은 table spanning으로도 잘 알려져있으며 view, join, inheritence tree 등을 표현하기위해 사용됩니다.

상속 트리에서의 class 매핑

RDBMS에서의 상속 트리를 클래스로 표현하기위해 사용되는 가장 일반적인 전략이 "vertical mapping, horizontal mapping, filtered mapping"입니다. (이 세가지 전략이 inheritence tree를 위해서만 이용되는것은 아닙니다.)

handy tip
  • abstract and concrete class
    abstract class는 부모 클래스로서 디자인된 클래스를 의미합니다. 반면에 concrete class는 entity로서 생성되어진 클래스입니다. concrete class는 abstract class와는 다릅니다. 아래의 예제에서 "Student", "Professor"가 concrete class가 됩니다.
    보다 자세한 정보는 http://encyclopedia.thefreedictionary.com/concrete+class 웹페이지의 "Abstract and concrete classes"를 참조하십시오.

'

  • 각각의 매핑 타입을 이해하기위해 다음의 UML 클래스 다이어그램을 이용합니다.
    '
  1. vertical mapping (One table per subclass)

    vertical mapping 방식은 각 클래스마다 테이블을 생성합니다. 모든 branch와 leaf 테이블들은 부모 테이블과 연결되며, 부모 테이블의 primary key를 참조합니다. vertical mapping의 장점은 object-oriendted concept에 가장 순응한다는 점입니다. 또한 테이블의 수정이나, 추가만 필요하므로 superclass와 subclass를 수정하기가 매우 쉽습니다. 그러나 복잡한 결과물이나 상속관계에서는 사용하기가 어렵습니다.
    '

  2. horizontal mapping (One table per concrete class)

    horizontal mapping 방식은 각 concrete class마다 테이블을 생성합니다. concrete class에는 자기 자신의 모든 attribute들이 컬럼으로 포함되어있으며, 추상화 상위 class의 모든 attribute들 또한 포함되어있습니다. 이러한 mapping 방법의 구현물들은 매우 빠른 성능을 제공하며, 디자인하기에도 간단합니다. 그렇지만, 추상화 상위 class의 컬럼이 변경되면 연관된 많은 테이블들이 변경되어야 합니다. 따라서, 이 매핑 방법은 attribute에의해 좌우되는 상속 트리보다, 메소드에의해 좌우되는 상속트리에 가장 유용하게 이용할 수 있습니다.
    '

  3. filtered mapping (One Table per class herarchy)

    filtered mapping 방식에서는 모든 concrete class들이 같은 테이블로 매핑됩니다. 테이블은 상속 트리에서 추상화된 모든 attribute들과 concrete class들(mapping에 관련된 상속 트리의 class들)을 포함합니다. 또한, 테이블에 filter column이 생성됩니다. filter column은 subclass들을 분리할때 사용됩니다. filtered mapping 방식의 구현물들은 적절한 성능을 제공하지만, table-normalization rule에는 위배됩니다. 또한 클래스간의 coupling을 증가 시킵니다.
    '

    여기에서 objectType column은 filter column으로 이용됩니다.

vertical, horizontal, filtered mapping 전략 비교

세가지 mapping 전략을 비교하기위해 "상속 트리에서의 class 매핑"에서 사용된 UML에 간단한 TenuredProfessor class를 추가하도록 하겠습니다.

  • TernuredProfessor class를 추가한 UML
    '

    '
  1. vertical mapping (One table per subclass)
    '
    vertical mapping 전략은 하나의 class를 하나의 data entity로 매핑합니다. 때문에 한개의 TenuredProfessor table을 생성하면 됩니다. 하지만 상위 추상 레벨의 테이블 정보를 확인해야 하는 등 여러번에 걸친 데이터 엑세스를 필요로 하는 단점이 있습니다.
    '

    '
  2. horizontal mapping (One table per concrete class)
    '
    horizontal mapping 전략은 각각의 concrete class를 각각의 data entity로 매핑합니다. 이때에도 한개의 테이블만 생성하면 됩니다. 하지만 릴레이션이 변경되었을 때, 이 object를 어떻게 핸들링할것인가에 대한 문제가 남아있습니다. Professor 테이블의 정보를 TenuredProfessor에 더했기 때문에 이러한 릴레이션 문제는 더욱 복잡해 집니다.
    '

    '
  3. filtered mapping (One Table per class herarchy)
    '
    filtered mapping 전략은 상속관계의 class들을 하나의 data entity로 매핑합니다. 때문에 데이터의 update를 어떻게 해야 할지 고민해야 하며, 낭비되는 공간은 상당히 거슬리는 문제일 것입니다.
    '
  • maping 전략들은 각각의 장단점을 가지고 있습니다. 다음은 이를 간략히 비교한 표 입니다.
    Factors to Consider(비교요소)vertical mappinghorizontal mappingfiltered mapping
    Ad hoc reportingMedium/DifficultMediumSimple
    Ease of implementation(사용성)DifficultMediumSimple
    Ease of data accessMedium/SimpleSimpleSimple
    CouplingLowHighVery high
    Speed of data accessMedium/FastFastFast
    Support for polymorphism(다형성)HighLowMedium

Mapping Object Relationships

object의 관계를 매핑하는 방법으로는 아래와 같은 방법들이 있습니다.

  1. One-to-One Relationships = 1:1
  2. One-to-Many Relationships = 1:n
  3. Owned Relationships (Aggregation)
  4. Referenced Relationships (Association)
  5. Many-to-Many Relationships = n:n

Join Table에 대한 모델링

Join Table에 대한 모델링 방법으로는 아래와 같은 방법들이 있습니다.

  1. Transactional Joins
  2. Transparent Joins
  3. Self-Join Tables

Mapping Examples

  • Class Model
    '

    '
  • Data Model 결과물
    '

    '
  • class, data model 매핑 분석
    1. vertical mapping (One table per subclass)
      • class = Person, Corporation, Individual
      • table = TB_PERSON, TB_CORPORATION, TB_INDIVIDUAL
      • source for hibernate
        Person.hbm.xml
        <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 2.0//EN"	"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd"><hibernate-mapping>  <class name="eg.hibernate.mapping.dataobject.Person" table="TB_PERSON" polymorphism="implicit">    <id name="id" column="ID">      <generator class="assigned"/>    </id>    <set name="rights" lazy="false">      <key column="REF_PERSON_ID"/>      <one-to-many class="eg.hibernate.mapping.dataobject.Right" />    </set>    <joined-subclass name="eg.hibernate.mapping.dataobject.Individual" table="TB_INDIVIDUAL">      <key column="id"/>      <property name="firstName" column="FIRST_NAME" type="java.lang.String" />      <property name="lastName" column="LAST_NAME" type="java.lang.String" />    </joined-subclass>    <joined-subclass name="eg.hibernate.mapping.dataobject.Corporation" table="TB_CORPORATION">      <key column="id"/>      <property name="name" column="NAME" type="string" />      <property name="registrationNumber" column="REGISTRATION_NUMBER" type="string" />    </joined-subclass>  </class></hibernate-mapping>
    2. horizontal mapping (One table per concrete class)
      • class = Estate, Building, Land
      • table = TB_BUILDING, TB_LAND
      • source for hibernate
        Estate.hbm.xml
        <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE hibernate-mapping PUBLIC   "-//Hibernate/Hibernate Mapping DTD 2.0//EN"  "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd"><hibernate-mapping>  <class name="eg.hibernate.mapping.dataobject.Land" table="TB_LAND" polymorphism="implicit">    <id name="id" column="ID">      <generator class="assigned"/>    </id>    <property name="description" column="DESCRIPTION" type="java.lang.String" />    <property name="squareFeet" column="SQUARE_FEET" type="java.lang.Double"/>  </class>  <class name="eg.hibernate.mapping.dataobject.Building" table="TB_BUILDING" polymorphism="implicit">    <id name="id" column="ID">      <generator class="assigned"/>    </id>    <property name="description" column="DESCRIPTION" type="java.lang.String" />    <property name="address" column="ADDRESS" type="java.lang.String"/>  </class></hibernate-mapping>
    3. filtered mapping (One Table per class herarchy)
      • class = Lease, property, Right
      • table = TB_RIGHT
      • source for hibernate
        Right.hbm.xml
        <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE hibernate-mapping PUBLIC   "-//Hibernate/Hibernate Mapping DTD 2.0//EN"  "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd"><hibernate-mapping>  <class name="eg.hibernate.mapping.dataobject.Right" table="TB_RIGHT" polymorphism="implicit">    <id name="id" column="ID">      <generator class="assigned"/>    </id>    <discriminator>      <column name="DISCRIMINATOR"/>    </discriminator>    <property name="date" column="DATE" type="java.sql.Date" />    <many-to-one name="person" class="eg.hibernate.mapping.dataobject.Person" column="REF_PERSON_ID"/>    <any name="estate"         meta-type="string"         id-type="java.lang.Integer">      <meta-value value="LND" class="eg.hibernate.mapping.dataobject.Land"/>      <meta-value value="BLD" class="eg.hibernate.mapping.dataobject.Building"/>      <column name="REF_ESTATE_TYPE"/>      <column name="REF_ESTATE_ID"/>              </any>        <subclass name="eg.hibernate.mapping.dataobject.Property" discriminator-value="PRO"/>        <subclass name="eg.hibernate.mapping.dataobject.Lease" discriminator-value="LEA">      <property name="duration" column="DURATION" type="java.lang.Integer" />     </subclass>  </class></hibernate-mapping>

참고문헌

+ Recent posts