WHAT IS HIBERNATE?
Hibernate ORM (Hibernate in
short) is an object-relational mapping library for the Java language, providing
a framework for mapping an object-oriented domain model to a traditional
relational database [1].
Hibernate's primary feature is
mapping from Java classes to database tables (and from Java data types to SQL
data types). Hibernate also provides data query and retrieval facilities. It
generates SQL calls and relieves the developer from manual result set handling
and object conversion. Applications using Hibernate are portable [1].
The Hibernate architecture is
layered to keep you isolated from having to know the underlying APIs. Hibernate
makes use of the database and configuration data to provide persistence
services (and persistent objects) to the application [2].
Following is a very high level
view of the Hibernate Application Architecture.
Figure 1 [2]
Persistent Objects
A persistent object is an object
that has been assigned a storage location in a federated database. When you
commit the transaction in which you create a persistent object, that object's
data is saved in the database; the object can then be accessed by other
processes. A persistent object continues to exist beyond the duration of the
process that creates it. In contrast, a transient object exists only within the
memory of the process that creates it; when that process terminates, the
transient object ceases to exist [3].
Hibernate Objects
Configuration Object
The Configuration object is the
first Hibernate object you create in any Hibernate application and usually
created only once during application initialization. It represents a
configuration or properties file required by the Hibernate.
SessionFactory Object
Configuration object is used to
create a SessionFactory object which inturn configures Hibernate for the
application using the supplied configuration file and allows for a Session
object to be instantiated. The SessionFactory is a thread safe object and used
by all the threads of an application.
You would need one SessionFactory
object per database using a separate configuration file. So if you are using
multiple databases then you would have to create multiple SessionFactory
objects.
Session Object
A Session is used to get a
physical connection with a database. The Session object is lightweight and
designed to be instantiated each time an interaction is needed with the
database. Persistent objects are saved and retrieved through a Session object.
Transaction Object
A Transaction represents a unit
of work with the database and most of the RDBMS supports transaction
functionality. Transactions in Hibernate are handled by an underlying
transaction manager and transaction (from JDBC or JTA) [2].
MYGEDIZ EXAMPLE IN NETBEANS
Create Java Project
In netbeans, we create a new Java
Project by choosing New Project choice under File Menu.
(Figure 2)
Figure 2
In displayed window we choose
Java Category and Java Application Project. Then Next button is clicked.
(Figure 3 )
Figure 3
Write Project name and if you
don’t want to have main class automatically, you should unselect Create Main
Class option. Then click Finish. We have created a Java Project. (Figure 4)
Figure 4
Add Required Library/Jar
MYGEDIZ Project will use
Hibernate framework. So our first job is to add required libraries. Right over
click Library directory. Select Add Library. (Figure 5)
Figure 5
Find Hibernate and Hibernate JPA
in the list and add them. (Figure 6)
Figure 6
Figure 7
We should add ojdbc6.jar
file too.
Design the Application
Now we can create new JFrame
Form. Right Click over package – JFrame Form. Right class name and click
Finish. (Figure 8)
Figure 8
We want to design a frame which is
like Figure 9.
.
Figure 9
For this reason we add Tabbed
Pane to frame and resize using arrow. We cover whole frame.
(Figure 10)
Figure 10
To add tab,select Panel and add
to tabbed pane. Then resize again and cover the pane. (Figure 11)
Figure 11
Then right click over tab1 text
to change tab name. For Examle first tab will be login tab. (Figure 12)
Figure 12
Now we add a Label to display
message that shows which field will be used to enter username. Select label in
palette and put int anywhere you want. Using little boxex you can resize
easily. To change label name, double click and enter label script. (Figure 13)
Figure 13
Now we want to add text field.
Select textfied from palette. Put it to panel. Right Click – Change variable
name. Then give meaningfu variable name to use it easily while coding. Then
again right click- edit text to delete jTextField1 text. Otherwise when
application is run it will be displayed. (Figure 14)
Figure 14
Add password label and password
field using requiered swings from palette. Then add button to click to login.
Choose OK button from palette and again put it to panel. Change variable name
and text. Then to give action double click on the button. Netbeans bring you
tou source code of action of the button automatically.
Go on designing until reaching
design like in Figure 9.
In this example we have login
page to access the application. We have users whose responsibility is to add
students, display, delete and update student information in mygediz system. So
we need three tables. For one table users to hold username and password of all student
affairs officers. For one table to hold students and the last one is to hold
all phone numbers of students. Becuase each student may have one more than
phone numbers. (Figure 15)
Figure 15
Design and Code Database Tables
Connect the database through
SQLPlus or SQLDeveloper. Create all required tables with given properties.
Cretating Users Table
In users table our primary key
will be username. Because it should be unique for each user. Instead of
username, we may add new column as id to select as primary key and add unique constraint to username. But we prefer
not to add extra column. We use username as primary key.
create table
users (username varchar(20),password varchar(20),primary key(username));
We should add users to the database.
insert into users values('esra','123456');
insert into
users values('said','111222');
Creating Students Table
create table students(
id int,
name varchar(20) not null,
surname varchar(20) not null,
department varchar(20),
gpa number(3,2),
primary key(id)
);
Creating Phones Table
Students may have one or more
phone numbers. So we hold Phones table which will have auto incremented id
column, stuId column which refers students table and shows which phone number
is belong to which student. So stuId is foreign key for phones table.
create table phones (
id int,
stuId int,
phone varchar(10) not null,
foreign key (stuId) references students,
primary key(id)
);
In Oracle to provide auto_increment we have to create
sequence and trigger. In phones table we want to start id with 1 and increment
it by 1.
CREATE SEQUENCE
phone_seq MINVALUE 1
START WITH 1
INCREMENT BY 1
CACHE 20;
CREATE OR REPLACE TRIGGER
phone_auto_increment
BEFORE INSERT ON phones
for each row
begin
select phone_seq.nextval into :new.id from dual;
end;
/
Creating Java Classes
For each table we have to create
a java class that is used to map. After the other configurations are done, we
can insert, delete, update any record as easily as creating java objects. We
have three tables : Users, Students, Phones. So we create three Java classes.
User.java
Under datapack package, we create
User.java class. In Uers table we have two columns whose names are userName and
password respectively. So in java class we create two fields to be used mapping
of these columns. We have to generate getter and setter methods. Because
hibernate needs them. To generate getter and setter methods in netbeans
automatically, right click over editor -> Insert code -> Getter and
setter. (Figure 16)
Figure 16
package datapack;
public class User {
private String userName;
private String password;
public String getUserName()
{
return userName;
}
public void setUserName(String
userName) {
this.userName = userName;
}
public String getPassword()
{
return password;
}
public void setPassword(String
password) {
this.password = password;
}
}
Student.java
Again under datapack package, we
create Student.java class. Our student has id, name, surname, department, gpa
information. So we use these columns’ name and write as attributes in this
class. We can give different names but to facilitate our job, we use same
names. In this scenario Students and Phones tables have one-to-many relation.
It means one student may have zero or more phone numbers. Likewise a phone
number can be just mapped to one student and many phone numbers can be mapped
to one student. To be able to access phone numbers in student object we have to
declare one-to-many relation in mapping file. To hold many phone numbers, we
have to declare a java data structure. So we declare Set<Phone> phones
attribute.
package datapack;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Column;
import
javax.persistence.Entity;
import
javax.persistence.Id;
import
javax.persistence.Table;
/**
*
* @author esranur.galip
*/
/*We can use annotations of
J(ava)P(ersistence)A(pi) to Show which attribute is mapped to which column of
table */
@Entity
@Table(name = "STUDENTS")
public class Student {
@Id
@Column(name = "id")
private int id;
@Column(name = "name")
private String name;
@Column(name = "surname")
private String surname;
@Column(name = "gpa")
private double gpa;
@Column(name = "department")
private String department;
private Set<Phone> phones = new
HashSet<Phone>();
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getSurname()
{
return surname;
}
public void setSurname(String
surname) {
this.surname = surname;
}
public double getGpa() {
return gpa;
}
public void setGpa(double gpa) {
this.gpa = gpa;
}
public String
getDepartment() {
return department;
}
public void setDepartment(String
department) {
this.department = department;
}
public Set<Phone>
getPhones() {
return phones;
}
public void
setPhones(Set<Phone> phones) {
this.phones = phones;
}
public String toString() {
String text = "Id\tName\tSurname\tDepartment\tGpa\tPhones\n";
text += id + "\t" + name + "\t" + surname + "\t" + department + "\t" + gpa;
for (Phone p : phones) {
text += "\t" + p.getNumber();
}
text += "\n";
return text;
}
}
We write toString method to be
able to use when a student’s information is displayed. We add all values to
text variable by formatting tabs. Then we add phone information by traversing
the phones list. Because of list object type is Phone, in for loop condition we
can declare Phone p varible and assign phones list to the variable. So in each
iteration p variable will have one phone value. In for loop we add phone number
to text variable.
Phone.java
Phones table has id, stuId and
phone columns. stuId is foreign key and refers to students table. As we said
before we have one-to-many relation between students and phones tables. So to
provide this mapping we added hashset of phones in student java class. In phone
class we declare student referans as attribute to relate phone to student.
package datapack;
/**
*
* @author esranur.galip
*/
public class Phone {
private int id;
private Student student;
private String number;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getNumber() {
return number;
}
public void setNumber(String
number) {
this.number = number;
}
public Student getStudent()
{
return student;
}
public void setStudent(Student
student) {
this.student = student;
}
}
After creating all classes I want
to add addPhone method to Student java class to facilitate to add phone
information. Because before adding new phone number, we have to set student
attribute of that phone after that we add that phone to student’s phone list.
public void addPhone(Phone
phone) {
phone.setStudent(this);
phones.add(phone);
}
After adding this method Student
java class is this :
package datapack;
import java.util.HashSet;
import java.util.Set;
import
javax.persistence.Column;
import
javax.persistence.Entity;
import
javax.persistence.Id;
import
javax.persistence.Table;
/**
*
* @author esranur.galip
*/
/*We can use annotations of
J(ava)P(ersistence)A(pi) to Show which attribute is mapped to which column of
table */
@Entity
@Table(name = "STUDENTS")
public class Student {
@Id
@Column(name = "id")
private int id;
@Column(name = "name")
private String name;
@Column(name = "surname")
private String surname;
@Column(name = "gpa")
private double gpa;
@Column(name = "department")
private String department;
private Set<Phone> phones = new
HashSet<Phone>();
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getSurname()
{
return surname;
}
public void setSurname(String
surname) {
this.surname = surname;
}
public double getGpa() {
return gpa;
}
public void setGpa(double gpa) {
this.gpa = gpa;
}
public String
getDepartment() {
return department;
}
public void setDepartment(String
department) {
this.department = department;
}
public Set<Phone>
getPhones() {
return phones;
}
public void addPhone(Phone
phone) {
phone.setStudent(this);
phones.add(phone);
}
public void
setPhones(Set<Phone> phones) {
this.phones = phones;
}
public String toString() {
String text = "Id\tName\tSurname\tDepartment\tGpa\tPhones\n";
text += id + "\t" + name + "\t" + surname + "\t" + department + "\t" + gpa;
for (Phone p : phones) {
text += "\t" + p.getNumber();
}
text += "\n";
return text;
}
}
Implementation
Configuration of Hibernate Files
Hibernate.cfg.xml
To connect to the database and
find the required mapping files we have to create hibernate configuration file.
I prefer to collect all hibernate files under hibernatepacks. So first we
create hibernatepacks package. Then to create the configration file, right
click over package, select Hibernate Configuration Wizard. (Figure 17, 18, 19)
Figure 17
Figure 18
Figure 19
NetBeans automatically generates configuration file. But we
need modification. (Figure 20)
Figure 20
To modify the code we select the
Source tab and we reach the source code. We change dialect as Oracle10gDialect.
Then we add password information and to display each transaction’s sql code in
the console we add Show_sql property. (Figure 21)
Figure 21
Users.hbm.xml
For each table in our database,
we prefer to create a class and hibernate mapping file. Mapping file provides
us to map each column to each attribute of class. To create new Hibernate
mapping file, right click over desired package, select Hibernate Mapping
Wizard. (Figure 22)
Figure 22
After giving name of file, we
will select the table whose mapping file is being created. (Figure 23)
Figure 23
Then to select related class for
mapping, click … button. Write class name into search field and find the
related class, add the class. (Figure 24)
Figure 24
We will do mapping between
hibernate-mapping tags. First we write class tag that declares which class
mapping is being done. name keyword in class tag we give path of mapped class.
Student java class is under datapack package. Then in table keyword we give
mapped table name. Between id tags we
declare our primary key column of our table. For other columns and attributes
we use property tag. name keyword in property tag specifies attribute
name of class. name keyword in column tag specifies related column name. (Figure 25)
Figure 25
After creating each hbm.xml file
we have to add it to hibernate configure file. Hibernate Mapping Wizard does
this operation as soon as cerating mapping file. If you don’t use Hibernate
framework, you should do it manually. (Figure 26)
Figure 26
Students.hbm.xml
Here we have different tag set.
This set tag is used to map phones hash set in Student java class. As doing
this we specify that in Phones table find all tuples whose stuId column is
equal to id of this student. So name keyword in set tag specifies hash set name
in Student table. Table keyword is declaring table name that holds student’s
phone number(s). Then with one-to-many tag we declare class name of phones
table.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate
Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="datapack.Student" table="STUDENTS">
<id name="id" type="int">
<column name="id"/>
</id>
<property name="name" type="java.lang.String">
<column length="20" name="name" not-null="true"/>
</property>
<property name="surname" type="java.lang.String">
<column length="20" name="surname" not-null="true"/>
</property>
<property name="department" type="java.lang.String">
<column length="20" name="department" not-null="false"/>
</property>
<property name="gpa" type="java.lang.Double">
<column name="gpa" precision="2" not-null="false"/>
</property>
<set name="phones" table="phones" cascade="all" inverse="true" >
<key>
<column name="stuId" not-null="true" />
</key>
<one-to-many class="datapack.Phone" />
</set>
</class>
</hibernate-mapping>
Phones.hbm.xml
In phones table we created
trigger to increment id automatically. Here we declare this using generator
keyword. To map student attribute whose type is Student java class we use
many-to-one tag. name keyword in the tag declare student attribute of Phone
java class. class keyword is path of Student java class.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate
Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="datapack.Phone" table="PHONES">
<id name="id" type="int">
<column name="id" />
<generator class="increment"/>
</id>
<many-to-one name="student" class="datapack.Student" cascade="all"> <column name="stuId" not-null="true" />
</many-to-one>
<property name="number" type="java.lang.String">
<column length="10" name="phone" unique="true" not-null="true"/>
</property>
</class>
</hibernate-mapping>
Last status of hibrerbate.cfg.xml
Figure 27
HibenateUtility.java
After writing configuration and
mapping files, to run code we have to create session to connect to the
database. So we first get instance of Sessionfactory by configuring
hibernate.cfg.xml file. This operation is done just one time. Because
SessionFactory is heavyweight. Using session factory we openSession. For each
database we have to create new session. For each transanction we have to begin
transaction. Note that commit the transaction and at the end of the application
don’t forget to close session. For this creating ang getting instance
operations we prefer to create HibenateUtility class under datapack package.
package datapack;
import
org.hibernate.Session;
import
org.hibernate.SessionFactory;
import
org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
/**
*
* @author esranur.galip
*/
public class HibernateUtility {
private SessionFactory sessionFactory;
private Session session;
private Transaction tx;
public void configureSession() {
sessionFactory = (SessionFactory) new
Configuration().configure("/hibernatepacks/hibernate.cfg.xml").buildSessionFactory();
}
public Session
openSession() {
session = sessionFactory.openSession();
return session;
}
public Transaction
beginTransaction() {
tx = session.beginTransaction();
return tx;
}
}
GUI.java
Here we have a part of GUI java
class code. Here we create an object of HibernateUtility class and we call
configureSession() method of it in constructor. Then we disabled all tabbes
except Login tab. Because if user can login successfully, then we want to
display contents of other tabs.
package mygediz;
import
datapack.HibernateUtility;
import datapack.Phone;
import datapack.Student;
import datapack.User;
import
org.hibernate.Session;
import
org.hibernate.Transaction;
public class Gui extends javax.swing.JFrame {
private HibernateUtility
utility;
public Gui() {
initComponents();
utility = new HibernateUtility();
utility.configureSession();
jTabbedPane1.setEnabledAt(1, false);
jTabbedPane1.setEnabledAt(2, false);
jTabbedPane1.setEnabledAt(3, false);
jTabbedPane1.setEnabledAt(4, false);
}
/* GUI components’ construction
methods here… */
…
private void
loginButtonActionPerformed(java.awt.event.ActionEvent evt) {
User user;
/*Open session and beginTransaction
using utility’s methods */
Session session = utility.openSession();
Transaction tx = utility.beginTransaction();
/*Check whether user entered
userName and password or not, if entered, using session object’s get method we
can find any record from table as object giving it’s id as parameter.*/
if
(!userName.getText().equals("") && !password.getText().equals("")) {
user = (User) session.get(User.class,
userName.getText());
tx.commit();
/*if user is null, it means we have
no record about user whose userName is equal to userName that was entered by
user */
if (user == null) {
loginMessage.setText("Invalid
userName");
}
/*If user is not null, it means we
have an user whose userName is equal to userName that was entered by user, we
check the password whether user object’s password is equal to password that was
entered by user or not. As said before using get method of session we get all
record values from table to user object. If it is not equal we display Invalid
password message */
else if
(!user.getPassword().equals(password.getText())) {
loginMessage.setText("Invalid
password");
}
/* If it equals, we display success
message and enable all tabs. */
else {
loginMessage.setText("Success");
jTabbedPane1.setEnabledAt(1, true);
jTabbedPane1.setEnabledAt(2, true);
jTabbedPane1.setEnabledAt(3, true);
jTabbedPane1.setEnabledAt(4, true);
}
}
/* If user didn’t enter a value or
directly click to login button, we display “Enter password” message */
else {
loginMessage.setText("Please enter
userName/password");
}
session.close();
}
private void
addButtonActionPerformed(java.awt.event.ActionEvent evt) {
/*Open
session and beginTransaction using utility’s methods */
Session session = utility.openSession();
Transaction tx = utility.beginTransaction();
/*We want to add new student. Values
are entered y user using gui components. So first we create a student object.
Then we set all values according to user entered values. */
Student newStudent = new Student();
newStudent.setId(Integer.parseInt(id.getText()));
newStudent.setName(name.getText());
newStudent.setSurname(surname.getText());
newStudent.setDepartment(department.getText());
newStudent.setGpa(Double.parseDouble(gpa.getText()));
/* We just use save method of
session object*/
session.save(newStudent);
/*Don’t forget to call commit methdo
of transaction */
tx.commit();
session.close();
addMessage.setText("Success");
}
/* This method is written to find
student record giving it’s id */
public Student
getStudent(Session session, int id) {
Student student = (Student) session.get(Student.class, id);
return student;
}
private void
updateButtonActionPerformed(java.awt.event.ActionEvent evt) {
/*Open
session and beginTransaction using utility’s methods */
Session session = utility.openSession();
Transaction tx = utility.beginTransaction();
/* find user using previously
written getStudent method*/
Student updatedStudent = getStudent(session,
Integer.parseInt(updateId.getText()));
if (updatedStudent == null) {
updateMessage.setText("Wrong
id");
} else {
/*If we have that student we can add
new phone.*/
Phone newPhone = new Phone();
newPhone.setNumber(updatePhone.getText());
updatedStudent.addPhone(newPhone);
/*Just call saveOrUpdate or just
update method of session */
session.saveOrUpdate(updatedStudent);
/*Don’t forget to call commit method
of transaction */
tx.commit();
updateMessage.setText("Success");
}
session.close();
}
private void
displayButtonActionPerformed(java.awt.event.ActionEvent evt) {
/*Open session using utility’s
method */
Session session = utility.openSession();
/* Find student */
Student displayedStudent = getStudent(session,
Integer.parseInt(displayId.getText()));
if (displayedStudent ==
null) {
displayArea.setText("Wrong
id");
} else {
/*Just paste information of student
to displayArea */
displayArea.setText(displayedStudent.toString());
}
session.close();
}
private void
deleteButtonActionPerformed(java.awt.event.ActionEvent evt) {
/*Open
session and beginTransaction using utility’s methods */
Session session = utility.openSession();
Transaction tx = utility.beginTransaction();
Student deletedStudent = getStudent(session, Integer.parseInt(deleteId.getText()));
if (deletedStudent == null) {
deleteMessage.setText("Wrong
id");
} else {
/* call delete method of session */
session.delete(deletedStudent);
tx.commit();
deleteMessage.setText("Success");
}
session.close();
}
/* Main class and attributes here */
…
}
Easy Way
Instead of creating mapping file
for each table, we can create just one mapping file. Between hibernate-mapping tags, for each class
we write class tag. Mappings are done between class tags for each table. In
hibernate.cfg.xml, insetead of writing 3 mapping files, we just write one
mapping file. Because this file contains all mappings.
Hibernate.cfg.xml
<?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-configuration>
<session-factory>
<property name="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</property>
<property name="hibernate.connection.driver_class">oracle.jdbc.OracleDriver</property>
<property name="hibernate.connection.url">jdbc:oracle:thin:@localhost:1521:XE</property>
<property name="hibernate.connection.username">esra</property>
<property name="hibernate.connection.password">123456</property>
<property name="show_sql">true</property>
<mapping resource="hibernatepacks/mappings.hbm.xml"/>
</session-factory>
</hibernate-configuration>
Mappings.hbm.xml
<?xml version="1.0"
encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping
PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="datapack.User"
table="USERS">
<id name="userName"
type="java.lang.String">
<column name="userName"
length="20"/>
</id>
<property name="password"
type="java.lang.String">
<column length="20"
name="password" not-null="true"/>
</property>
</class>
<class name="datapack.Student"
table="STUDENTS">
<id name="id"
type="int">
<column name="id"/>
</id>
<property name="name"
type="java.lang.String">
<column length="20"
name="name" not-null="true"/>
</property>
<property name="surname"
type="java.lang.String">
<column length="20"
name="surname" not-null="true"/>
</property>
<property name="department"
type="java.lang.String">
<column length="20"
name="department" not-null="false"/>
</property>
<property name="gpa"
type="java.lang.Double">
<column name="gpa" precision="2"
not-null="false"/>
</property>
<set name="phones"
table="phones" cascade="all"
inverse="true" >
<key>
<column name="stuId"
not-null="true" />
</key>
<one-to-many class="datapack.Phone"
/>
</set>
</class>
<class name="datapack.Phone"
table="PHONES">
<id name="id"
type="int">
<column name="id"
/>
<generator class="increment"/>
</id>
<many-to-one name="
student"
class=
"datapack.Student" cascade=
"all">
<column name="stuId"
not-null="true" />
</many-to-one>
<property name="number"
type="java.lang.String">
<column length="10" name="phone"
unique="true" not-null="true"/>
</property>
</class>
</hibernate-mapping>
REFERENCES