Search This Blog

Wednesday, 21 September 2011

Bidirectional Components

Components in Hibernate can include a back-reference that allows them to link back to their owning Entity. I decided to try this out and for this created a simple Person entity that includes a name and an Address.
As one Address is allowed for one person only ( at least in this crazy example , that's the rule ;p )
public class Person {
    private Integer id;
    private String name;
    private Address address;
// setter -getters 
}
The Address Component is as below:
public class Address {
    private Person person;
    private String city;
    private String street;
    private String zipCode;
// setter -getters
}
The mapping file for the above classes is as below:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.model.components.bidirectional">
    <class name="Person" table="PERSON">
        <id name="id" type="long">
            <column name="ID" />
            <generator class="native" />
        </id>
        <property name="name" type="string">
            <column name="NAME" />
        </property>

        <component name="address" class="Address">
            <parent name="person" />
            <property name="city" type="string" not-null="true">
                <column name="CITY" />
            </property>
            <property name="street" type="string" not-null="true">
                <column name="STREET" />
            </property>
            <property name="zipCode" type="string" not-null="true">
                <column name="ZIP_CODE" />
            </property>
        </component>
    </class>
</hibernate-mapping>

The parent element is used to indicate the back-link that exists in the component class. The table created for this is a simple table:

create table PERSON (
        ID bigint not null auto_increment,
        NAME varchar(255),
        CITY varchar(255),
        STREET varchar(255),
        ZIP_CODE varchar(255),
        primary key (ID)
    )
On executing a query to fetch records
static void testHqlRetrieval() {
    Session session = SESSION_FACTORY.openSession();
    try {
        //get Entity with query on component
        Query query = session.createQuery("from Person p where p.address.city = ?");
        query.setString(0, "Pune");
        Person person = (Person) query.list().get(0);
        System.out.println("Person : " + person + " City : " + person.getAddress().getCity());
            
        //get Components with query on component property
        query = session.createQuery("select u.address from Person u where u.address.city like ? " +
                " order by u.id");
        query.setString(0, "%u%");            
        int i =0;
        List<Address> addresses = query.list();
        for (Address addr : addresses) {
            System.out.println("Result No : " + i + " City : " + addr.getCity() + " Person " + addr.getPerson());
            i++;
        }
    } catch (HibernateException e) {
        e.printStackTrace();
    } finally {
        session.close();
    }
}

For the first HQL the logs are as below:

625 [main] DEBUG org.hibernate.SQL  - 
    select
        person0_.ID as ID0_,
        person0_.NAME as NAME0_,
        person0_.CITY as CITY0_,
        person0_.STREET as STREET0_,
        person0_.ZIP_CODE as ZIP5_0_ 
    from
        PERSON person0_ 
    where
        person0_.CITY=?
...
Person : com.model.components.bidirectional.Person@bbf7aa City : Pune
As can be seen a select query was fired on the table and the returned Entity of type Person can be seen with its city field.
However for the second query, the fetch was performed directly on the component

57172 [main] DEBUG org.hibernate.SQL  - 
    select
        person0_.CITY as col_0_0_,
        person0_.STREET as col_0_1_,
        person0_.ZIP_CODE as col_0_2_ 
    from
        PERSON person0_ 
    where
        person0_.CITY like ? 
    order by
        person0_.ID
...
Result No : 0 City : Punavali Person null
Result No : 1 City : Pune Person null 
You cannot navigate from a fetched Component to the Entity. As seen from the generated SQL only the columns specific to the component were retrieved from the db. Hibernate does not posses any information of the Entity and hence access to the person shows up as null. The back link would work in case 1 as the Entity was fetched completely and hence Hibernate is aware of the relation .
We can complicate this component parent relationship much further as shown in this post.

No comments:

Post a Comment