My experiments with technology

Investigating Hibernate Associations – One to One

August 12, 2007 · 19 Comments

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.

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.

Categories: Hibernate

19 responses so far ↓

  • Investigating Hibernate Associations - One to Many « My experiments with technology // August 18, 2007 at 12:33 pm | Reply

    [...] 18th, 2007 at 12:33 pm (Hibernate) My previous post covered one-to-one associations in Hibernate. This will cover one-to-many associations in [...]

  • Investigating Hibernate Associations - Many to Many « My experiments with technology // September 25, 2007 at 1:02 pm | Reply

    [...] 25th, 2007 at 1:02 pm (Hibernate) In my previous posts, we have looked at one-to-one and one-to-many hibernate associations. This one is going to focus on many-to-many hibernate [...]

  • Neal // April 25, 2008 at 7:55 pm | Reply

    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.

  • Vasan // May 23, 2008 at 2:26 pm | Reply

    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..

  • Mr. President // May 23, 2008 at 2:55 pm | Reply

    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.

  • Sachin // July 4, 2008 at 11:33 am | Reply

    Hi
    Thanks such good and informative article ….

    -sachin misurkar

  • soniya // January 17, 2009 at 7:30 am | Reply

    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

  • Mr. President // January 17, 2009 at 10:36 am | Reply

    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.

  • soniya // January 19, 2009 at 5:42 am | Reply

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

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

  • soniya // January 19, 2009 at 5:46 am | Reply

    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

  • The YPI // February 5, 2009 at 5:52 am | Reply

    Nice intro to one to one mapping…

    Thanks

  • Toni // February 23, 2009 at 9:52 am | Reply

    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

  • Mr. President // February 23, 2009 at 10:36 am | Reply

    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.

  • jack // March 16, 2009 at 2:14 pm | Reply

    Thanks good article.

    Will the one-to-one relationship work if I have an int instead of VARCHAR as personId ?

    • Mr. President // March 16, 2009 at 3:44 pm | Reply

      Yes. An int can be used instead of a VARCHAR as personid. Refer hibernate documentation section 4.1.2

  • jack // March 16, 2009 at 6:33 pm | Reply

    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.

    • Mr. President // March 17, 2009 at 3:48 pm | Reply

      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.

  • jack // March 17, 2009 at 5:14 pm | Reply

    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.

Leave a Comment