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
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
}
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 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
For the very first insert statement, Hibernate first secures primary-key for each of the entities involved.
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.
Delete record is cascaded to child tables by virtue of annotation @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
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.
Got a thought to share or found a bug in the code? We'd love to hear from you: