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