Hibernate实战_笔记33(创建CompositeUserType、参数化定制类型)

创建CompositeUserType
public class MonetaryAmountCompositeUserType implements CompositeUserType{

	@SuppressWarnings("unchecked")
	public Class returnedClass() {
		return MonetaryAmount.class;
	}

	public boolean isMutable() {
		return false;
	}

	//创建值的快照
	public Object deepCopy(Object value) throws HibernateException {
		return value;
	}

	//以序列化的形式保存信息的高速缓存
	public Serializable disassemble(Object value, SessionImplementor session)
			throws HibernateException {
		return (Serializable)value;
	}

	//高速缓存的数据转变为MonetaryAmount的一个实例
	public Object assemble(Serializable cached, SessionImplementor session,
			Object owner) throws HibernateException {
		return cached;
	}

	//处理脱管对象状态的合并
	public Object replace(Object original, Object target,
			SessionImplementor session, Object owner) throws HibernateException {
		return original;
	}
	
	public boolean equals(Object x, Object y) throws HibernateException {
		if (x == y)
			return true;
		if (null == x || null == y)
			return false;
		return x.equals(y);
	}
	
	public int hashCode(Object x) throws HibernateException {
		return x.hashCode();
	}
	
	//从JDBC的ResultSet获取属性值
	public Object nullSafeGet(ResultSet rs, String[] names,
			SessionImplementor session, Object owner)
			throws HibernateException, SQLException {
		BigDecimal value = rs.getBigDecimal(names[0]);
		if(rs.wasNull()) return null;
		Currency currency = Currency.getInstance(rs.getString(names[1]));
		return new MonetaryAmount(value,currency);
	}
	
	//把属性值写到JDBC的PreparedStatement
	public void nullSafeSet(PreparedStatement st, Object value, int index,
			SessionImplementor session) throws HibernateException, SQLException {
		if(null == value){
			st.setNull(index, BigDecimalType.INSTANCE.sqlType());
			st.setNull(index+1, CurrencyType.INSTANCE.sqlType());
		}else{
			MonetaryAmount amount = (MonetaryAmount)value;
			String currencyCode = amount.getCurrency().getCurrencyCode();
			st.setBigDecimal(index,amount.getAmount());
			st.setString(index+1, currencyCode);
		}
	}

	//公开值类型属性
	public String[] getPropertyNames() {
		return new String[] { "amount", "currency" };
	}
	
	//属性类型
	public Type[] getPropertyTypes() {
		return new Type[]{BigDecimalType.INSTANCE,CurrencyType.INSTANCE};
	}
	
	//返回MonetaryAmount的单个属性的值
	public Object getPropertyValue(Object component, int property)
			throws HibernateException {
		MonetaryAmount monetaryAmount = (MonetaryAmount)component;
		if(property == 0){
			return monetaryAmount.getAmount();
		}else{
			return monetaryAmount.getCurrency();
		}
	}
	
	public void setPropertyValue(Object component, int property, Object value)
			throws HibernateException {
		throw new UnsupportedOperationException("Immutable MonetaryAmount!");
	}
}
initialPrice属性现在映射到两个列,因此在映射文件中要对两者都进行声明。第一列保存值;第二列保存MonetaryAmount的货币类型:
<property name="initialPrice"
    type="persistence.MonetaryAmountCompositeUserType">
    <column name="INITIAL_PRICE"/>
    <column name="INITIAL_PRICE_CURRENCY"/>
</property>
如果通过注解映射Item,就必须给这个属性声明几个列。无法多次使用javax.persistence.Column注解,因此需要一个特定于Hibernate的新注解:
@org.hibernate.annotations.Type(
    type = "persistence.MonetaryAmountUserType"
)
@org.hibernate.annotations.Columns(columns = {
    @Column(name = "INITIAL_PRICE"),
    @cOLUMN(NAME = "INITIAL_PRICE_CURRENCY", length = 2)
})
在Hibernate查询中,现在可以引用定制类型的amount和currency属性,即使它们没有作为单独的属性出现在映射文件的任何地方:
from Item i
where i.initialPrice.amount > 100.0
    and i.initiaPrice.currency = ‘AUD‘

参数化定制类型

假设你再次面临最初的问题:当把金额保存到数据库中时把它转化为一种不同的货币类型。这些问题经常比一般的转化更为微妙;例如,可以在一些表中保存美元,而在其他表中保存欧元。你仍然想要给它编写单个定制的映射类型,使它可以进行任意的转化。如果把ParameterizedType接口添加到UserType或者CompositeUserType类,这是可能的:
public class MonetaryAmountConversionType implements UserType,
		ParameterizedType {
	
	private Currency convertTo;

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

	public int[] sqlTypes() {
		return new int[] { BigDecimalType.INSTANCE.sqlType(),
				StringType.INSTANCE.sqlType() };
	}

	@SuppressWarnings("unchecked")
	public Class returnedClass() {
		return MonetaryAmount.class;
	}

	public boolean isMutable() {
		return false;
	}

	// 创建值的快照
	public Object deepCopy(Object value) throws HibernateException {
		return value;
	}

	// 以序列化的形式保存信息的高速缓存
	public Serializable disassemble(Object value, SessionImplementor session)
			throws HibernateException {
		return (Serializable) value;
	}

	// 高速缓存的数据转变为MonetaryAmount的一个实例
	public Serializable disassemble(Object value) throws HibernateException {
		return (Serializable) value;
	}
	
	public Object assemble(Serializable cached, Object owner)
			throws HibernateException {
		return cached;
	}

	// 处理脱管对象状态的合并
	public Object replace(Object original, Object target, Object owner)
			throws HibernateException {
		return original;
	}

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

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

	public Object nullSafeGet(ResultSet rs, String[] names, Object owner)
			throws HibernateException, SQLException {
		BigDecimal value = rs.getBigDecimal(names[0]);
		if(rs.wasNull()) return null;
		Currency currency = Currency.getInstance(rs.getString(names[1]));
		return new MonetaryAmount(value, currency);
	}
	
	public void nullSafeSet(PreparedStatement st, Object value, int index)
			throws HibernateException, SQLException {
		if(null == value){
			st.setNull(index, BigDecimalType.INSTANCE.sqlType());
			st.setNull(index+1, CurrencyType.INSTANCE.sqlType());
		}else{
			MonetaryAmount amount = (MonetaryAmount)value;
			st.setBigDecimal(index, amount.convert(convertTo));
			st.setString(index+1, amount.getCurrency().getCurrencyCode());
		}
	}
}
应用定制映射类型时,现在必须在映射文件中设置配置参数。一种简单的解决方案是属性中嵌套着的<type>映射:
<property name="initialPrice">
    <column name="INITIAL_PRICE"/>
    <column name="INITIAL_PRICE_CUR"/>
    <type name="persistence.MonetaryAmountConversionType">
        <param name="convertTo">USD</param>
    </type>
</property>
然而,如果在你的领域模型中有许多个货币金额时,这样很不方便,并且需要复制。一种更好的策略是使用类型的单个定义,包括所有参数,用一个可以在所有映射中重用的唯一名称。通过单个的<typedef>元素(你也可以不通过参数使用它)完成这一占为:
<typedef name="monetary_amount_usd" class="persistence.MonetaryAmountConversionType">
    <param name="convertTo">USD</param>
</typedef>
<typedef name="monetary_amount_eur" class="persistence.MonetaryAmountConversionType">
    <param name="convertTo">EUR</param>
</typedef>
这里介绍的是,把带有一些参数的定制映射类型绑定到名称monetary_amount_usd和monetary_amount_eur上。这个定义可以放在映射文件中的任何位置;它是<hibernate-mapping>的一个子元素。利用Hibernate扩展,可以在注解中定义带有参数定制类型:
@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")}
	)
})
这个注解元数据也是全局的,因此可以放在任何Java类声明之外或者在一个单独的文件package-info.java中。
在XML映射文件和注解映射中,现在引用被定义的类型名称,而不是定制类型完全限定的类名:
<property name = "initialPrice"
    type = "monetary_amount_usd">
    <column name="INITIAL_PRICE"/>
    <column name="INITIAL_PRICE_CUR"/>
</property>
@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实战_笔记33(创建CompositeUserType、参数化定制类型),古老的榕树,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。