Whiteship's Note


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

Write a comment.


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

Write a comment.


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

Write a comment.


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

Write a comment.