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

来源:互联网 发布:淘宝店铺解封 编辑:程序博客网 时间:2024/05/16 09:30
创建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的PreparedStatementpublic 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 iwhere 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;
2 0
原创粉丝点击