I am using Hibernate 4.0. I have create CompositeUserType as following
Code:
public class MoneyType implements CompositeUserType {
@Override
public String[] getPropertyNames() {
// ORDER IS IMPORTANT! it must match the order the columns are defined in the property mapping
return new String[] { "amount", "currency" };
}
@Override
public Type[] getPropertyTypes() {
return new Type[] { StringType.INSTANCE, StringType.INSTANCE };
}
public Class getReturnedClass() {
return Money.class;
}
@Override
public Object getPropertyValue(Object component, int propertyIndex) {
if ( component == null ) {
return null;
}
final Money money = (Money) component;
switch ( propertyIndex ) {
case 0: {
return money.getAmount();
}
case 1: {
return money.getCurrency();
}
default: {
throw new HibernateException( "Invalid property index [" + propertyIndex + "]" );
}
}
}
@Override
public void setPropertyValue(Object component, int propertyIndex, Object value) throws HibernateException {
throw new UnsupportedOperationException("Immutable!");
}
@SuppressWarnings("unused")
@Override
public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) throws SQLException {
assert names.length == 2;
BigDecimal amount = new BigDecimal(decrypt((String)StringType.INSTANCE.get(rs, names[0], session)));
Currency currency = Currency.getInstance(decrypt((String)StringType.INSTANCE.get( rs, names[1], session)));
if(amount==null && currency == null)
{
return null;
}
else if(amount == null)
{
return new Money(currency);
}
return new Money(amount);
}
@Override
public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) throws SQLException {
if ( value == null ) {
StringType.INSTANCE.set( st, null, index, session );
StringType.INSTANCE.set( st, null, index+1, session );
}
else {
final Money money = (Money) value;
StringType.INSTANCE.set( st, encrypt(money.getAmount()), index, session );
StringType.INSTANCE.set( st, encrypt(money.getCurrency()), index+1, session );
}
}
private String encrypt(Object data)
{
return data.toString();
}
private String decrypt(String data)
{
return data;
}
@Override
public Class returnedClass()
{
return Money.class;
}
@Override
public boolean equals(Object x, Object y) throws HibernateException
{
return x.equals(y);
}
@Override
public int hashCode(Object x) throws HibernateException
{
return x.hashCode();
}
@Override
public Object deepCopy(Object value) throws HibernateException
{
// TODO Auto-generated method stub
return null;
}
@Override
public boolean isMutable()
{
return false;
}
@Override
public Serializable disassemble(Object value, SessionImplementor session) throws HibernateException
{
return (Serializable) value;
}
@Override
public Object assemble(Serializable cached, SessionImplementor session, Object owner) throws HibernateException
{
return cached;
}
@Override
public Object replace(Object original, Object target, SessionImplementor session, Object owner) throws HibernateException
{
// TODO Auto-generated method stub
return null;
}
}
Then I want to use this MoneyType inside my Price class as follows
Code:
@Entity
@Table(name = "price")
@TypeDef(name = "moneyType", typeClass = MoneyType.class)
public class Price
{
private Money money;
/**
* @return The money.
*/
@Embedded
@Type(type="moneyType")
@Columns(columns = { @Column(name = "amount"),
@Column(name = "currency")})
public Money getMoney()
{
return money;
}
/**
* @param money The money to set.
*/
public void setMoney(Money money)
{
this.money = money;
}
}
And Money class looks as follows
Code:
@Embeddable
public final class Money implements Cloneable, Comparable<Money>, Serializable
{
private final Currency currency;
private final BigDecimal amount;
public Money()
{
this(BigDecimal.ZERO);
}
/**
* Sets the amount for this instance. The currency is set to the currency
* assigned to default system locale.
*
* @param amount A {@link BigDecimal}.
*/
public Money(final BigDecimal amount)
{
this(Currency.getInstance(Locale.getDefault()), amount);
}
/**
* Sets the amount for this instance. The currency is set to the currency
* assigned to default system locale.
*
* @param amount A {@link BigInteger}.
*/
public Money(final BigInteger amount)
{
this(new BigDecimal(amount));
}
/**
* Sets the currency for this instance. The amount is set to zero.
*
* @param currency A {@link Currency}.
*/
public Money(final Currency currency)
{
this(currency, BigDecimal.ZERO);
}
/**
* Sets the currency and amount for this instance.
*
* @param currency A {@link Currency}.
* @param amount A {@link BigDecimal}.
*/
public Money(final Currency currency, final BigDecimal amount)
{
if (currency == null)
{
throw new RuntimeException("Currency cannot be null.");
}
if (amount == null)
{
throw new RuntimeException("Amount cannot be null.");
}
this.currency = currency;
this.amount = amount.setScale(currency.getDefaultFractionDigits(), ROUNDING_MODE);
}
/**
* Gets the amount for this instance.
*
* @return The amount for this instance.
*/
public BigDecimal getAmount()
{
return this.amount.abs();
}
/**
* Gets the currency for this instance.
*
* @return The currency for this instance.
*/
public Currency getCurrency()
{
return this.currency;
}
}
In this case I found that even after defining
@TypeDef(name = "moneyType", typeClass = MoneyType.class)
And
@Embedded
@Type(type="moneyType")
@Columns(columns = { @Column(name = "amount"),
@Column(name = "currency")})
public Money getMoney()
{
return money;
} Money in my case is not getting persisted as per my defined CompositeUserType MoneyType. I never get control in MoneyType whenever I try to save/retrieve Price.
I found that MoneyType is not getting applied on money in Price class.
Am I missing anything here? Is there anyother configuration here which I am missing?