Create CompositeUserType
Public class MonetaryAmountCompositeUserType implements CompositeUserType {@ SuppressWarnings ("unchecked") public Class returnedClass () {return MonetaryAmount. class;} public boolean isMutable () {return false;} // snapshot of the created value public Object deepCopy (Object value) throws HibernateException {return value ;} // cache for storing information in serialized form public Serializable disassemble (Object value, SessionImplementor session) throws HibernateExc Eption {return (Serializable) value;} // convert the cached data to a public Object assemble (Serializable cached, SessionImplementor session, Object owner) instance of MonetaryAmount) throws HibernateException {return cached;} // process the merged public Object replace (Object original, Object target, SessionImplementor session, Object owner) throws HibernateException {return original ;} public boolean equals (Object x, Object y) throws Hiber NateException {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 ();} // obtain the attribute value from the 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 () re Turn null; Currency currency = Currency. getInstance (rs. getString (names [1]); return new MonetaryAmount (value, currency);} // write the attribute value to the 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 value type attribute public String [] getPropertyNames () {return new String [] {"amount ", "currency" };}// attribute Type public Type [] getPropertyTypes () {return new Type [] {BigDecimalType. INSTANCE, CurrencyType. INSTANCE} ;} // Return MonetaryAmount's single attribute value 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 UnsupportedOperatio NException ("Immutable MonetaryAmount! ");}}
The initialPrice attribute is now mapped to two columns. Therefore, you must declare both columns in the ing file. The first column stores the value; the second column stores the currency type of MonetaryAmount:
If you map items through annotations, you must declare several columns for this attribute. The javax. persistence. Column annotation cannot be used multiple times. Therefore, a new annotation specific to Hibernate is required:
@org.hibernate.annotations.Type( type = "persistence.MonetaryAmountUserType")@org.hibernate.annotations.Columns(columns = { @Column(name = "INITIAL_PRICE"), @cOLUMN(NAME = "INITIAL_PRICE_CURRENCY", length = 2)})
In Hibernate queries, you can now reference custom types of amount and currency attributes, even if they do not appear as individual attributes anywhere in the ing file:
from Item iwhere i.initialPrice.amount > 100.0 and i.initiaPrice.currency = 'AUD'
Parameterized custom types assume that you once again face the initial problem: when you save the amount to the database, convert it into a different currency type. These problems are often more subtle than general conversions; for example, you can save the dollar in some tables, while saving the euro in other tables. You still want to write a single custom ing type for it so that it can be converted at will. It is possible to add the ParameterizedType interface to the UserType or CompositeUserType class:
Public class MonetaryAmountConversionType implements UserType, ParameterizedType {private Currency convertize; public void setParameterValues (Properties parameters) {this. convertion = Currency. getInstance (parameters. getProperty ("convertize");} 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;} // snapshot of the created value public Object deepCopy (Object value) throws HibernateException {return value ;} // cache for storing information in serialized form public Serializable disassemble (Object value, SessionImplementor session) throws HibernateException {return (Serializable) value ;} // convert the cached data to an instance of MonetaryAmount public Serializable disassemble (Object value) throws HibernateException {return (Serializable) value;} public Object assemble (Serializable cached, Object owner) throws HibernateException {return cached;} // merge public Object replace (Object original, Object target, Object owner) to process the status of the unmanaged Object 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 (convertize); st. setString (index + 1, amount. getCurrency (). getCurrencyCode ());}}}
When customizing the ing type for an application, you must set the configuration parameters in the ing file. A simple solution is embedded in attributes. Ing:
USD
However, if you have many currency amounts in your domain model, this is inconvenient and requires replication. A better policy is to use a single definition of the type, including all parameters, with a unique name that can be reused in all mappings. Through a single Element (you can also use it without passing the parameter) to complete this proportion:
USD
EUR
The following describes how to bind custom ing types with some parameters to the names monetary_amount_usd and monetary_amount_eur. This definition can be placed anywhere in the ing file; it is . With the Hibernate extension, you can define a custom type with parameters in the annotation:
@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")})})
This annotation metadata is also global, so it can be placed out of any Java class Declaration or in a separate file package-info.java.
In the XML ing file and annotation ing, the name of the defined type is referenced, instead of the class name that is fully qualified for the custom type:
@org.hibernate.annotations.Type(type = "monetary_amount_eur")@org.hibernate.annotations.Columns({ @Column(name = "BID_AMOUNT"), @Column(name = "BID_AMOUNT_CUR")})private MonetaryAmount bidAmount;