This article, the third in our series on Hibernate, focuses on the life-cycle states of persistent objects and associations. These concepts are put to work with continued development of an application that was created in the first two articles of this series.Relationships between entities form the crux of the relational approach. This must be well reflected by an ORM framework. In the world of the object-oriented approach, relations between objects are represented by associations. So the task of any ORM framework is to create a mapping between the relationships and the associations. This mapping is known as an association. The term is the same as that of the object oriented approach.

But before going into the depths of associations and how they affect queries, one must understand the life-cycle of the persistent object. This is necessary because queries depend on the current state of the object. Most of the logical errors occur due to operations tried on the persistent objects without keeping their current state in mind. In short, most of the “gotchas” in Hibernate can be corollated with the developer overlooking the state.

Hence this discussion will focus on both the life-cycle states of persistent objects and associations. The first section will deal with the life-cycle of the persistent object. In the second section I will discuss associations. The third section will put the concepts to work, as I will extend the application which was being developed during previous parts to include one more table with a many to one relationship with the order table. Now that the agenda has been disclosed, l will get right into the topic.
The state of an object reflects the values contained in the attributes of an object at a given time.  So whenever the values change, the state also changes to reflect the current values. The total number of states traversed by an object to reach its initial state is known as the life-cycle of the object. Following are the states that form the life-cycle of a persistent object, also known as life-cycle of persistence:

1. Transience

2. Persistence

3. Detachment

These are the states of an object. Starting from the transient state, moving on to the persistent state and reaching the detached state, this image should help you understand it better:

Each of the state transitions is associated with a task or an operation in object-oriented lingo. The details of what each state does to the persistent object and the operations that initiate the state transitions are as follows:

1. Transient State:

Transient is the state of an object when it is not associated with any database. Any object instantiated using the new operator is in this state. Since they are not associated with any row of a table, they are destroyed (or become eligible to be collected by the garbage collector). Transient objects don’t come under the context of a transaction. That means if the values of a transient object are updated, calling the rollback() method doesn’t undo the changes made to the object.

2. Persistent State:

An object is said to be in a persistent state when it has a database identity. An object having a database identity simply means that the identity of the object has been set using the primary key value of the table row it represents. In short, when the identity of the object contains the value of the primary key of the table row it reaches a persistent state. To make any transient object persistent, the save method of the Persistence manager has to be called. The most important characteristic feature of an object in the persistent state is that it takes part in transactional activities.

Even in the persistent state, the object stays in different states. Until the commit() method of transaction management is called with the object as a parameter, the object remains in the new state. Whenever any value held by a persistent object changes, the object becomes dirty. This state is not visible to the application. The trick used by Hibernate is known as Transparent Transaction-Level Write-Behind. The framework does this by delaying the synchronization of the object to the database table and then hides the details from the application.

This feature leads us to another feature where only those column values are updated which are modified. To do so the dynamic-update attribute of the <class > node of the mapping file is used. Finally an object in the persistent state can be made transient by calling the delete() method of the Persistence manager with the object as a parameter.

3. Detached State:

In Hibernate, when the transaction completes by calling the close() method on the Session object, the instances of persistence class lose their association with the Persistence manager. Thus the detached state is attained. In this state the object is no longer in sync with the database. Also Hibernate doesn’t care about the modifications being done to the object, as the object is no longer under the management of Hibernate. Though the Persistence or Transaction manager is no longer managing the object, it still contains the persistent data (data contained in the row of the corresponding table). This property makes it the most eligible candidate to be used as a Data Transfer Object in a multi-layered architecture.

How does this look in code? It looks something like this:

        Session sess = sf.openSession();

        Order o=new Order(); //transient state

        // search and return

      Query q = session.createQuery(“from Order order where”+

                                                            +”order.id=:id”);

      q.setString(“id”,name);

       List result = q.list();

        if (list.size() == 0) {

            System.out.println(“No Order having id ”

                               + name);

            System.exit(0);

        }

        o = (Order) list.get(0);//Persistent state

        sess.close();

      

        o.getOrderDate();//o is now detached

That clears the smoke around the life-cycle of a Persistent object. Now we can move on to more pressing issues — issues related to associations and association mappings.

To represent relationships between classes, associations are used. Before going into the details of how Hibernate perceives associations, an understanding of the working of container managed associations is needed. Managed association means that, if a change is made to one end of the association, it will be reflected at the other end.

For example, let’s consider the Order table. If it has a one-to-many relationship with the Product table, the Order class will have a one-to-many association with the Product class. So when changes are made to the attributes participating in this association, they will be reflected at the other end automatically. The developer doesn’t have to mange the associations manually.

How Hibernate implements the management of association is different from that of Container Managed Relationships/Associations or CMR generally provided by EJB CMP. In CMR the association is bidirectional, whereas Hibernate treats each association as different. The primary reason is that Hibernate builds its persistence based on Plain Old Java Object or POJO, and in Java associations are unidirectional. Thus Hibernate doesn’t implement CMR. So the associations are unidirectional. In essence it means if the on-update-cascade attribute is set in the mapping for Order and not in Product, any operation on Product would not affect Order.

Keeping these points in mind, let’s move on to the different types of associations supported by Hibernate. Hibernate mainly supports two types of associations:

1. One-to-Many

2. Many-to-One

To work with associations, the changes would be required in both the mapping as well as in the persistence class. The details are as follows:

1. One-to-Many:

In this kind of association one object of a class is in a relationship with many  objects of another class. The class that is having the single instance contains a collection of instances of the other class. To specify this association the mapping file would have to be modified. The added code would be:

<one-to-many

name=”nameOfheVariable

column=”NAME_OF_THE_COLUMN

class=”ClassName”

not-null=”true”/>

The name attribute takes the name of the variable that is participating in the association. The column attribute is used to specify the table column that is participating in the association. The class attribute takes the class name to which this class is associated. In the Persistent class the following change would be there:

class <className>

{

      //other variable declarations

      Set <className> =new HashSet();

     //constructors and getter/setter code follows

      :

}

Then the constructor with the added parameter for Set must be given along with the getter and setter for the Set. In the third section I will be discussing a real world example to illustrate this point.

2. Many-to-One:

This is the opposite of the One-to-Many association. In this case, the class that is having a Many-to-One association contains the object of the class. For example, if class A has a Many-to-One association with class B, then each instance of B would have an instance of A. And the identity of this instance can be the same for multiple objects of B. The change in the mapping would be:

<many-to-one

name=”nameOfheVariable

column=”NAME_OF_THE_COLUMN

class=”ClassName”

not-null=”true”/>

The attributes are the same as the case of One-to-Many. In the case of code it would be

class <className>

{

  <classNameofAssociatedClass> o=new <classNameofAssociatedClass>

//construtors and getter/setter code follows

:

}

The associations will be clearer when I discuss the real world usage of association in the next section.

Till now I was using only one table. Let’s make things interesting by adding one more table. This table is the Product table. Each Order can have more than one Product. Hence the relationship between Order and Product is One-to-Many. The schema of the Product table is:

CREATE TABLE PRODUCT(

        ID VARCHAR NOT NULL PRIMARY KEY,

        NAME VARCHAR NOT NULL,

        PRICE DOUBLE NOT NULL,

        AMOUNT INTEGER NOT NULL,

        ORDER_ID VARCHAR NOT NULL)

The next step is to create the persistent class for the Product table. The persistent class is as follows:

package com.someorg.persist;

 

public class Product {

    private String id;

    private String name;

    private double price;

    private int amount;

    private Order order;

  

    public Product(String id, String name, double price, int amount, Order 

                               order)

    {

          this.order=order;

           //others not shown for brevity

    }

    public String getId() {

        return id;

    }

    public void setId(String string) {

        id = string;

    }

    // default constructor and other

    // getters/setters not shown for brevity

    // …

}

The next part is Product.hbm.xml, that is the mapping file:

<?xml version=”1.0″ encoding=”UTF-8″?>

<!DOCTYPE hibernate-mapping

    PUBLIC “-//Hibernate/Hibernate Mapping DTD//EN”

    “http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd”>

   

<hibernate-mapping>

    <class name=”test.hibernate.Product”

           table=”products”>

             

        <id name=”id” type=”string”

            unsaved-value=”null”>

            <column name=”id” sql-type=”char(32)”

                    not-null=”true”/>

            <generator class=”assigned”/>

        </id>

        <property name=”name”>

            <column name=”name” sql-type=”char(255)”

                    not-null=”true”/>

        </property>

        <property name=”price”>

            <column name=”price” sql-type=”double”

                    not-null=”true”/>

        </property>

        <property name=”amount”>

            <column name=”amount” sql-type=”integer”

                    not-null=”true”/>

        </property>       

         <property name=”orderId”>

            <column name=”ORDER_ID” sql-type=”char(255)”

                    not-null=”true”/>

 

        <many-to-one

                                name=”orderId”

                                column=”ORDER_ID”

                                class=”ORDER”

                                not-null=”true”/>

                                                       

 

    </class>

</hibernate-mapping>

That is all that is required for the Product table. Now we need to make some changes in the Order class.

package com.someorg.persist;

import java.util.Date;

import java.util.HashSet;

import java.util.Set;

 public class Order {

    private String id;

    private Date date;

    private double priceTotal;

    private Set products =new HashSet();         

    // Automatically set the creation time of

    // this Order

    public Order() {

        this.date = new Date();

    }

  

   public Order(String id, Date date, private double priceTotal,  Set products){

   this.products=products;

   //others are not shown for brevity

   }

    public String getId() {

        return id;

    }

    

    public void setProducts(Set products) {

     this.products = products;

    }

   

    public Set getProducts () {

      return products;

    }

 

    public void setId(String string) {

        id = string;

    }

    // other getters/setters not shown for

    // brevity

    // …

}

The next part is changing in the Order.hbm.xml, which is:

<?xml version=’1.0′ encoding=’utf-8′?>

<!DOCTYPE hibernate-configuration PUBLIC

“-//Hibernate/Hibernate Configuration DTD 3.0//EN”

“http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd”>

<hibernate-mapping default-cascade=”none” default-access=”property” auto-import=”true”>

 <class name=” com.someorg.persist.Order” table=”orders” mutable=”true” select- before-update=”false” optimistic-lock=”version”>

<id name=”id” type=”string” unsaved-value=”null”>

  <column name=”id” sql-type=”char(32)” not-null=”true” />

  <generator class=”assigned” />

  </id>

<property name=”date” not-null=”false” >

  <column name=”order_date” sql-type=”datetime” not-null=”true” />

  </property>

<property name=”priceTotal” not-null=”false” >

  <column name=”price_total” sql-type=”double” not-null=”true” />

  </property>

 

  <set name=”products”>

    <key column=”PRODUCT_ID”/>

    <one-to-many class=”Product”/>

  </set>

 

 

   </class>

  </hibernate-mapping>

The next step is to test it. To test it I will be using the Criteria query. In a QBC the joins are done using the setFetchMode method of the Criteria class. The mode would be JOIN. Here is how it works:

import java.util.List;

//other imports

// use as

// java test. FindOrderById name

public class FindOrderById {

    public static void main(String[] args) throws Exception {

        // query to issue

        String query =

            “select order from Order “

             + “where order.id=:id”;

        // search for what?

        String name = args[0];

        // init

        Configuration cfg = new Configuration()

                           .addClass(Order.class);

        SessionFactory sf = cfg.buildSessionFactory();

        // open session

        Session sess = sf.openSession();

       

        // search and return

      Criteria criteria = session.createCriteria(Order.class);

      criteria.add( Expression.eq(“id”, name) )

                          .setFetchMode(“products”,FetchMode.JOIN);

      List result = criteria.list();

 

        if (list.size() == 0) {

            System.out.println(“No Order having id ”

                               + name);

            System.exit(0);

        }

        Order o = (Order) list.get(0);

        sess.close();

        System.out.println(“Found Order: ” + o);//this is just an example Here the o //

        //object can be traversed to achieve anything

    }

}

That brings us to the end of this discussion. Though the complete picture is becoming clear, some edges are still hazy. These edges will be brought into sharper focus in the forthcoming discussions. Till next time.


One thought on “Hibernate: Understanding Associations

Leave a Reply to sangeethanaik Cancel reply

Your email address will not be published.

Click to Insert Smiley

SmileBig SmileGrinLaughFrownBig FrownCryNeutralWinkKissRazzChicCoolAngryReally AngryConfusedQuestionThinkingPainShockYesNoLOLSillyBeautyLashesCuteShyBlushKissedIn LoveDroolGiggleSnickerHeh!SmirkWiltWeepIDKStruggleSide FrownDazedHypnotizedSweatEek!Roll EyesSarcasmDisdainSmugMoney MouthFoot in MouthShut MouthQuietShameBeat UpMeanEvil GrinGrit TeethShoutPissed OffReally PissedMad RazzDrunken RazzSickYawnSleepyDanceClapJumpHandshakeHigh FiveHug LeftHug RightKiss BlowKissingByeGo AwayCall MeOn the PhoneSecretMeetingWavingStopTime OutTalk to the HandLoserLyingDOH!Fingers CrossedWaitingSuspenseTremblePrayWorshipStarvingEatVictoryCurseAlienAngelClownCowboyCyclopsDevilDoctorFemale FighterMale FighterMohawkMusicNerdPartyPirateSkywalkerSnowmanSoldierVampireZombie KillerGhostSkeletonBunnyCatCat 2ChickChickenChicken 2CowCow 2DogDog 2DuckGoatHippoKoalaLionMonkeyMonkey 2MousePandaPigPig 2SheepSheep 2ReindeerSnailTigerTurtleBeerDrinkLiquorCoffeeCakePizzaWatermelonBowlPlateCanFemaleMaleHeartBroken HeartRoseDead RosePeaceYin YangUS FlagMoonStarSunCloudyRainThunderUmbrellaRainbowMusic NoteAirplaneCarIslandAnnouncebrbMailCellPhoneCameraFilmTVClockLampSearchCoinsComputerConsolePresentSoccerCloverPumpkinBombHammerKnifeHandcuffsPillPoopCigarette