Make delicious recipes!


Hibernate Hierarchical Example


In a real-world Java application, there will be numerous objects having relationships.
For example:



Without an ORM like Hibernate, one has to insert parent records first, then child records and then update the foreign key etc.
With Hibernate, all this can be automated by just specifying some annotations/configurations.

In the example, we will model the above relationship using Hibernate/JPA annotations.
Required resources:
Type Used in this example Description
Java Version 1.6 Java environment to compile and run.
Database Oracle
Java POJOs Person.java,
Address.java,
Country.java
Classes with which Hibernate will interact with the corresponding tables.
Main Class HibernateExample.java A Java class to configure and run Hibernate.
Maven Apache Maven 3.1.1 A very popular build/release-management tool.
<Database>-specific library ojdbc6.jar (for Oracle) Each database has its own lib to interact with Hibernate.
hibernate.cfg.xml Contains options to configure hibernate as a whole.
<POJO>.hbm.xml Required only if not using the annotated-POJO version.
(Not used in this example).





Step 1: Create a skeleton maven framework.
A quick and coarse maven introduction: Maven is an awesome build tool which abstracts away all the
details associated with library downloading, versioning issues and jar inter-dependency.
If you do not know about maven, it's recommended to go through this short Maven Tutorial
mvn archetype:generate \
-DgroupId=com.prismoskills.hibernate \
-DartifactId=hierarchy \
-Dpackage=com.prismoskills.hibernate \
-Dversion=1.0.0-SNAPSHOT



Step 2: Create a table and a sequence in the database:

// Drop existing tables if any
Drop table Person;
Drop table Address;
Drop table Country;

// Create new tables
Create Table Country (
  Country_id Number(20) NOT NULL,
  Country Varchar(64),
  PRIMARY KEY (Country_id),
  CONSTRAINT u_country UNIQUE (Country)
);

// Note the use of foreign-key to country-table
Create Table Address (
  Address_id Number(20) NOT NULL,
  Zip_code Number(20) NOT NULL,
  City Varchar(64),
  State Varchar(64),
  Country_id Number(20),
  PRIMARY KEY (Address_id),
  FOREIGN KEY (Country_id) references Country(Country_Id)
);

// Note the use of foreign-key to Address table
Create Table Person (
  Person_Id Number(20) NOT NULL,
  First_name Varchar(64),
  Last_name Varchar(64),
  Salary Number(20),
  Address_Id Number(20),
  PRIMARY KEY (Person_Id),
  FOREIGN KEY (Address_Id) references Address(Address_Id)
);
 
// Create a sequence to automatically generate ID values for each table.
Create Sequence person_sequence
MinValue 1
MaxValue 20000
Start With 1
Increment By 1
Cache 100;

// Initialize the sequence
Select person_sequence.nextval from dual;



Step 3: Create POJO classes used by Hibernate to interface with the database:

Country.java

package com.prismoskills.hibernate.hierarchy.pojos;

import javax.persistence.*;

@Entity
@Table(name = "Country")
@javax.persistence.SequenceGenerator(
        name="P_SEQ",
        sequenceName="person_sequence"
    )
public class Country
{
    @Id
    @Column(name = "Country_ID")
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator="P_SEQ")
    private int countryId;

    @Column(name = "Country")
    private String country;

    public Country() {}

    public Country(String country)
    {
        this.country = country;
    }

    // Getter - Setters
}

Address.java

package com.prismoskills.hibernate.hierarchy.pojos;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity
@Table(name = "Address")
@javax.persistence.SequenceGenerator(
        name="P_SEQ",
        sequenceName="person_sequence"
    )
public class Address
{
    @Id
    @Column(name = "Address_ID")
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator="P_SEQ")
    private int addressId;

    private String city;
    private String state;

    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "COUNTRY_ID", nullable = false)
    private Country country;

    @Column(name="Zip_Code")
    private int zipCode;

    public Address() {}

    public Address(String city, String state, Country country, int zipCode)
    {
        this.city = city;
        this.state = state;
        this.country = country;
        this.zipCode = zipCode;
    }

    // Getter - Setters
}

Person.java

package com.prismoskills.hibernate.hierarchy.pojos;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;


@Entity // Required annotation
@Table(name="Person") // can be omitted if table name is exactly same as Class name
@javax.persistence.SequenceGenerator(
        name="P_SEQ",
        sequenceName="person_sequence"
    )
public class Person
{
    @Id // Required annotation
    @Column(name="Person_ID") // can be omitted if column name is same as field-name
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator="P_SEQ")
    private int personId;

    @Column(name="FIRST_NAME")
    private String firstName;

    @Column(name="LAST_NAME")
    private String lastName;

    // Column annotation not required here since DB column-name is same as field-name
    private int salary;

    @OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinColumn(name = "ADDRESS_ID", nullable = false)
    private Address address;

    public Person() {}

    public Person(String fname, String lname, int salary, Address address)
    {
        this.firstName = fname;
        this.lastName = lname;
        this.salary = salary;
        this.address = address;
    }

    // Getter - Setters
}


Step 4: Create a main class to configure and run Hibernate:

package com.prismoskills.hibernate.hierarchy;

import java.util.Iterator;
import java.util.List;

import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;

import com.prismoskills.hibernate.hierarchy.pojos.Address;
import com.prismoskills.hibernate.hierarchy.pojos.Country;
import com.prismoskills.hibernate.hierarchy.pojos.Person;


public class HibernateExample
{
    private static SessionFactory factory;
    Country country = new Country("USA");
    Address address = new Address("Los Angeles", "California", country, 12345);

    public static void main(String[] args)
    {
        try
        {
            factory = new Configuration().configure().buildSessionFactory();
        }
        catch (Exception e)
        {
            e.printStackTrace();
            System.exit(1);
        }
        HibernateExample example = new HibernateExample();

        /* Add some records to the database */
        Integer id1 = example.addRecord("Johny", "Travol", 10);
        Integer id2 = example.addRecord("Kevin", "Becker", 20);
        Integer id3 = example.addRecord("Bruce", "Almighty", 30);

        /* Update a record */
        example.updateRecord(id1, 50);

        /* Delete */
        example.deleteRecord(id3);

        /* List the records */
        example.listRecords();

        System.out.println("Done with execution");
    }


    /* CREATE a record in the database */
    public Integer addRecord(String fname, String lname, int salary)
    {
        printOperation("Adding Record");
        Session session = factory.openSession();
        Transaction tx = null;
        Integer id = null;
        try
        {
            tx = session.beginTransaction();
            Person p = new Person(fname, lname, salary, address);
            id = (Integer) session.save(p);
            tx.commit();
        }
        catch (Exception e)
        {
            if (tx!=null) tx.rollback();
            e.printStackTrace();
        }
        finally
        {
            session.close();
        }
        return id;
    }


    /* READ all the records */
    public void listRecords()
    {
        printOperation("Reading Records");
        Session session = factory.openSession();
        try
        {
            List ppl = session.createQuery("FROM Person").list();
            Iterator itr = ppl.iterator();
            while (itr.hasNext())
            {
                Person p = (Person) itr.next();
                System.out.println (
                        "Id: " + p.getPersonId()
                        + ", Name: " + p.getFirstName() + " " + p.getLastName()
                        + ", Salary: " + p.getSalary()
                        );
            }
            System.out.println("\nRead all records.");
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        finally
        {
            session.close();
        }
    }


    /* UPDATE a column in the DB */
    public void updateRecord(Integer id, int salary )
    {
        printOperation("Updating Record with id = " + id);
        if (id == null)
            return;

        Session session = factory.openSession();
        Transaction tx = null;
        try
        {
            tx = session.beginTransaction();
            Person p = (Person)session.get(Person.class, id);
            p.setSalary( salary );
            session.update(p);
            tx.commit();
        }
        catch (Exception e)
        {
            if (tx!=null) tx.rollback();
            e.printStackTrace();
        }
        finally
        {
            session.close();
        }
    }


    /* Method to DELETE from the records */
    public void deleteRecord(Integer id)
    {
        printOperation("Deleting Record with id = " + id);
        if (id == null)
            return;

        Session session = factory.openSession();
        Transaction tx = null;
        try
        {
            tx = session.beginTransaction();
            Person p = (Person)session.get(Person.class, id);
            session.delete(p);
            tx.commit();
        }
        catch (HibernateException e)
        {
            if (tx!=null)
                tx.rollback();
            e.printStackTrace();
        }
        finally
        {
            session.close();
        }
    }

    private void printOperation(String msg)
    {
        System.out.println("\n============= " + msg + " =============");
    }
}



Step 5: Create ./src/main/resources/hibernate.cfg.xml used to specify overall Hibernate options:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
 
<hibernate-configuration>
<session-factory>
 
<!-- Database Type -->
<property name="hibernate.dialect">
org.hibernate.dialect.Oracle10gDialect
</property>
<property name="hibernate.connection.driver_class">
oracle.jdbc.driver.OracleDriver
</property>
 
<!-- Database Properties -->
<property name="hibernate.connection.url">
jdbc:oracle:thin:@//localhost:1522/XE
</property>
<property name="hibernate.connection.username">system</property>
<property name="hibernate.connection.password">password</property>
 
 
<!-- Schema Validation -->
<property name="hbm2ddl.auto">validate</property>
 
 
<!-- Debugging and Formatting -->
<property name="show_sql">true</property>
<property name="format_sql">true</property>
 
<!-- Required for annotations to work without Person.hbm.xml -->
<mapping class="com.prismoskills.hibernate.hierarchy.pojos.Country" />
<mapping class="com.prismoskills.hibernate.hierarchy.pojos.Address" />
<mapping class="com.prismoskills.hibernate.hierarchy.pojos.Person" />

</session-factory>
</hibernate-configuration>


Step 6: Download ojdbc6.jar (or your database-specific jars)
ojdbc6.jar is not available in public maven repositories, so we will need to install it manually.
Go to your project directory where pom.xml is there and execute the following:
mvn install:install-file -Dfile=C:/ojdbc6.jar -DgroupId=com.oracle -DartifactId=ojdbc6 -Dversion=11.2.0 -Dpackaging=jar


Step 7: Add dependencies to pom.xml as follows:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.prismoskills.hibernate</groupId>
  <artifactId>hierarchy</artifactId>
  <version>1.0.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>hierarchy</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
  
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
      <version>4.3.0.CR2</version>
    </dependency>
            
    <dependency>
      <groupId>com.oracle</groupId>
      <artifactId>ojdbc6</artifactId>
      <version>11.2.0</version>
    </dependency>
    
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.16</version>
    </dependency>
            
  </dependencies>

 
  <build>
    <plugins>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>2.1</version>
        <configuration>
            <transformers>
                <transformer
                    implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                    <manifestEntries>
                        <Main-Class>com.prismoskills.hibernate.hierarchy.HibernateExample</Main-Class>
                    </manifestEntries>
                </transformer>
            </transformers>
        </configuration>
        <executions>
            <execution>
                <phase>package</phase>
                <goals>
                    <goal>shade</goal>
                </goals>
            </execution>
        </executions>
    </plugin>
    </plugins>
  </build>
  
</project>


Step 8: Final directory structure should look like:
./pom.xml
./src/main/java/com/prismoskills/hibernate/hierarchy/HibernateExample.java
./src/main/java/com/prismoskills/hibernate/hierarchy/pojos/Address.java
./src/main/java/com/prismoskills/hibernate/hierarchy/pojos/Country.java
./src/main/java/com/prismoskills/hibernate/hierarchy/pojos/Person.java
./src/main/resources/hibernate.cfg.xml


Step 9: Finally, run the above setup as:

mvn clean package
cd target
java -jar hierarchy-1.0.0-SNAPSHOT.jar




Output


The above example prints the below SQL when run.

//============= Adding Record =============

//Hibernate: 
    select person_sequence.nextval 
    from dual

//Hibernate: 
    select person_sequence.nextval 
    from dual

//Hibernate: 
    select person_sequence.nextval 
    from dual

//Hibernate: 
    insert into
    Country (Country, Country_ID) 
    values (?, ?)

//Hibernate: 
    insert into
    Address (city, COUNTRY_ID, state, Zip_Code, Address_ID) 
    values (?, ?, ?, ?, ?)

//Hibernate: 
    insert into
    Person (ADDRESS_ID, FIRST_NAME, LAST_NAME, salary, Person_ID) 
    values (?, ?, ?, ?, ?)


//============= Adding Record =============

//Hibernate: 
    insert into
    Person (ADDRESS_ID, FIRST_NAME, LAST_NAME, salary, Person_ID) 
    values (?, ?, ?, ?, ?)

//Hibernate: 
    update
        Address 
    set
        city=?,
        COUNTRY_ID=?,
        state=?,
        Zip_Code=? 
    where
        Address_ID=?

//Hibernate: 
    update
        Country 
    set
        Country=? 
    where
        Country_ID=?


//============= Adding Record =============

//Hibernate: 
    insert into
    Person (ADDRESS_ID, FIRST_NAME, LAST_NAME, salary, Person_ID) 
    values (?, ?, ?, ?, ?)

//Hibernate: 
    update
        Address 
    set
        city=?,
        COUNTRY_ID=?,
        state=?,
        Zip_Code=? 
    where
        Address_ID=?

//Hibernate: 
    update
        Country 
    set
        Country=? 
    where
        Country_ID=?


//============= Updating Record with id = 1600 =============

//Hibernate: 
    select
        person0_.Person_ID as Person_ID1_2_0_,
        person0_.ADDRESS_ID as ADDRESS_ID5_2_0_,
        person0_.FIRST_NAME as FIRST_NAME2_2_0_,
        person0_.LAST_NAME as LAST_NAME3_2_0_,
        person0_.salary as salary4_2_0_,
        address1_.Address_ID as Address_ID1_0_1_,
        address1_.city as city2_0_1_,
        address1_.COUNTRY_ID as COUNTRY_ID5_0_1_,
        address1_.state as state3_0_1_,
        address1_.Zip_Code as Zip_Code4_0_1_ 
    from
        Person person0_ 
    inner join
        Address address1_ 
            on person0_.ADDRESS_ID=address1_.Address_ID 
    where
        person0_.Person_ID=?

//Hibernate: 
    update
        Person 
    set
        ADDRESS_ID=?,
        FIRST_NAME=?,
        LAST_NAME=?,
        salary=? 
    where
        Person_ID=?


//============= Deleting Record with id = 1602 =============

//Hibernate: 
    select
        person0_.Person_ID as Person_ID1_2_0_,
        person0_.ADDRESS_ID as ADDRESS_ID5_2_0_,
        person0_.FIRST_NAME as FIRST_NAME2_2_0_,
        person0_.LAST_NAME as LAST_NAME3_2_0_,
        person0_.salary as salary4_2_0_,
        address1_.Address_ID as Address_ID1_0_1_,
        address1_.city as city2_0_1_,
        address1_.COUNTRY_ID as COUNTRY_ID5_0_1_,
        address1_.state as state3_0_1_,
        address1_.Zip_Code as Zip_Code4_0_1_ 
    from
        Person person0_ 
    inner join
        Address address1_ 
            on person0_.ADDRESS_ID=address1_.Address_ID 
    where
        person0_.Person_ID=?

//Hibernate: 
    select
        country0_.Country_ID as Country_ID1_1_0_,
        country0_.Country as Country2_1_0_ 
    from
        Country country0_ 
    where
        country0_.Country_ID=?

//Hibernate: 
    delete 
    from
        Person 
    where
        Person_ID=?

//Hibernate: 
    delete 
    from
        Address 
    where
        Address_ID=?

/*
org.hibernate.exception.ConstraintViolationException:
  could not execute statement
        at org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:72)
        ...
        at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:190)
        ...
        at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:421)
        at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101)
        at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:177)
        at com.prismoskills.hibernate.hierarchy.HibernateExample.deleteRecord(HibernateExample.java:155)
        at com.prismoskills.hibernate.hierarchy.HibernateExample.main(HibernateExample.java:45)
        
Caused by: java.sql.SQLIntegrityConstraintViolationException:
  ORA-02292: integrity constraint (SYSTEM.SYS_C009879) violated - child record found
        at oracle.jdbc.driver.SQLStateMapping.newSQLException(SQLStateMapping.java:85)
        ...
        at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:413)
        at oracle.jdbc.driver.T4C8Oall.receive(T4C8Oall.java:1034)
        ...
        at oracle.jdbc.driver.OraclePreparedStatementWrapper.executeUpdate(OraclePreparedStatementWrapper.java:1350)
        at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:187)
*/

//============= Reading Records =============

//Hibernate: 
    select
        person0_.Person_ID as Person_ID1_2_,
        person0_.ADDRESS_ID as ADDRESS_ID5_2_,
        person0_.FIRST_NAME as FIRST_NAME2_2_,
        person0_.LAST_NAME as LAST_NAME3_2_,
        person0_.salary as salary4_2_ 
    from
        Person person0_

//Hibernate: 
    select
        address0_.Address_ID as Address_ID1_0_0_,
        address0_.city as city2_0_0_,
        address0_.COUNTRY_ID as COUNTRY_ID5_0_0_,
        address0_.state as state3_0_0_,
        address0_.Zip_Code as Zip_Code4_0_0_ 
    from
        Address address0_ 
    where
        address0_.Address_ID=?

/*
Id: 1602, Name: Bruce Almighty, Salary: 30

Id: 1600, Name: Johny Travol, Salary: 50

Id: 1601, Name: Kevin Becker, Salary: 20


Read all records.
Done with execution
*/


Note: The above exception is intentionally kept in the code to show that you cannot delete a record whose primary key is available as foreign-key in some other table.

The delete operation cascades to all the child entities because of the annotation:
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)

So a Person-delete calls Address-delete.
But address cannot be deleted because other Persons are living there :) meaning other Person entries have that address in their foreign-keys.



Observations


  1. For the very first insert statement, Hibernate first secures primary-key for each of the entities involved.

  2. For the very first insert statement, there are 3 insert statements but after that, 2 of the insert statements become update statements as the entities for them are the same.

  3. Delete record is cascaded to child tables by virtue of annotation @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)

  4. Do not use the below configuration in pom.xml
    <configuration>
      <minimizeJar>true</minimizeJar>
    </configuration>
    
    The above strives to remove dependencies which are not used by the project but in doing so, it can remove several good dependencies also and it may be a pain to add them all one by one.







Like us on Facebook to remain in touch
with the latest in technology and tutorials!


Got a thought to share or found a
bug in the code?
We'd love to hear from you:

Name:
Email: (Your email is not shared with anybody)
Comment:

Facebook comments:

Site Owner: Sachin Goyal