Whiteship's Note


Hibernate Core VS JPA

Hibernate/Chapter 5 : 2008.02.04 17:39


Hibernate JPA
네 가지 상속 맵핑 전략을 제공하고, 상속 전략을 혼용하는 것이 가능하다. 네 가지 맵핑 전략을 표준화 했다. 이 중에선 Table Per Class Hierarchy와 Table Per Subclass만 JPA 호환 필수요소다.
영속성 대상이 되는 클래스의 상위 클래스는 abstract class나 interface도 될 수 있다. abstract class만 이식 가능하다.
Value Type을 위한 유연한 내장 타입과 컴버터를 제공한다. 표준화된 방법으로 맵핑 타입을 자동으로 찾아 준다. 커스텀 타입을 찾을 때는 하이버네이트 확장 애노테이션을 사용한다.
막강한 타입 확장 시스템 Enum을 위한 내장 타입 표준이 필요하다.

'Hibernate > Chapter 5' 카테고리의 다른 글

Hibernate Core VS JPA  (0) 2008.02.04
Enum Type 맵핑하기 - 실습  (0) 2008.02.04
Enum Type 만들기  (0) 2008.02.04
ParameterizedType 만들기  (0) 2008.02.04
CompositeUserType 만들기  (0) 2008.02.04
UserType만들기  (0) 2008.02.04
맵핑 타입 사용하기  (0) 2008.02.04
내장된 맵핑 타입  (0) 2008.02.04
Entity와 Value type 복습  (0) 2008.02.04
상속 맵핑 전략 선택하기  (0) 2008.02.01
상속 맵핑 전략 혼용하기  (0) 2008.02.01
top


Enum Type 맵핑하기 - 실습

Hibernate/Chapter 5 : 2008.02.04 17:35


1. 테스트 코드


* 그냥 하나를 저장하고 그걸 다시 꺼내 봅니다. 꺼낼 때 get을 사용해도 되지만, 쿼리를 어떻게 작성해야 하는지 보기 위해서 HQL로 작성했습니다.

* 그냥 문자열일 뿐인데, 저걸 읽어서 Enum 타입을 알아내고 그것의 name()을 호출한 값으로 대체 해주는 하이버... 정말 똑똑하지 않나요. 대단합니다.

* 위 클래스에 있는 애노테이션들을 붙이면 Spring 2.5 전에 사용하던 AbstractTransactionalDataSource어쩌구저쩌구를 상속받은 클래스와 같은 녀석이 됩니다.(엄밀히 따지면 같지는 않습니다. applicationContext를 가지고 있지 않아서, 명시적으로 getBean() 할 수가 없습니다.)

2. Member 클래스와 MemberType 클래스

* 별거 없습니다. id, membeType, name.

* MemberType은 매우 단순한 enum입니다.

* 주목해야 할 것은 MemberType 속성 위에 붙인 @Enumerated 애노테이션 입니다. 이녀석이 굉장한 일을해줍니다.

3. XML 설정

* 역시나 뭐 별거 없습니다. 그냥 스프링+하이버 SessionFactory, TransactionManager, DataSource입니다.

4. 결과
Hibernate: insert into Member (id, TYPE, name) values (null, ?, ?)
Hibernate: call identity()
Hibernate: select member0_.id as id0_, member0_.TYPE as TYPE0_, member0_.name as name0_ from Member member0_ where member0_.TYPE='ADMIN'
id: 1 name: 기선 type: ADMIN

결과 쿼리를 보면 재밌습니다. 저는 그냥 @Enumerated 애노테이션 하나 붙였을 뿐인데, 알아서 ADMIN으로 저장해주고, HQL도 알아서 변경해 줍니다. 엘레강트 한 녀석입니다.

'Hibernate > Chapter 5' 카테고리의 다른 글

Hibernate Core VS JPA  (0) 2008.02.04
Enum Type 맵핑하기 - 실습  (0) 2008.02.04
Enum Type 만들기  (0) 2008.02.04
ParameterizedType 만들기  (0) 2008.02.04
CompositeUserType 만들기  (0) 2008.02.04
UserType만들기  (0) 2008.02.04
맵핑 타입 사용하기  (0) 2008.02.04
내장된 맵핑 타입  (0) 2008.02.04
Entity와 Value type 복습  (0) 2008.02.04
상속 맵핑 전략 선택하기  (0) 2008.02.01
상속 맵핑 전략 혼용하기  (0) 2008.02.01
top


Enum Type 만들기

Hibernate/Chapter 5 : 2008.02.04 17:20


클래스 구현하기.

  • EnhancedUserType와 ParameterizedType 인터페이스를 구현한다.
private Class<Enum> enumClass;
    
    public void setParameterValues(Properties parameters) {
        String enumClassName =
            parameters.getProperty("enumClassname");
        try {
            enumClass = ReflectHelper.classForName(enumClassName);
        } catch (ClassNotFoundException cnfe) {
            throw new
              HibernateException("Enum class not found", cnfe);
        }
    }

    public Class returnedClass() {
        return enumClass;
    }

    public Object fromXMLString(String xmlValue) {
        return Enum.valueOf(enumClass, xmlValue);
    }

    public String objectToSQLString(Object value) {
        return '\'' + ( (Enum) value ).name() + '\'';
    }

    public String toXMLString(Object value) {
        return ( (Enum) value ).name();
    }

    public Object nullSafeGet(ResultSet rs,
                              String[] names,
                              Object owner)
            throws SQLException {
        String name = rs.getString( names[0] );
        return rs.wasNull() ? null : Enum.valueOf(enumClass, name);
    }

    public void nullSafeSet(PreparedStatement st,
                            Object value,
                            int index)
            throws SQLException {
        if (value == null) {
            st.setNull(index, Hibernate.STRING.sqlType());
        } else {
            st.setString( index, ( (Enum) value ).name() );
        }
    }
  • setParameterValues()는 클래스 이름 가져와서 Class 변수에 세팅한다.
  • fromXMLString(), objectToSQLString(), toXMLString()는 XML과 Enum의 value를 맵핑한다.
  • nullSafeGet()는 DB에서 value를 읽은 다음 Enum으로 반환한다.
  • nullSafeSet()는 Enum에서 DB로 저장할 value를 뽑아낸다.

맵핑하기

  • JPA의 경우 커스텀 타입 클래스 만들지 않고도 String으로 저장하거나, 선택된 Enum의 value를 저장할 수 있다.
public class Comment {
    ...
    @Enumerated(EnumType.STRING)
    @Column(name = "RATING", nullable = false, updatable = false)
    private Rating rating;
    ...
}

쿼리 작성하기

  • 다음과 같이 쿼리를 작성할 수 있다.
Query q =
    session.createQuery(
      "from Comment c where c.rating = auction.model.Rating.BAD"
    );
  • c.rating의 rating은 DB 컬럼의 이름이 아니라 객체의 속성이름이다.
"from Member m where m.memberType = '" + MemberType.ADMIN + "'"
"from Member m where m.memberType = chapter5.customType.example.MemberType.ADMIN"

'Hibernate > Chapter 5' 카테고리의 다른 글

Hibernate Core VS JPA  (0) 2008.02.04
Enum Type 맵핑하기 - 실습  (0) 2008.02.04
Enum Type 만들기  (0) 2008.02.04
ParameterizedType 만들기  (0) 2008.02.04
CompositeUserType 만들기  (0) 2008.02.04
UserType만들기  (0) 2008.02.04
맵핑 타입 사용하기  (0) 2008.02.04
내장된 맵핑 타입  (0) 2008.02.04
Entity와 Value type 복습  (0) 2008.02.04
상속 맵핑 전략 선택하기  (0) 2008.02.01
상속 맵핑 전략 혼용하기  (0) 2008.02.01
top


ParameterizedType 만들기

Hibernate/Chapter 5 : 2008.02.04 17:20


클래스 구현하기

  • DB에 값을 저장할 때 특정 조건에 따라 다른 값으로 변환하여 저장할 필요가 있다면, 파라미터가 필요하다. 이럴 때 유요하다.
  • ParameterizedType 인터페이스를 구현한다.
private Currency convertTo;

	@Override
	public void setParameterValues(Properties parameters) {
		this.convertTo = Currency.getInstance(
                parameters.getProperty("convertTo"));
	}

맵핑하기

  • 같은 Value Type 클래스 타입의 객체들 여럿을 맵핑 할 때는 TypeDef 애노테이션을 사용해서, 전역에서 사용할 설정을 정의해 둘 수 있다.
@org.hibernate.annotations.TypeDefs({
    @org.hibernate.annotations.TypeDef(
        name="monetary_amount_usd",
        typeClass = persistence.MonetaryAmountConversionType.class,
        parameters = { @Parameter(name="convertTo", value="USD") }
    ),
    @org.hibernate.annotations.TypeDef(
        name="monetary_amount_eur",
        typeClass = persistence.MonetaryAmountConversionType.class,
        parameters = { @Parameter(name="convertTo", value="EUR") }
    )
})
  • 위의 설정은 import문 바로 아래 또는 class정의 부분 위나, 별도의 자바 파일, package-info.java 같은 클래스에 둔다.(2.2.1 참조)
  • 다음과 같이 참조해서 사용할 수 있다.
@org.hibernate.annotations.Type(type = "monetary_amount_eur")
@org.hibernate.annotations.Columns({
  @Column(name = "BID_AMOUNT"),
  @Column(name = "BID_AMOUNT_CUR")
})
private MonetaryAmount bidAmount;

'Hibernate > Chapter 5' 카테고리의 다른 글

Hibernate Core VS JPA  (0) 2008.02.04
Enum Type 맵핑하기 - 실습  (0) 2008.02.04
Enum Type 만들기  (0) 2008.02.04
ParameterizedType 만들기  (0) 2008.02.04
CompositeUserType 만들기  (0) 2008.02.04
UserType만들기  (0) 2008.02.04
맵핑 타입 사용하기  (0) 2008.02.04
내장된 맵핑 타입  (0) 2008.02.04
Entity와 Value type 복습  (0) 2008.02.04
상속 맵핑 전략 선택하기  (0) 2008.02.01
상속 맵핑 전략 혼용하기  (0) 2008.02.01
top


CompositeUserType 만들기

Hibernate/Chapter 5 : 2008.02.04 17:19


클래스 구현하기

public class MonetaryAmountCompositeUserType implements CompositeUserType {

	public String[] getPropertyNames() {
		return new String[] { "amount", "currency" };
	}

	public Type[] getPropertyTypes() {
		return new Type[] { Hibernate.BIG_DECIMAL, Hibernate.CURRENCY };
	}

	public Object getPropertyValue(Object component, int property)
			throws HibernateException {
		MonetaryAmount monetaryAmount = (MonetaryAmount) component;
		if (property == 0)
			return monetaryAmount.getAmount();
		else
			return monetaryAmount.getCurrency();
	}

	public Object nullSafeGet(ResultSet resultSet, String[] names,
			SessionImplementor session, Object owner)
			throws HibernateException, SQLException {
		BigDecimal value = resultSet.getBigDecimal(names[0]);
		if (resultSet.wasNull())
			return null;
		Currency currency = Currency.getInstance(resultSet.getString(names[1]));
		return new MonetaryAmount(value, currency);
	}

	public void nullSafeSet(PreparedStatement statement, Object value,
			int index, SessionImplementor arg3) throws HibernateException,
			SQLException {
		if (value == null) {
			statement.setNull(index, Hibernate.BIG_DECIMAL.sqlType());
			statement.setNull(index + 1, Hibernate.CURRENCY.sqlType());
		} else {
			MonetaryAmount amount = (MonetaryAmount) value;
			String currencyCode = amount.getCurrency().getCurrencyCode();
			statement.setBigDecimal(index, amount.getAmount());
			statement.setString(index + 1, currencyCode);
		}
	}

	public void setPropertyValue(Object arg0, int arg1, Object arg2)
			throws HibernateException {
		throw new UnsupportedOperationException("Immutable MonetaryAmount!");
	}

}
  • CompositeUserType 인터페이스를 구현한다.
  • nullSafeSet()은 ResultSet에 담겨있는 두 개의 값을 Monetary 객체의 속성값으로 변환하면 된다.
  • nullSafeSet()은 객체가 가진 두 개의 값을 DB에 저장하도록 statement를 수정한다.
  • getPropertyNames()를 사용해서 Value Type의 속성들을 알려준다.
  • getPropertyValue()를 사용해서 Value Type의 각각의 속성이 가진 값을 알려준다.
  • setPropertyValue()를 사용해서 Value Type의 속성에 값을 설정한다.

맵핑하기

@org.hibernate.annotations.Type(
    type = "persistence.MonetaryAmountUserType"
)
@org.hibernate.annotations.Columns(columns = {
    @Column(name="INITIAL_PRICE"),
    @Column(name="INITIAL_PRICE_CURRENCY", length = 2)
})
private MonetaryAmount initialPrice;
  • 다음과 같이 쿼리를 작성할 수 있다.
from Item i
where i.initialPrice.amount > 100.0
  and i.initialPrice.currency = 'AUD'

'Hibernate > Chapter 5' 카테고리의 다른 글

Hibernate Core VS JPA  (0) 2008.02.04
Enum Type 맵핑하기 - 실습  (0) 2008.02.04
Enum Type 만들기  (0) 2008.02.04
ParameterizedType 만들기  (0) 2008.02.04
CompositeUserType 만들기  (0) 2008.02.04
UserType만들기  (0) 2008.02.04
맵핑 타입 사용하기  (0) 2008.02.04
내장된 맵핑 타입  (0) 2008.02.04
Entity와 Value type 복습  (0) 2008.02.04
상속 맵핑 전략 선택하기  (0) 2008.02.01
상속 맵핑 전략 혼용하기  (0) 2008.02.01
top


UserType만들기

Hibernate/Chapter 5 : 2008.02.04 17:18


클래스 구현하기

public class MonetaryAmountUserType implements UserType {

	public Object assemble(Serializable cached, Object owner)
			throws HibernateException {
		return cached;
	}

	public Object deepCopy(Object value) throws HibernateException {
		return value;
	}

	public Serializable disassemble(Object value) throws HibernateException {
		return (Serializable) value;
	}

	public boolean equals(Object x, Object y) throws HibernateException {
		if (x == y)
			return true;
		if (x == null || y == null)
			return false;
		return x.equals(y);
	}

	public int hashCode(Object x) throws HibernateException {
		return x.hashCode();
	}

	public boolean isMutable() {
		return false;
	}

	public Object nullSafeGet(ResultSet resultSet, String[] names, Object owner)
			throws HibernateException, SQLException {

		BigDecimal valueInUSD = resultSet.getBigDecimal(names[0]);
		if (resultSet.wasNull())
			return null;

		// DB에서 가져온 다음에 원하는 값으로 캐스팅 해서 반환.

		return null;
	}

	public void nullSafeSet(PreparedStatement statement, Object value, int index)
			throws HibernateException, SQLException {
		
		// SQL을 DB에 보내기 전에 statement의 특정 위치에 들어갈 값을 변경한다.

	}

	public Object replace(Object original, Object target, Object owner)
			throws HibernateException {
		return original;
	}

	public Class returnedClass() {
		return MonetaryAmount.class;
	}

	public int[] sqlTypes() {
		return new int[] { Hibernate.BIG_DECIMAL.sqlType() };
	}

}
  • sqlTypes() 메소드는 하이버네이트가 어떤 SQL 컬럼 타입으로 DDL 스키마를 생성할 지 알려준다.
  • returnClass() 메소드는 어떤 자바 타입을 맵핑할 것인지 알려준다.
  • deepCopy()는 스냅샷 객체을 전달해 준다. 따라서, immutable한 타입일 경우에는 그냥 넘기면 된다.
  • disassemble()은 MonetaryAmount를 하이버네이트 2차 캐쉬에 집어 넣을 때 호출된다. 이곳에서 직렬화된 상태로 데이터를 캐슁한다.
  • assemble()은 캐쉬된 상태의 데이터를 MonetaryAmount 객체로 변환한다.
  • replace()는 detached 상태의 객체를 merging할 때 사용한다.
  • equals()는 dirty checking 할 때 사용한다.
  • nullSafeGet()은 ResultSet에서 데이터를 가져올 때 사용한다.
  • nullSafeSet()은 PreparedStatement에 저장할 값을 설정할 때 사용한다.

맵핑하기

@org.hibernate.annotations.Type(
    type = " persistence.MonetaryAmountUserType"
)
@Column(name = "INITIAL_PRICE")
private MonetaryAmount initialPrice;

단점

  • 하이버네이트는 Monetary Amount의 속성을 모른다. 따라서 그 안에 있는 amount나 currency를 가져오는 쿼리를 작성할 수 없다.

해결책

  • CompositeUserType을 사용하면 된다.

'Hibernate > Chapter 5' 카테고리의 다른 글

Hibernate Core VS JPA  (0) 2008.02.04
Enum Type 맵핑하기 - 실습  (0) 2008.02.04
Enum Type 만들기  (0) 2008.02.04
ParameterizedType 만들기  (0) 2008.02.04
CompositeUserType 만들기  (0) 2008.02.04
UserType만들기  (0) 2008.02.04
맵핑 타입 사용하기  (0) 2008.02.04
내장된 맵핑 타입  (0) 2008.02.04
Entity와 Value type 복습  (0) 2008.02.04
상속 맵핑 전략 선택하기  (0) 2008.02.01
상속 맵핑 전략 혼용하기  (0) 2008.02.01
top


맵핑 타입 사용하기

Hibernate/Chapter 5 : 2008.02.04 00:17


  • type 속성을 명시하지 않아도 된다.
  • java.lang.String 타입의 변수는 기본 맵핑으로 string을 사용한다.
  • java.util.Date는 timestamp를 사용한다.
  • JPA를 사용할 때는 @Temporal을 사용해서 정확도를 명시해 준다.
  • 기본값은 TemporalType.TIMESTAMP, (가용한 값은, TIEM, DATE)
  • 하이버네이트 맵핑 타입을 명시적으로 표현하고 싶을 때는 하이버네이트의 @Type 애노테이션 사용.
  • 커스텀 타입도 만들 수 있다.

'Hibernate > Chapter 5' 카테고리의 다른 글

Enum Type 맵핑하기 - 실습  (0) 2008.02.04
Enum Type 만들기  (0) 2008.02.04
ParameterizedType 만들기  (0) 2008.02.04
CompositeUserType 만들기  (0) 2008.02.04
UserType만들기  (0) 2008.02.04
맵핑 타입 사용하기  (0) 2008.02.04
내장된 맵핑 타입  (0) 2008.02.04
Entity와 Value type 복습  (0) 2008.02.04
상속 맵핑 전략 선택하기  (0) 2008.02.01
상속 맵핑 전략 혼용하기  (0) 2008.02.01
Table per subclass  (0) 2008.02.01
top


내장된 맵핑 타입

Hibernate/Chapter 5 : 2008.02.04 00:17


특징

  • ORM은 자바의 타입과 SQL 데이터타입을 중개해 주는 역할을 해야한다.
  • 하이버는 보통 자바 타입 이름을 따르고 있다. 하지만 자바 타입 하나에 여러 하이버네이트 타입이 있을 수 있다.
  • Custom Value Type을 정의할 수 있다.

자바 기본 타입 맵핑

  • 벤더가 ANSI 표준을 따르지 않을 수도 있다. 그래도 JDBC 드라이버는 추상화 계층을 제공해 주니까 이걸 하이버네이트가 읽어 들여서 벤더에 적당한 타입으로 변환해준다.
  • 즉, 사용자는 타입 걱정하지 않아도 된다.
  • 문자열의 길이 제한 설정(length 속성 값)에 따라서 하이버가 적당한 타입으로 결정한다.
  • 이런 전략 자체도 사용자 맘대로 변경할 수 있다.
Java Type Hibernate Mapping Type SQL Type
int, Integer integer INTEGER
long, Long long BIGINT
short, Short short SMALLINT
float, FLOAT float FLOAT
double, Double double DOUBLE
BigDecimal big_decimal NUMERIC
String character CHAR(1)
String string VARCHAR
byte, Byte byte TINYINT
boolean, Boolean boolean BIT
boolean, Boolean yes_no CHAR(1) ('Y' or 'N')
boolean, Boolean true_false CHAR(1) ('T' or 'F')

날짜와 시간 맵핑

  • java.util.Date 타입을 timestamp로 맵핑할 경우 DB에서 나노초까지 값을 가져온다.
  • 책을 쓸 시점에서는 나노초를 짤라주지 않아서, equlas() 메소드로 비교할 경우 Date.getTime() 으로 비교하거나 Custom Mapping Type을 만들어서 사용해야 한다.
Java Type Hibernate Mapping Type SQL Type
java.util.Data, java.sql.Date date DATE
java.util.Date, Java.sql.Time time TIME
java.util.Date, java.sql.Timestamp timetamp TIMESTAMP
java.util.Calendar calendar TIMESTAMP
java.util.Calendar calendar_date DATE

바이너리와 큰 값 맵핑

  • binary 맵핑 타입만 식별자 속성으로 사용할 수 있다.
  • byte[]일 경우에는 VARBINARY로 맵핑하고, String일 경우에는 CLOB로 맵핑할 수 있다.
  • 이 두 경우에 Entity 객체가 속성 값을 읽어들일 때, 초기화를 하려고 할텐데, 너무 큰 값이라서 Lazy loading 하고 싶을 수 있다.
  • 특정 속성을 lazy loading 하는 두 가지 방법이 있다.
    • 13.1.6에서 다루는 Lazy loading with interception
    • java.sql.Clob 나 java.sql.Blob 타입을 사용한다.(로딩하는 시점에 Entity는 Persistent 상태여야 한다.)
  • JPA를 사용할 때 String 타입은 기본으로 VARCHAR로 맵핑 된다. Clob 타입으로 맵핑하려면 @Lob 애노테이션을 사용한다.
  • Serializable 타입은 변수의 값을 바이트 스트림으로 바꿔서 VARBINARY 타입 컬럼에 저장한다. 로딩할 때는 역직렬화한다.
Java Type Hibernate Mapping Type SQL Type
byte[] binary VARBINARY
String text CLOB
java.sql.Clob clob CLOB
java.sql.Blob blob BLOB
java.io.Serializable serializable VARBINARY

JDK 맵핑 타입

  • <property>만 type 속성을 가진 것은 아니다.
Java Type Hibernate Mapping Type SQL Type
java.lang.Class class VARCHAR
java.util.Locale locale VARCHAR
java.util.TimeZone timezone VARCHAR
java.util.Currency currency VARCHAR

'Hibernate > Chapter 5' 카테고리의 다른 글

Enum Type 만들기  (0) 2008.02.04
ParameterizedType 만들기  (0) 2008.02.04
CompositeUserType 만들기  (0) 2008.02.04
UserType만들기  (0) 2008.02.04
맵핑 타입 사용하기  (0) 2008.02.04
내장된 맵핑 타입  (0) 2008.02.04
Entity와 Value type 복습  (0) 2008.02.04
상속 맵핑 전략 선택하기  (0) 2008.02.01
상속 맵핑 전략 혼용하기  (0) 2008.02.01
Table per subclass  (0) 2008.02.01
Table per class hierarchy  (0) 2008.02.01
top


Entity와 Value type 복습

Hibernate/Chapter 5 : 2008.02.04 00:16


특징

Entity Value Type
Coarse-grained Fine-grained
도메인 설명에 잘 나타난다. 잘 나타나지 않는다.
Persistent id를 가지고 있다. 그런거 없고, Entity 클래스에 종속된다.
상태(transient, detached, persistent)가 있다. 이런 상태를 신경쓰지 않는다.
자신만의 라이프 사이클을 가지고 있다. 라이프 사이클은 자신을 소유한 Entity를 따른다.
공유 된다. 공유되지 않는다. Entity는 고유한 Value Type을 가지고 있다.
@Entity로 맵핑한다. @Embeddable로 맵핑한다.

'Hibernate > Chapter 5' 카테고리의 다른 글

ParameterizedType 만들기  (0) 2008.02.04
CompositeUserType 만들기  (0) 2008.02.04
UserType만들기  (0) 2008.02.04
맵핑 타입 사용하기  (0) 2008.02.04
내장된 맵핑 타입  (0) 2008.02.04
Entity와 Value type 복습  (0) 2008.02.04
상속 맵핑 전략 선택하기  (0) 2008.02.01
상속 맵핑 전략 혼용하기  (0) 2008.02.01
Table per subclass  (0) 2008.02.01
Table per class hierarchy  (0) 2008.02.01
Table per concrete class  (0) 2008.02.01
top


상속 맵핑 전략 선택하기

Hibernate/Chapter 5 : 2008.02.01 21:40


맵핑 전략

  • 다형적인 관계나 쿼리가 필요하지 ㅇ낳다면, Table Per Concrete Class를 사용하라. 즉, 상위 타입으로 객체들을 가져다 쓰지 않을거라면...
  • 다형적인 관계나 쿼리가 필요하고, 하위 클래스들의 속성이 몇 개 되지 않는 다면, Table Per Class Hierarchy를 사용하라.
  • 다형적인 관계나 쿼리가 필요하고, 하위 클래스들에 속성들이 많다면 Table Per SubClass를 사용하라.

특징

  • 간단한 경우에는 Table Per Class Hierarchy를 사용하라. 그 다음에 좀 더 복잡한 경우에 Table Per Subclass를 고려하라. 하지만 그렇게 하기 전에 상속구조를 Deligation으로 대체할 수 없을지 고민해 봐라.
  • 하이버네이트가 도메인과 관계형 모델의 버퍼 역할을 해주긴 하지만, 그렇다고 해서 Persistence Concern을 몰라도 된다는 뜻은 아니다.

'Hibernate > Chapter 5' 카테고리의 다른 글

CompositeUserType 만들기  (0) 2008.02.04
UserType만들기  (0) 2008.02.04
맵핑 타입 사용하기  (0) 2008.02.04
내장된 맵핑 타입  (0) 2008.02.04
Entity와 Value type 복습  (0) 2008.02.04
상속 맵핑 전략 선택하기  (0) 2008.02.01
상속 맵핑 전략 혼용하기  (0) 2008.02.01
Table per subclass  (0) 2008.02.01
Table per class hierarchy  (0) 2008.02.01
Table per concrete class  (0) 2008.02.01
Table per connrete class with implicit polymorphism  (0) 2008.02.01
top


상속 맵핑 전략 혼용하기

Hibernate/Chapter 5 : 2008.02.01 21:40


특징

  • 특정 하위 클래스에 대한 맵핑 정랸글 변경할 수 있다.
  • 하이버네이트가 2차 테이블에서 속성들을 가져오도록 설정한다.

맵핑하기

  • 기본으로 Table Per Class Hierachy를 사용하고 특정 하위 클래스만 Table Per Sub Class 사용하기.
  • @SecondaryTable 애노테이션 사용하기.
  • 2차 테이블에는 모든 속성을 명시해 주어야 한다.

'Hibernate > Chapter 5' 카테고리의 다른 글

CompositeUserType 만들기  (0) 2008.02.04
UserType만들기  (0) 2008.02.04
맵핑 타입 사용하기  (0) 2008.02.04
내장된 맵핑 타입  (0) 2008.02.04
Entity와 Value type 복습  (0) 2008.02.04
상속 맵핑 전략 선택하기  (0) 2008.02.01
상속 맵핑 전략 혼용하기  (0) 2008.02.01
Table per subclass  (0) 2008.02.01
Table per class hierarchy  (0) 2008.02.01
Table per concrete class  (0) 2008.02.01
Table per connrete class with implicit polymorphism  (0) 2008.02.01
top


Table per subclass

Hibernate/Chapter 5 : 2008.02.01 21:39


특징

  • 영속화 할 필드를 가진 모든 클래스의 테이블을 만든다.
  • 상위 클래스의 주키를 하위 클래스의 주키이자 외례키로 사용한다.
  • Join을 사용해서 상위 타입으로 하위 타입의 객체를 가져올 수 있다.

장점

  • 정규화를 할 수 있다.
  • 스키마 개선과 무결성 제약 정의가 간단하다.
  • 다형성은 특정 하위 클래스로의 외례키 참조로 가능하다.
  • 구분자 컬럼 필요없다.

단점

  • 복잡한 클래스 구조에서는 성능이 안 좋을 수 있다.

맵핑하기

  • 상위 클래스
@Entity
@Table(name="BILLING_DETAIL")
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class BillingDetails {
  • 하위 클래스
@Entity
public class CreditCard extends BillingDetails {

or

@Entity
@PrimaryKeyJoinColumn(name="CREDIIT_CARD_ID")
public class CreditCard extends BillingDetails {

쿼리 분석

  • 상위 타입으로 요청할 때는 left outter join
select billingdet0_.BILLING_DETAILS_ID as BILLING1_0_, billingdet0_.OWNER as OWNER0_, 
billingdet0_1_.ACCOUNT as ACCOUNT1_, billingdet0_2_.CC_NUMBER as CC2_2_,
case
when billingdet0_1_.BILLING_DETAILS_ID is not null then 1
when billingdet0_2_.BILLING_DETAILS_ID is not null then 2
when billingdet0_.BILLING_DETAILS_ID is not null then 0 end as clazz_
from BILLING_DETAIL billingdet0_
left outer join BankAccount billingdet0_1_ on
billingdet0_.BILLING_DETAILS_ID=billingdet0_1_.BILLING_DETAILS_ID
left outer join CreditCard billingdet0_2_ on
billingdet0_.BILLING_DETAILS_ID=billingdet0_2_.BILLING_DETAILS_ID
  • 하위 타입으로 요청할 때는 inner join.
select bankaccoun0_.BILLING_DETAILS_ID as BILLING1_0_, bankaccoun0_1_.OWNER as OWNER0_, 
bankaccoun0_.ACCOUNT as ACCOUNT1_
from BankAccount bankaccoun0_
inner join BILLING_DETAIL bankaccoun0_1_ on
bankaccoun0_.BILLING_DETAILS_ID=bankaccoun0_1_.BILLING_DETAILS_ID

'Hibernate > Chapter 5' 카테고리의 다른 글

CompositeUserType 만들기  (0) 2008.02.04
UserType만들기  (0) 2008.02.04
맵핑 타입 사용하기  (0) 2008.02.04
내장된 맵핑 타입  (0) 2008.02.04
Entity와 Value type 복습  (0) 2008.02.04
상속 맵핑 전략 선택하기  (0) 2008.02.01
상속 맵핑 전략 혼용하기  (0) 2008.02.01
Table per subclass  (0) 2008.02.01
Table per class hierarchy  (0) 2008.02.01
Table per concrete class  (0) 2008.02.01
Table per connrete class with implicit polymorphism  (0) 2008.02.01
top


Table per class hierarchy

Hibernate/Chapter 5 : 2008.02.01 21:38


특징

  • 전체 상속 구조 하나를 단일 클래스로 맵핑한다.
  • 하위 클래스의 객체들은 타입 구분자 Type Discriminator 컬럼의 값으로 구분한다.
  • 구분자를 추가할 수 없는 상황에서는 formula를 사용한다. SQL의 CASE/WHERN

장점

  • 다형적이거나 그렇지 않은 쿼리를 수행하는 성능이 좋다. <- 조인이나 Union을 하지 않으니까.
  • 구현하기도 간편하다. <- 스키마가 간단하니까.

단점

  • 하위 클래스의 속성들은 반드시 nullable이어야 한다.
  • 정규화가 되지 않는다. 키가 아닌 값에 종속성에 생기므로, 3차 정규화를 깨트렸다. DBA가 싫어할 것이다.

맵핑하기

  • 상위 클래스(discriminator 사용)
@Entity
@Table(name="BILLING_DETAIL")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="BILLING_DETAILS_TYPE", discriminatorType = DiscriminatorType.STRING)
public abstract class BillingDetails {

@Id
@GeneratedValue(strategy=GenerationType.SEQUENCE)
@Column(name = "BILLING_DETAILS_ID")
private Long id = null;

@Column(name = "OWNER", nullable = false)
private String owner;
  • 상위 클래스(formula 사용)
@Entity
@Table(name="BILLING_DETAIL")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorFormula("case when CC_NUMBER is not null then 'CC' else 'BA' end")
public abstract class BillingDetails {
  • 하위 클래스
@Entity
@DiscriminatorValue("CC")
public class CreditCard extends BillingDetails {

@Column(name = "CC_NUMBER")
private String number;

쿼리 분석

  • 상위 타입으로 가져올 때는 조건 없이 모든 컬럼 가져온다.
List<BillingDetails> bdList = session.createQuery("from BillingDetails").list();

select billingdet0_.BILLING_DETAILS_ID as BILLING2_0_, billingdet0_.OWNER as OWNER0_, billingdet0_.ACCOUNT as ACCOUNT0_, billingdet0_.CC_NUMBER as CC5_0_, billingdet0_.BILLING_DETAILS_TYPE as BILLING1_0_
from BILLING_DETAIL billingdet0_
  • 하위 타입으로 가져올 때는 Discriminator 사용해서 조건을 준다.
List<CreditCard> bdList = session.createQuery("from CreditCard").list();

select creditcard0_.BILLING_DETAILS_ID as BILLING2_0_, creditcard0_.OWNER as OWNER0_, creditcard0_.CC_NUMBER as CC5_0_
from BILLING_DETAIL creditcard0_
where creditcard0_.BILLING_DETAILS_TYPE='CC'

'Hibernate > Chapter 5' 카테고리의 다른 글

CompositeUserType 만들기  (0) 2008.02.04
UserType만들기  (0) 2008.02.04
맵핑 타입 사용하기  (0) 2008.02.04
내장된 맵핑 타입  (0) 2008.02.04
Entity와 Value type 복습  (0) 2008.02.04
상속 맵핑 전략 선택하기  (0) 2008.02.01
상속 맵핑 전략 혼용하기  (0) 2008.02.01
Table per subclass  (0) 2008.02.01
Table per class hierarchy  (0) 2008.02.01
Table per concrete class  (0) 2008.02.01
Table per connrete class with implicit polymorphism  (0) 2008.02.01
top


Table per concrete class

Hibernate/Chapter 5 : 2008.02.01 21:37


특징

  • 상위 클래스에 하위 클래스들을 Union으로 포함하여 가지고 있게 설정한다.
  • JPA에서 TABLE_PER_CLASS는 필수 구현사항이 아니다. 따라서 밴더마다 다를 수 있다.
  • Hibernate의 polymorphic loader engine이 쿼리의 성능 걱정을 덜어 줄 것이다.
  • 하위 클래스들이 상위 클래스에 정의한 하나의 주키를 공유한다.
  • 주키 생성 타입을 identity로 하면 안 된다. AUTO로 설정했을 때 주의할 것. 안 그러면 Cannot use identity column key generation with <union-subclass> mapping. 이 에러를 만나게 될 것이다.

장점

  • 다형적인 관계를 다룰 수 있다. UNION 쿼리를 사용해서 단일 테이블을 관계 맵핑 처럼 사용할 것이다.(?? 7.3 참조)
  • 지금까지는 SQL 스키마는 전혀 상속을 위한 추가 설정이 필요하지 않았다. 정규화도 잘 되었고 추가적인 외례키를 필요도 하지도 않았다.

맵핑하기

  • 상위 클래스
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class BillingDetails {

@Id
@GeneratedValue(strategy=GenerationType.SEQUENCE)
@Column(name = "BILLING_DETAILS_ID")
private Long id = null;

@Column(name = "OWNER", nullable = false)
private String owner;
  • 하위 클래스
@Entity
@Table(name="CREDIT_CARD")
public class CreditCard extends BillingDetails {

@Column(name = "NUMBER", nullable = false)
private String number;

쿼리 분석

select BILLING_DETAILS_ID, OWNER, ACCOUNT, NUMBER, clazz_
from
( select OWNER, ACCOUNT, null as NUMBER, BILLING_DETAILS_ID, 1 as clazz_ from BANK_ACCOUNT
union
select OWNER, null as ACCOUNT, NUMBER, BILLING_DETAILS_ID, 2 as clazz_ from CREDIT_CARD )
  • clazz_ 컬럼: 컬렉션에서 하위 타입의 객체를 만들 때 구분하기 위해서 사용.
  • null 인 컬럼들(BANK_ACCOUNT의 NUMBER, CREDIT_CARD의 ACCOUNT): UNION을 사용하기 위해서는 같은 컬럼을 가지고 있어야 한다.

'Hibernate > Chapter 5' 카테고리의 다른 글

CompositeUserType 만들기  (0) 2008.02.04
UserType만들기  (0) 2008.02.04
맵핑 타입 사용하기  (0) 2008.02.04
내장된 맵핑 타입  (0) 2008.02.04
Entity와 Value type 복습  (0) 2008.02.04
상속 맵핑 전략 선택하기  (0) 2008.02.01
상속 맵핑 전략 혼용하기  (0) 2008.02.01
Table per subclass  (0) 2008.02.01
Table per class hierarchy  (0) 2008.02.01
Table per concrete class  (0) 2008.02.01
Table per connrete class with implicit polymorphism  (0) 2008.02.01
top


Table per connrete class with implicit polymorphism

Hibernate/Chapter 5 : 2008.02.01 21:37


특징

  • 모든 구현 클래스 하나 당 하나의 테이블로 표현한다.
  • 상속 받은 속성들도 테이블의 컬럼으로 맵핑한다.

문제점

  • 다형적인 관계를 잘 지원하지 못해. 왜? 상위 클래스와 관계를 맺고 있는 클래스가 하위 클래스를 참조해야 한다면, 이 클래스가 가져야 할 하위 클래스의 외례키는 두 개의 하위 클래스 모두의 주키를 대변해야 할 텐데, 이런게 테이블에서는 불가능하다.
  • 다형적인 쿼리(특정 상위 클래스 타입에 해당하는 모든 객체를 요구하는 쿼리)도 역시 문제가 된다. 왜? 각각의 하위 클래스마다 쿼리를 수행해서 데이터를 가져와야 한다.
  • 약간 다른 여러 테이블이 같은 정보를 공유하고 있다. 이게 왜 문제가 될까? 스키마를 점점 복잡하게 만든다. 예를들어, 상위 타입의 속성 하나를 변경하면 그것을 상속하나 모든 테이블을 수정해야 한다. 모든 하위 클래스에 무결성 제약을 적용하기도 힘들다.
  • 변경될 가능성이 거의 없고, 다형성이 별로 필요 없는, 최상위 레벨의 상속구조에서 사용하기를 권장한다.
  • JPA는 다형적인 쿼리를 지원하지 않는다. 하지만 Hibernate의 쿼리 인터페이스는 지원한다.

해결책

  • SQL의 UNION 함수를 사용해서 위에서 발생한 다형적인 쿼리와 관계에 대한 이슈 대부분을 제거할 수 있다.

맵핑하기

  • 하위 클래스는 @Entity로 맵핑하기
@Entity
@AttributeOverride(name = "owner", column = @Column(name = "CC_OWNER", nullable = false))
public class CreditCard extends BillingDetails {

@Id
@GeneratedValue
@Column(name = "CREDIT_CARD_ID")
private Long id = null;

@Column(name = "NUMBER", nullable = false)
private String number;
  • 상위 클래스는 @MappedSuperclass로 맵핑하기
@MappedSuperclass
public class BillingDetails {

@Column(name = "OWNER", nullable = false)
private String owner;

'Hibernate > Chapter 5' 카테고리의 다른 글

CompositeUserType 만들기  (0) 2008.02.04
UserType만들기  (0) 2008.02.04
맵핑 타입 사용하기  (0) 2008.02.04
내장된 맵핑 타입  (0) 2008.02.04
Entity와 Value type 복습  (0) 2008.02.04
상속 맵핑 전략 선택하기  (0) 2008.02.01
상속 맵핑 전략 혼용하기  (0) 2008.02.01
Table per subclass  (0) 2008.02.01
Table per class hierarchy  (0) 2008.02.01
Table per concrete class  (0) 2008.02.01
Table per connrete class with implicit polymorphism  (0) 2008.02.01
top