Hibernate Core: 3.2.0 CR2
Hibernate Entity Manager: 3.2.0 CR1
Hibernate Annotations: 3.2.0 CR1
Database: Oracle 10g
Our project has multiple currencies. We have Money object which has attributes amount and currency (similar to the example in CaveatEmptor’s MonetaryAmount.java). However, we do not want to store currency in each table corresponding to each money attribute. We would like to have a subgraph of entities with Money objects in various entities but the currency is stored in only one entity of the subgraph. For e.g. some of the entities of the subgraph are ServiceCharge (root object of subgraph), ServiceChargeLine, ServiceChargeContextEventLine, ServiceChargeContextAgencyLine. These entities are connected (traversable with bidirectional relationship). Some of these entities have multiple Money objects. The corresponding database tables have amount part of the money. But the currency of Money objects of these entities is present in ServiceCharge (one entity of sub graph) only.
When we retrieve (from database) any entity that has Money object as attribute, we get the amount from the corresponding table, but the currency has to be retrieved by traversing object graph to the object that has currency. While persisting an entity that has Money attribute, the currency part of Money is not persisted in database.
We have an Interface CurrencyRetrieval which is implemented by each entity with Money attribute(s). This interface has a methods such as getCurrency(). This method traverses the object graph to retrieve the currency.
The custom UserType class MoneyType cannot be used to create Money objects with correct currency as the ‘owner’ object parameter of nullsafeget() method is not hydrated by the time it is called. So we cannot traverse the object graph to get the currency.
I tried to use EJB 3.0 PostLoad event callback method to retrieve the currency and set it in all money attributes of an entity. This approach works while retrieving the entities (using EntityManager’ find(class, PrimiaryKey)). However, it does not work when we update the entity graph using EntityManager.merge(entity). It seems when we call merge(), Hibernate is loading ServiceChargeLine first and calling PostLoad call back method before hydrating its parent object ServiceCharge.
Questions:
1) Why is the PostLoad method/hydration working fine when we call EntityManager.find(), but not working when we call EntityManager.merge()?
2) How can we make it work in both find() and merge()?
3) Please suggest any other suggestions/ideas to retrieve Currency part of Money from another entity?
Any suggestions, code samples are greatly appreciated.
Simplified code I am using is:
Code:
public interface CurrencyRetrieval {
public String getCurrencyCode();
public void setCurrencyInMoneyObjects();
}
@Entity
@Table(name="SERVICE_CHARGE_LINE")
public class ServiceChargeLine implements CurrencyRetrieval{
@Id @Column(name = "SERVICE_CHARGE_LINE_ID")
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SERVICE_CHARGE_LINE_ID_GEN")
private Long id;
@ManyToOne(targetEntity = ServiceCharge.class, optional=false, fetch = FetchType.EAGER)
@JoinColumn(name="SERVICE_CHARGE_ID")
private ServiceCharge serviceCharge;
@Column(name="CHARGEABLE_AMOUNT", columnDefinition = "NUMBER(22,5)", nullable = true)
@Type(type = "MoneyType")
private Money changeableAmount;
/* Traverse the object graph to retrieve the Currency code
// THIS IS GIVING CURRENCY CODE AS NULL, IF WE CALL ENTITYMANAGER's MERGE().
// THE ATTRIBUTES OF PARENT OBJECT 'SERVICECHARGE' ARE NOT HYDRATED EXCEPT THE PRIMARY KEY.
// THE HYDRATION OF PARENT IS DONE WHEN WE CALL ENTITYMANAGER.FIND() IS CALLED.
*/
public String getCurrencyCode(){
return serviceCharge.getCurrencyCode();
}
// Sets the currency in Money attributes of this entity
public void setCurrencyInMoneyObjects(){
String currencyCode = getCurrencyCode();
Money.setCurrency(changeableAmount, currencyCode);
}
}
// Entity having currency code.
@Entity
@Table(name="SERVICE_CHARGE")
public class ServiceCharge {
@Id @Column(name = "SERVICE_CHARGE_ID")
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SERVICE_CHARGE_ID_GEN")
private Long id;
@Column(name = "CURRENCY_CODE", length = 3, nullable = false)
private String currencyCode;
@OneToMany(targetEntity = ServiceChargeLine.class, mappedBy = "serviceCharge", cascade = CascadeType.ALL)
@JoinColumn(name = "SERVICE_CHARGE_ID", nullable = false)
private Collection<ServiceChargeLine> serviceChargeLines;
}
Thanks,
Srinivas