Tuesday, July 5, 2011

Many-to-one, bidirectional Associations

In this case we allow navigation in both directions between the two classes. On the Many side it is a standard java reference. on the One side it is a collection. However, the two associations are not independent of each other but rather, one is the inverse of the other and the same foreign key column is used for both associations. Thus our Lecturer object now has a property which is a collection of Student objects while the Student objects have a properties which refers to the Student's supervising Lecturer.

A Many-to-One, bidirectional Association

To achieve this, we start by using the many-to-one element as before in the mapping file for the Student class, and the Set element as before in the mapping file for the Lecturer class, ensuring that both associations use the same column in Student's table to encode the association. Then we add a new attribute, inverse="true" to the set element in Lecturer's mapping file. Without this, adding a new Student as an advisee to a Lecturer would trigger Hibernate to set the foreign key column of the Student table twice: once for each association that has been changed. The inverse attribute tells hibernate that Student owns the association and that Hibernate should not trigger updates of the foreign key column when it changes on the Lecturer side.
Thus the mapping file for Lecturer looks like this:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
          "-//Hibernate/Hibernate Mapping DTD//EN"
          "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping>
    <class name="Lecturer" table="lecturers">
        <id name="id" column="lecturer_id">
            <generator class="sequence"/>
        </id>
        <version name="version" column="version"/>
        <property name="name" column="name"/>
        <set name="advisees" inverse="true" cascade="save-update" lazy="true">
            <key column="lecturer_id"/>
            <one-to-many class="Student"/>
        </set>
    </class>
</hibernate-mapping>

The mapping file for student is as follows:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
          "-//Hibernate/Hibernate Mapping DTD//EN"
          "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping>
    <class name="Student" table="students">
        <id name="id" column="student_id">
            <generator class="sequence"/>
        </id>
        <version name="version" column="version"/> 
        <property name="name" column="name"/>
        <property name="regNo" column="reg_no"/>
        <many-to-one name="advisor" column="lecturer_id" cascade="save-update"/>
    </class>
</hibernate-mapping>

Now all the programmer has to do is to ensure that, when the Lecturer's advisee property property is changed, the corresponding correct changes are made to the appropriate Student's advisor property. So long as both are done together, the Java object graph will be correct and the correct update on disk will be made as well. Furthermore, since the association belongs to the Student, there will never be an insert of a Student record with a null Lecturer foreign key if the Student has an advisor, thus avoiding not-null constraint breaking. To ensure that these updates are made together, it is usual to add some convenience methods: in Lecturer, change the getAdvisees() and setAdvisees() methods to private and add a convenience method to update the object graph correctly when adding a new Student advisee to a Lecturer:

public void addAdvisee(Student st)
    {
        Lecturer oldAdvisor = st.getAdvisor() ;
        if (oldAdvisor != this)
        {
            if (oldAdvisor != null)
                oldAdvisor.getAdvisees().remove(st) ;
            st.setAdvisor(this);
            advisees.add(st) ;
        }
    }

Note how we are careful to correctly handle removal of a Student from a previous advising Lecturer before adding it to this one. Whether you need to do something similar for your code will depend on your detailed design.
If we have a true composition relationship, i.e., a parent-child relationship where if the parent gets deleted then the child should also be deleted etc., then we should change the cascade attribute on the set element in the Lecturer mapping file to be all-delete-orphan.

No comments:

Post a Comment

Chitika