Investigating Hibernate Associations – One to One

My previous posts set the ball rolling. This one is going to dabble into Hibernate relationships/associations.

Before we start looking at Hibernate associations let’s do a refresher on the configuration set-up so far. There’s the contents of hibernate.cfg.xml file.

<hibernate-configuration>
    <session-factory>
	<!-- Database connection settings -->
        <property name="connection.driver_class">
            com.mysql.jdbc.Driver</property>
        <property name="connection.url">
        	jdbc:mysql://localhost:3306/<dbname></property>
        <property name="connection.username"><username></property>
        <property name="connection.password"><password></property>
	<property name="transaction.factory_class">
	 	org.hibernate.transaction.JDBCTransactionFactory
	 </property>

        <!-- JDBC connection pool (use the built-in) -->
        <property name="connection.pool_size">1</property>
        <!-- SQL dialect -->
        <property  name="dialect">
            org.hibernate.dialect.MySQL5InnoDBDialect
        </property>
        <!-- Enable Hibernate's automatic session context management -->
        <property name="current_session_context_class">thread</property>
        <!-- Disable the second-level cache  -->
        <property name="cache.provider_class">
 		org.hibernate.cache.NoCacheProvider
	</property>
        <!-- Echo all executed SQL to stdout -->
        <property name="show_sql">true</property>
        <!-- Drop and re-create the database schema on startup -->
        <property name="hbm2ddl.auto">update</property>
        <mapping resource="com/tutorial/hibernate/simple/Person.hbm.xml"/>
	<mapping resource="com/tutorial/hibernate/simple/Phone.hbm.xml" />
</session-factory>
</hibernate-configuration>

The hibernate configuration file for the only persistent class Person is as below:

<hibernate-mapping package="com.tutorial.hibernate.simple">
	<class name="Person" table="PERSON" dynamic-update="true"
        	dynamic-insert="true" select-before-update="false">
	        <id name="id">
        	    <generator class="assigned"/>
	        </id>
        	<property name="firstName"/>
	        <property name="lastName"/>
	        <property name="dob" type="date" />
	</class>
</hibernate-mapping>

To understand the role of associations, let’s create another persistent class Phone. The configuration file Phone.hbm.xml for Phone is as below:

<hibernate-mapping package="com.tutorial.hibernate.simple">
	<class name="Phone" table="PHONE" dynamic-update="true"
		dynamic-insert="true" select-before-update="false">
        <id name="id">
            <generator class="assigned"/>
        </id>
        <property name="number" column="contactnumber"/>
        <property name="type" column="phonetype"/>
        <property name="personId" column="personid"/>
    </class>
</hibernate-mapping>

Create java class Phone with id, number and type as private String attributes. The CREATE TABLE SQL is:

CREATE TABLE Phone (
	id VARCHAR(255),
	contactNumber VARCHAR(10),
	phoneType VARCHAR(255),
	personid VARCHAR(255),
	primary key (id))

Different types of associations defined by Hibernate are as follows:

  1. one-to-one
  2. one-to-many
  3. many-to-one
  4. many-to-many

Along with these associations we have directionality – unidirectional or bidirectional.
Let’s start with one-to-one unidirectional association. Add the following xml tag in the Person.hbm.xml.

<one-to-one name="phone" class="Phone" property-ref="personId"
     	cascade="all"/>

Add the personId attribute in the Phone class along with its getters and setters. In the setPhone method of Person class make the following changes:

	public void setPhone(Phone phone) {
	 	phone.setPersonId(this.id);
	 	this.phone = phone;

 	}

Please find below the sample code to understand how to use one-to-one associations.

	Configuration cfg = new Configuration();
	cfg.configure();
	SessionFactory sessFactory = cfg.buildSessionFactory();
	try {
	 	Session session = sessFactory.openSession();
		Person p = new Person();
	 	p.setId("1245");
	 	p.setFirstName("Clark");
	 	p.setLastName("Kent");
	 	p.setDob(new java.util.Date());

		Phone phone = new Phone();
	 	phone.setId("451");
	 	phone.setNumber("1234567");
	 	phone.setType("BUS");
	 	p.setPhone(phone);

		Transaction tx = session.beginTransaction();
	 	session.save(p);
	 	tx.commit();

		session = sessFactory.openSession();
	 	Person p1 = (Person)session.load(Person.class,"1245");
	 	System.out.println(p1);
	} catch(Exception e) {
	 	e.printStackTrace();
	}

The SQL output generated is as follows:

Hibernate: select phone_.id, phone_.contactnumber as contactn2_1_,
           phone_.phonetype as phonetype1_, phone_.personid
           as personid1_
           from PHONE phone_ where phone_.id=?
Hibernate: insert into PERSON (firstName, lastName, dob, id)
	   values (?, ?, ?, ?)
Hibernate: insert into PHONE (contactnumber, phonetype, personid, id)
	   values (?, ?, ?, ?)
Hibernate: select person0_.id as id0_1_, person0_.firstName
	       as firstName0_1_, person0_.lastName as lastName0_1_,
	       person0_.dob as dob0_1_, phone1_.id as id1_0_,
	       phone1_.contactnumber as contactn2_1_0_, phone1_.phonetype
	       as phonetype1_0_, phone1_.personid as personid1_0_
	       from PERSON person0_
           left outer join PHONE phone1_ on person0_.id=phone1_.personid
	       where person0_.id=?
           com.tutorial.hibernate.simple.Person@64c34e

So we have achieved the basic goal of inserting the Person and Phone information using hibernate. An outer join query was generated combining the query on the tables Person and Phone.

In case you do not wish the outer join query to be used, add the following XML tag in the hibernate.cfg.xml

<property name="hibernate.max_fetch_depth">0</property>

Setting hibernate.max_fetch_depth property to zero globally disables eager fetching of the associations. This is a global option. If you want specific associations to be loaded separately, do the following:

Set hibernate.max_fetch_depth to 1. This allows outer join fetching. Change the following definition in the Person.hbm.xml file

<one-to-one name="phone" class="Phone" property-ref="personId"
	fetch="select" cascade="all" />
Hibernate: select person0_.id as id0_0_, person0_.firstName as firstName0_0_,
	       person0_.lastName as lastName0_0_, person0_.dob as dob0_0_
	       from PERSON person0_ where person0_.id=?
Hibernate: select phone0_.id as id1_0_, phone0_.contactnumber
	       as contactn2_1_0_,phone0_.phonetype as phonetype1_0_,
	       phone0_.personid as personid1_0_
	       from PHONE phone0_ where phone0_.personid=?

It appears that irrespective of whether you are interested in getting Phone information or not Phone was being retrieved. Tried session.get() with same results. Tried using a query like “FROM Person WHERE id = ‘1245’ ” instead of the session’s load() or get() methods no change. Tried the Criteria querying option and specified FETCHMODE as LAZY. That did not help either. As per Hibernate documentation will need to define the relationship as Many-to-One. That I do not like. Will need to dig deeper here.

Update in response to @Roger’s comment(25th Jan 2012):Another useful tutorial covering one-to-one relationship is available here.

I tried developing a bi-directional relationship. However it failed. It appears that for one-to-one relationships, the table strutures and hibernate persistent objects definition should be as per the above mentioned link. Even though I believe that approach puts unnecessary constraints, that’s how hibernate has defined it.

Next I intend to dabble in one-to-many relationships.

Advertisements

24 thoughts on “Investigating Hibernate Associations – One to One

  1. What is opinion in terms of primary key one-to-one versus foreign key one-to-one strategy?
    Here you have mentioned the latter.
    What is the advantage or disadvantage of the former(pk one-to-one)?

    AUTHOR’s RESPONSE:
    ================
    I personally prefer the one-to-one FK approach for the following reasons:
    Keeps the objects clean. Person does not have any information related to the Phone object. Only if one wishes to relate the two we need to use the relationship defined on Phone to retrieve it. In an ideal world, one would not retrieve Phone without the person information.

    Makes changing the relationship from one-to-one to one-to-many easier. It is always possible the multiplicity could change during development iterations.

    That’s my two cents on the matter. Hope you concur.

  2. Great investigation. Want to echo the previous post..
    There are occasions where primary key one-to-one can be preferred (such as breaking a table with too-many columns into two tables). I have not yet got this to work nicely in Hibernate..

  3. Thanks for your comment. Yes, it is possible that for breaking a table having too many columns, we could have a case for primary key one-to-one, but even here I would prefer to have a different primary key. Having two classes with the same primary key would not be object oriented.

  4. In this above example you have created two configuration files Person.hbm.xml and Phone.hbm.xml.
    From hibernate.cfg.xml we get the reference/path of Person.hbm.xml but where do we get reference for Phone.hbm.xml?

    Regards
    Soniya

  5. By oversight I missed adding the mapping resource for Phone.hbm.xml. Thanks for pointing out the error. I have corrected the mistake in the post. The updated configuration.cfg.xml file has the mapping for Phone.

  6. *********************************
    person.hbm.xml
    *********************************

    **********************************
    gender.hbm.xml
    **********************************

  7. one-to-one name=”gender” class=”Gender” property-ref=”genderId”

    This is the line I have written in person.hbm.xml along with the property and column tags as you have given in the example. I also have written gender.hbm.xml
    Hope you will be able to help me out.
    wit this much of the info.

    Regards
    Soniya

  8. Dear Sir
    Can you help me to make hobernate ORM,
    I have 2 table( pp and gudangbarang)
    I will join 2 table using one-t-one connection, can help me to join this.
    This is the table

    and

    please help me.
    thanks

  9. Sorry Toni, your table structure information did not come thru. However you can map your tables to this blog post example and you should be able to get one-to-one mapping done.

  10. Thankyou. I am just trying to do that but it the value gets persisted as 0. from the SQL I can see that it triggers a insert but does not trigger a update.

    So the value of personid in Phone has value 0.

    From what I understand hibernate is suppsoed to late resolve this value but it does not seem to do so. Any idea would be most helpful.

    1. From an efficiency point of view an update should not be necessary. If you refer to my post, after the one-to-one tag addition mentioned for Person.hbm.xml, I have recommended addition of the following code:

      public void setPhone(Phone phone) {
      phone.setPersonId(this.id);
      this.phone = phone;

      }

      This should set the personid in the phone table. In case instead of the assigned generator, you are using a database sequence. Refer to hibernate documentation section 5.1.13 for one-to-one mapping. Within the same, have a look at sub-topic primary key association. That should provide a solution. Hope this helps.

  11. Thanks for your time.
    Let me further elaborate on the problem.

    All primary key ID as assigned by hibernate are set.

    The issue is only when phone.setPersonId(this.id);

    The personId in Phone is still zero.

    Thanks for your help.

  12. HI,

    i am using one -to-one association between address and customer .

    Address.hbm.xml is

    This class contains address details.

    and customer.hbm.xml

    This class contains Customer details.

    addressId in customer table is not updated with customer id. always it is having only 0.
    could you please look into the issue?
    unless i have mapping (correct value in address) i wont get customer when i am fetching address details.

    Thanks in advance.

    Regards,
    Anil

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s