tags:

views:

399

answers:

3

I have a hibernate object that gets detached and transferred to a thick java swing client using EJBs. The object is then modified in some way and returned to the server where the server then updates the modified object to the database. Everything works well for properties that change, but I am having a problem on how to delete a many-to-one mapping.

For instance, given the following mapping:

<hibernate-mapping>
<class name="com.wg.common.bean.Patient" table="patient">
    <id name="id" type="integer">
        <column name="id" />
        <generator class="sequence">
            <param name="sequence">patient_id_seq</param>
        </generator>
    </id>
    <many-to-one name="address" class="com.wg.common.bean.Address" cascade="all">
        <column name="address_id" />
    </many-to-one>
    ...

Say I have a patient object that is sent to the client. The user has previously added an address. There can only be one address per patient. On the client if the user removes the address, I call setAddress(null) on the patient object before returning to the server. When I get to the server it saves the address_id field as null, but leaves the address record in the database. I understand why it is doing that. I am only breaking one end of the relationship. Preferably I would use delete-orphan. However, according to the Hibernate documentation, delete-orphan is not available for many-to-one mappings. The proper procedure is to call on the server (pseudocode):

Address address = patient.getAddress();
session.delete(address);
patient.setAddress(null);

The problem with this pattern is that if I want to stick to a process of simply passing back the Patient object I want saved, I have no way of knowing if it had an address that needs to be deleted. I have to do some less-than-elegant workarounds to solve this problem, such as querying the database to see if there was an Address and removing it if it is null in the passed object, or creating setDeleteAddress(boolean), getDeleteAddress() methods in Patient class and setting those on the client side if the user wants to remove the address.

Another alternative would be to make the association a one-to-many between Patient and Address. Then I could use delete-orphan. However, since it is really a one-to-one relationship, I would need to put some crazy getter/setter in the Patient class like this so I don't litter my code with collection references when there isn't really a collection:

public Address getAddress() {
    if(addresses != null && addresses.size() > 0) return addresses.get(0);
    else return null;
}

Is there a better way to address this issue? How have you handled removing an entity on detached objects?

A: 

Mappings address as many-to-one seems to be the root of your problems. Address is the "parent" in such a relationship, which doesn't make a lot of sense. Consider either mapping it as component or one-to-one instead. No collection should be involved either way.

ChssPly76
I agree the many-to-one seems to be the root of the problem. I am inheriting this system. The problem I find is that a one-to-one relationship also does not support the delete-orphan cascade, only one-to-many. Very odd. Thanks for your help.
Nemi
"delete-orphan" cascade ONLY makes sense when your "child" lifecycle is bound to (and governed by) your "parent"'s lifecycle. Both many-to-one and one-to-one associations assume that both ends of the relationship are independent. If you can not change the database (which you'd have to in order to map this as a component which is what you REALLY need to do), I suggest you change the mapping to one-to-one and manually delete "address" when you need to.
ChssPly76
A: 

I seem to recall that it is in fact possible to map a one-to-relationship with a single property in some cases. I cannot verify this, but there are some hints in the documentation.

Alternatively, a foreign key with a unique constraint, from Employee to Person, can be expressed as:

<many-to-one name="person" class="Person" column="PERSON_ID" unique="true"/>

This association can be made bidirectional by adding the following to the Person mapping:

<one-to-one name="employee" class="Employee" property-ref="person"/>

You may have to fiddle around a little and add cascade="delete" to the one-to-one mapping. I have never tried this myself. The best alternative is ChssPly76's answer, if you have control over the database schema.

waxwing
I also read about the unique qualifier making the many-to-one really a one-to-one. However, I cannot find the benefit of this in the documentation. It doesn't allow me to use cascade="all-delete-orphan", which is what I really need.
Nemi
A: 

I ended up simply creating a deleteFlag in the Address class to designate to the EJB that the Address in question should be deleted from the Patient.

Nemi