Hello there:
the application I'm working on requires two entities with a one-to-one relationship, with one end of the relationship being polymorphic. For the sake of illustration, I'll use a variation of an example from the "Java Persistence with Hibernate" book. Consider the entities from section "7.3.1 Polymorphic many-to-one associations":
Code:
+-----------+ default +------------------+
| User | ------------> | BillingDetails |
+-----------+ | < abstract > |
+------------------+
^
|
+-----------------------+
| |
| |
+-----------------+ +-------------------+
| CreditCard | | BankAccount |
+_________________+ +___________________+
Here, "default" is a one-to-one relationship that may vary over time: a User may start with a CreditCard instance as its default BillingDetails, but later this can be changed so that a BankAccount instance becomes the default. At any one time, only one concrete BillingDetails subclass should be associated with the User.
My database schema is using a single table per-class-hierarchy strategy along the lines of:
Code:
+-------------------------+ +--------------------------+
| USER | | BILLING_DETAILS |
|-------------------------| |--------------------------|
| FK : billing_details_id | ----------> | PK : billing_details_id |
|-------------------------| |--------------------------|
| PK : user_id | | billing_type |
+-------------------------+ +--------------------------+
where "billing_type" is the discriminator column identifying whether the record corresponds to a CreditCard or BankAccount instance.
In summary, at any one time, there should be exactly one record in BILLING_DETAILS table for each record in USER.
I'm using the following xml Hibernate mapping files:
User.hbm.xml:
=========
Code:
<hibernate-mapping package="com.foo.domain">
<class name="User" table="USER">
<id name="id" column="user_id">
<generator class="native" />
</id>
<!-- user properties go here -->
<many-to-one name="billingDetails" class="BillingDetails"
column="billing_details_id" cascade="save-update"
unique="true" lazy="false" />
</class>
</hibernate-mapping>
BillingDetails.hbm.xml:
==============
Code:
<hibernate-mapping package="com.foo.domain">
<class name="BillingDetails" table="BILLING_DETAILS">
<id name="id" column="billing_details_id">
<generator class="native" />
</id>
<discriminator column="billing_type" />
<!-- common billing details properties go here -->
<subclass name="CreditCard" discriminator-value="CC">
<!-- credit card specific properties go here -->
</subclass>
<subclass name="BankAccount" discriminator-value="BA">
<!-- bank account specific properties go here -->
</subclass>
</class>
</hibernate-mapping>
Creation works as expected, but I'm having problems with updates. Consider the following scenario: a User instance with a BankAccount exists in the database and the application receives an update request to replace the User's BankAccount with a CreditCard instance:
Code:
// Session is created and new transaction is opened
// Get the User from the db: user has BankAccount instance attached
User user = getSession().get( User.class, 55 );
// creditCard is a detached instance of type CreditCard passed in as a parameter:
// merge it with the the loaded billing details attached to the user
BillingDetails newDetails = (BillingDetails)getSession().merge( creditCard );
// Update user with new billing details (credit card)
user.setBillingDetails( newDetails );
// Persist changes to user
getSession().saveOrUpdate( user );
// Transaction is committed and session closed
For simplicity' sake, I extracted the steps above from various classes (SessionFilter, UserService, UserDao), just to illustrate the flow.
Before the update, the data looks like:
user:
----
user_id = 55
billing_details_id = 56
billing_details:
---------------
billing_details_id = 56
billing_type = BA
After the update, I get:
user:
----
user_id = 55
billing_details_id = 57
billing_details:
---------------
billing_details_id = 56
billing_type = BA
billing_details_id = 57
billing_type = CC
Notice that the old billing_details record (id=56) still exists in the db and a new billing_details record (id=57) was created and associated with the user.
Ideally, what I want is to update the existing billing_details record with the new information, so that the db ends up like:
user:
----
user_id = 55
billing_details_id = 56
billing_details:
---------------
billing_details_id = 56
billing_type = CC
I am clearly doing something wrong, so I'd be happy to hear any suggestions/recommendations that anyone can give.
Let me know if I've left out any relevant information that may help clarify things.
Thank you in advance for any help.
Regards