1) Introduction
Persistent data can be seen anywhere in an application. Managing persistent data is one of the few challenges that modern technologies/products are facing. A solution called Object-Relational Mapping (ORM) has gained major popularity over the past few years. ORM is a piece of software/product for the representation and conversion of data between the database and the object-oriented programming language. Hibernate is one such ORM solution and it is an open-source project.
Though Hibernate Framework is not the only persistence solution, it has become very famous over the recent past because of its huge variety of features when compared with its competitors. It takes much of the database related boiler-plate code from the developers, thereby asking the developers to concentrate on the core business logic of the application and not with the error-prone SQL syntax.
2) Persistence
The definition of persistence can be given like this, “Data that can be stored to some permanent medium and can be seen at any point of time even after the application that created the data has ended”. Persisting (or preserving) data is not an easy task and it is one of the basic necessities for almost any application. The common storage mediums that we see in our day-to-day life are hard-disk and a database.
Databases are the most preferred storage medium for persisting data because of the relatively simple way for data-access using the Structured Query Language (SQL). Data within a database can be viewed in a table format, where each row in the table represents a single record of data.
3) Object-Relational Mapping
As mentioned in the introductory part, ORM software greatly simplifies the transformation of business data between an application and a relational-database. ORM can be viewed as a bridge between an application and the relational-database that it is depending on.
The following picture clearly depicts that:
ORM acting as a bridge between the application and the database As one infer from the above picture, application depends on the ORM for all the database-related services like persisting service (for saving the data), query service (for retrieving existing data from the database) and the ORM takes care of communicating with the appropriate database.
Some of the most popular ORM project/products are iBatis Data Access Objects from Apache, NDO (.NET Data Objects) for .NET languages, TopLink from Oracle and PowerDesigner.
4) Comparison with other Technologies
Before the advent of Hibernate, there were so many other technologies and specifications from Sun, for persistence. Few are them are explained in detail in the following sections along with their drawbacks which led them not being able to become a successful one.
4.1) Problems with JDBC
Java provided an initial solution for persistence in the form of JDBC and developers are depending on JDBC API’s to connect to the database. One of the major drawbacks of JDBC is that, one has to embed the application’s data into the SQL syntax, i.e., for saving the data in an application, the business object has to be converted to relational-object model by embedding the various pieces of data within the SQL language syntax.
Consider the following situation, there is a business object called Employee with properties name and age and this has to be persisted to a database. The following is the class representation of the Employee class,
class Employee { private String name; private int age; //Other Methods go here. } Employee employee = getEmployeeObject(); String sqlInsertStmt = "INSERT INTO EMPLOYEE VALUES ('" + employee.getName() + "', '" + employee.getAge() + "')"); statement.executeQuery(sqlInsertStmt);
The above is generally a poorer code, and also the business object (Employee object, in our case) is tightly integrated with the SQL syntax.
During data retrieval, the reverse of the above process will happen. The information that is fetched from the database has to be mapped to the business object. Consider the following piece of code which has the conversion process of representing the business object from the relational data model.
String sqlQueryStmt = "SELECT * FROM EMPLOYEE WHERE EMP_ID = '100'"); ResultSet employees = statement.executeQuery(sqlInsertStmt); if (employees.next()) { Employee fetchedEmployee = new Employee(); fetchedEmployee.setName(employees.getString("name")); fetchedEmployee.setAge(employees.getInt("age")); }
4.2) Entity Beans
Entity beans, which were one of the major components in the EJB Specification failed drastically to provide a persistent layer in the applications.
The initial version of the EJB specification has little support for the Entity beans. The first disadvantage is, an Entity Bean is not a local object, but a remote object, so, there are overheads related to network performance issues. And the worst thing is that the relationships between entity beans have to be manually managed in the application code and the actual mappings were done in vendor-specific configuration files which invented lots of changes in the configuration files when the application is made to point out to a different database server.
Though, the later version of the specification overcome many of the drawbacks that were found in the initial versions by introducing the notion of local interfaces for representing an enterprise bean and EJB Query Language (EJB-QL), etc., the art of designing and managing an entity bean still yielded much complexity in the hands of the developers.
5) Hibernate – ORM product
Hibernate is one of the Object-Relational Mapping software for persisting and querying data. Most of the Hibernate features look very similar that are found in Java Persistence API (JPA) specification. Also Hibernate provides some add-on functionalities that are not mentioned in the JPA specification. Hibernate doesn’t replace JDBC. Hibernate is sitting on top of JDBC to connect to the database. Internally Hibernate is using these JDBC calls to connect to the database systems.
Hibernate is neither forcing its developers to use some specific interfaces for the classes to be persisted in an application nor it requires the program to follow some standard design to achieve its goals. For this reason, Hibernate can well integrate with all kinds any of J2SE/J2EE application and with any kind of frameworks (like Spring, Struts etc).
5.1) Hibernate Modules
The open-source Hibernate project has various different modules each representing a unique functionality.
- Hibernate Core which represent the basic and the core module of the Hibernate project provides support for OO features along with the Query services.
- Another module called Java Persistence with Hibernate is an implementation for the specification “Java Persistence API (JPA)” from Sun and we can be used in any Sun’s compliant Application Server as well as with J2SE applications.
- There is a separate module called Hibernate Search that provides rich functionality for searching the persistence domain model using a simplified API.
- For validating the data, Hibernate comes with a set of validation rules in the form of Annotations in a module called Hibernate validations and it also allows the developers to create custom validation rules.
- Support for Querying and inserting data from multiple databases can be achieved by using
the Hibernate Shards API.
6) Hibernate Core API
Following section covers the various core interfaces/classes that should be used by an application which is depending on the persistence-level service from Hibernate.
6.1) The ‘Configuration’ class
The Configuration class is the initial entry point of a Hibernate application. An instance of Configuration class represents application specific configuration that provides persistence services. This class provides convinient ways to import the Hibernate Configuration File and the necessary Mapping Files (which are discussed later) into an application which can be later used by SessionFactory(which is detailed in the next section) class to create Session objects.
The following code shows how to create and configure a Configuration object.
Configuration configuration = new Configuration().configure();
Calling the Configuration.configure() method will configure the object by loading the configuration file and the mapping files that are available in the application’s class-path.
Usually only one instance of a Configuration class will be maintained by an application.
6.2) The ‘SessionFactory’ interface
The Configuration class can be used to create SessionFactory object which serves as a factory class that creates Session objects (discussed in the next section). An application may have one SessionFactory class but many number of Session objects. The following code shows how to create a SessionFactory object by calling the Configuration.buildSessionFactory().
Configuration configuration = new Configuration().configure(); SessionFactory sessionFactory = configuration.buildSessionFactory();
Configuration.buildSessionFactory() will create a new SessionFactory object with configurations and the mapping information taken from the Hibernate Configuration and the Mapping Files.
6.3) The ‘Session’ interface
A Hibernate Session interface forms the primary interface for promoting persistence services to the application classes. The following code shows how to create a Session object.
Configuration configuration = new Configuration().configure(); SessionFactory sessionFactory = configuration.buildSessionFactory(); Session newSession = sessionFactory.openSession();
Calling the SessionFactory.openSession() method, will create a new database connection (the connection related information like the Database server, username, password etc., will be taken the Hibernate configuration files which is discussed in the later section) and opens a new session from the created connection. Calling the SessionFactory.openSession() method again will create new Session objects as well as new java.sql.Connection object. One can get a reference to the newly created connection by calling the Session.connection() method, like the following,
java.sql.Connection connection = session.connection();
6.3.1) Object Classification based on States
Three different types of objects are categorized in Hibernate technology based on the state of the object. They are,
- Persistent Object
- Transient Object
- Detached Object
Persistent Object:
A persistent object is one which is associated with the current Session and has its state values synchronized with the database.
Transient Object:
A transient object doesn’t have any association with the current Session, but can be made persistent at a later time.
Detached Object:
A detached object was having an association at a previous point of time with the Session interface, but now is made to detach (taken-off) from the current Session.
6.3.2) Operations using the Session Interface
As seen previously, the Session interface provides various services in the form of operations for persisting any Object. It also provides transactional services to the persistent objects. Rich Querying operations are also supported in the form of Query interface. The following code can be used to persist an object.
Employee objectToBePersisted = new Employee(); // Set properties for employee object here. Session session = …; session.save(objectToBePersisted);
Session.save(object) can be used to save a persistent object along with a unique key (primary key). Calling the method again on the same object will cause the following exception to be thrown. org.hibernate.exception.ConstraintViolationException. However, calling this method doesn’t immediately synchronize the state of the object with the database. It will just mark this object to be saved. Calling the method Session.flush() will make all the objects that are associated with the Session to be synchronized with the database.
session.flush();
There is a method called Session.update(object) which is used to update the state of the persistent object that was modified for some reasons after a call to Session.save(object). Consider the following code which will demonstrate this,
Employee employee = …; //Set employee properties here. session.save(employee); //Update the properties of employee object here. employee.set(…); session.update(employee);
If you don’t like to use Session.save(object) or Session.update(object), use the smart Session.saveOrUpdate(object) method, which will call Session.save(object), if the object in consideration is a new object or will call Session.update(object), if it is an updated (saved) object.
session.saveOrUpdate(employee);
There are so many other useful services and operations available from Session interface like Session.delete(object), Session.merge(object), and Session.refresh(object). Objects can be retrieved from the Database using the Query interface. The following code resembles the traditional select query to select all the object from a table called User.
Query selectQuery = session.createQuery("FROM User"); List userList = selectQuery.list(); for(int index = 0; index < userList.size(); index ++){ User user = (User)userList.get(0); System.out.println("Name = " + user.getName() + ", Age = " + user.getAge()); }
7) Configuration and Mapping Files
This section details the structure and functionality of the Hibernate XML Configuration File and the Hibernate Mapping Files.
7.1) Hibernate Configuration File
An application that uses Hibernate API must use a single configuration file. A configuration file for a Hibernate application will tell some of the information like the URL of the Database Server to which the application wants to connect, the username/password for the Database, class name of the Driver file and with other set of preferences.
A Hibernate configuration file can be an XML-based file (hibernate.cfg.xml) or it can be ordinary Java properties file (hibernate.properties) with key-value combination. This configuration file should be placed in the run-time classpath of an application. If both the XML-based configuration file and the properties-based configuration file are found in the classpath, then the XML-based configuration file will take preference over the other.
Following is the structure of a Hibernate configuration file,
Hibernate.cfg.xml:
<?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC "- //Hibernate/Hibernate Configuration DTD//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="hibernate.connection.driver_class"> ClassNameOfTheDriver </property> <property name="hibernate.connection.url"> DbServerUrl </property> <property name="hibernate.connection.username"> DbUser </property> <property name="hibernate.connection.password"> DbPassword </property> <mapping resource="MappingFile1.xml"/> <mapping resource="MappingFile2.xml"/> ..... <mapping resource="MappingFileN.xml"/> </session-factory> </hibernate-configuration>
Let us look into the structure of the XML in detail here. The first observable thing is that this XML has a well-defined DTD defined from “http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd” meaning that the construction of this XML document should be validated against the DTD.
The outer-most element is the ‘hibernate-configuration’ element which forms the root element for specifying the configuration information. The next inner element is the ‘session-factory’ element which corresponds to the SessionFactory class that contains information related to the Database like the URL of the Database server, the username/password information on the Database server etc.
The various properties within the ‘session-factory’ element is represented in Hibernate Configuration file as a ‘property’ element.
Whenever we say,Configuration configuration = new Configuration().configure(), the no argument Configuration.configure() method will load the configuration file called “hibernate.cfg.xml” from the application’s classpath. The default name for the Hibernate Configuration file is “hibernate.cfg.xml”. Different configuration file can be loaded into the application by calling the one-argument Configuration.configure() method, like the following,
File configurationFile = new File(“.\\config\\MyConfiguration.xml”); Configuration configuration = new Configuration().configure(configurationFile);
The code, SessionFactory sessionFactory = configuration.buildSessionFactory(), will populate the SessionFactory object with the set of property values values that are taken from the ‘session-factory’ XML element. So, in our case, the SessionFactory object will be populated with values the class name of the driver, the URL of the Database server and the username/password of the Database.
The next section in the Hibernate Configuration File is the ‘mapping’ element which represents a XML file that contains the mapping information such as how a Java Class should be mapped against a Database table. The next section deals with the structure of the Mapping Files in greater detail.
The same configuration information in the XML-based Hibernate configuration file can be given in the property-based Hibernate configuration file and such a file may look like the following,
hibernate.properties:
hibernate.connection.driver_class = ClassNameOfTheDriver hibernate.connection.url = URLOfTheDatabaseServer hibernate.connection.username = DatabaseUsername hibernate.connection.password = DatabasePassword
7.2 Hibernate Mapping Files
A Hibernate mapping file provides mapping information like how a Java Class is mapped to a relational database table. It also contains other information like which Java Property (or Field) in a class will map to which Table Column. Relations between entities can be defined in the Mapping file through various associations like one-to-one, one-to-many, many-to-many etc.
The mapping files that are needed for an application are defined and referenced in the Hibernate configuration file throught the ‘mapping’ element. If you remember, the Hibernate configuration file has references to Hibernate mapping files like this,
<hibernate-configuration> <session-factory> ...... <! -- Mapping files --> <mapping resource="MappingFile1.xml"/> <mapping resource="MappingFile2.xml"/> ...... <mapping resource="MappingFileN.xml"/> </session-factory> </hibernate-configuration>
Let us look into the structure of a mapping file.
TestMapping.xml:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="ClassNameWithPackage" table="TableName"> <id name="fieldName" column="DbColumnName"> <generator/> </id> <property name=" fieldName"> <column name="DbColumnName"/> </property> .... </class> </hibernate-mapping>
Like the Hibernate Configuration file, this mapping file has to be validated against the DTD given by “http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd”. The root element in this XML file is the ‘hibernate-mapping’ which acts as a container for several different mapping definitions in the form of ‘class’ tag. Any number of mapping definitions can be given inside the ‘hibernate-mapping’ element, but the preferred way is to give one definition per XML file.
The ‘name’ attribute in the ‘class’ element is the name of the Java class that we want to persist. The ‘table’ attribute represents the name of the table for this java Class in the database. All the ‘property’ elements will map Java fields to Table Column names. The Java Field is identified by the ‘name’ attribute in the ‘property’ element and the table column name is represented by the ‘name’ attribute in the ‘column’ element.
A primary key is a must for every table as it used to uniquely identify Java instances. The ‘id’ element represents the primary-key portion wherein with ‘name’ and the ‘column’ attribute corresponds to the Java Field and the table primary key column. The value for the ‘generator’ element represents how the primary-key is generated for the table. So many different implementations for generating a primary-key are available in Hibernate like ‘identity’, ‘select’, ‘assigned’, etc. If the value of the ‘generator’ element is ‘assigned’, then it is the responsibility of the developer to manually set the primary key
8) Test Application
With the above basics and theory in mind, let us develop a sample application whose sole purpose is to persist a Java object in the Database.
8.1) Pre-requisites
Following are the pre-requisite softwares needed to run the application.
- Java Development Kit:
(http://java.sun.com/javase/downloads/index.jsp) - Hibernate 3.2 (http://www.hibernate.org/6.html)
- MySQL Database Server
(http://dev.mysql.com/downloads/mysql/6.0.html) - MySQL Database Driver
(http://dev.mysql.com/downloads/connector/j/3.1.html)
Three components are needed for this sample application. One is the Java Class whose instances are to be persisted. The other one is the Hibernate Configuration file (hibernate.cfg.xml) which will contain all the configuration related information related to the database like the database URL, the class-name of the driver, the username/password of the Database etc. The final one is the XML Mapping file which provides mapping information between the Java class and the database table name.
Let us assume that all the above softwares/products are installed to appropriate location, and let us assume that the name of the MySQL database is ‘DbForHibernate’.
8.2) Sample
Following is the code listing of a Java class called ‘User’ that is to be persisted.
User.java:
package test; public class User { private String name; private int age; private long id; public User(){ } public String getName(){ return name; } public void setName(String name){ this.name = name; } public int getAge(){ return age; } public void setAge(int age){ this.age = age; } public long getId(){ return id; } public void setId(long id){ this.id = id; } }
Nothing great to explain about this class. This ‘User’ class follows the standard JavaBeans specification for representing the properties ‘name’, ‘age’ and ‘id’ that we wish to persist. Let we see in the Hibernate Mapping file, how these Java fields are mapped to the Database column names.
Hibernate Configuration file:
Following is the Hibernate configuration file.
hibernate.cfg.xml:
<?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="hibernate.connection.url"> jdbc:mysql://localhost/DbForHibernate </property> <property name="hibernate.connection.driver_class"> com.mysql.jdbc.Driver </property> <property name="hibernate.connection.username"> root </property> <property name="hibernate.connection.password"> root </property> <property name="dialect"> org.hibernate.dialect.MySQLDialect </property> <! -- Mapping files --> <mapping resource="person.hbm.xml"/> </session-factory> </hibernate-configuration>
The above XML file has the various database parameters like the URL, class name of the driver, username and password set to ‘jdbc:mysql://localhost/DbForHibernate’, ‘com.mysql.jdbc.Driver’, ‘root’ and ‘root’ respectively. We have already seen how these various parameters will affect the configuration of the SessionFactory object.
Sql Dialects:
The only new property defined in this file is the ‘dialect’ property which is pointing to org.hibernate.dialect.MySQLDialect. Dialects are one among the various new features in Hibernate. Consider a database vendor ‘DB-V1’ who provides a feature called performance tuning using some SQL syntax. If some other database vendor ‘DB-V2’ provides that performance tuning feature, then it’s very likely that the syntax of both the queries will be different.
Suppose, if such a feature is enabled in a hibernate application by some piece of code, Hibernate should using two different syntax for two different databases. How will Hibernate knows the correct SQL syntax for different databases?
The answer lies in SQL Dialect. Every database has their own SQL Dialect. The following provides some major SQL Dialects from some popular Database vendors.
Database | SQL Dialect |
Microsoft SQL Server | org.hibernate.dialect.SQLServerDialect |
MySQL | org.hibernate.dialect.MySQLDialect |
Oracle | org.hibernate.dialect.OracleDialect |
Informix | org.hibernate.dialect.InformixDialect |
Sybase | org.hibernate.dialect.SybaseDialect |
So, if an application uses any vendor specific features, it is a must that the application must provide that particular SQL Dialect in the Hibernate Configuration file to function properly.
Hibernate Mapping File:
The XML-based Hibernate mapping file is shown below,
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="test.User" table="User"> <id name="id" column="Id"> <generator/> </id> <property name="name"> <column name="Name"/> </property> <property name="age"> <column name="Age"/> </property> </class> </hibernate-mapping>
As explained previously, the Hibernate mapping file contains the mapping information between a Java Class and the database table name. In the above file, the value for the ‘generator’ element is given as ‘assigned’, which means that the application when persisting the Java object must manually set the value for the primary key (in our case, it’s the ‘Id’ column). If it is not set, then an exception will be thrown by Hibernate at run-time.
Client Application:
Following is the listing for the Client Application.
RunClient.java:
package client; import org.hibernate.*; import org.hibernate.cfg.Configuration; import test.User; public class RunClient { public static void main(String[] args) { try{ Configuration conf = new Configuration().configure(); SessionFactory sessionFactory = conf.buildSessionFactory(); Session session =sessionFactory.openSession(); User user = new User(); user.setId(1000); user.setName("Test User"); user.setAge(24); Transaction t = session.beginTransaction(); session.save(user); t.commit(); session.close(); }catch(Exception e){ e.printStackTrace(); } } }
For the above program to compile and run properly, two set of Jar files have to placed properly in the application’s class-path. One is the set of Jar files that are available within the Hibernate Ditribution, which is normally the <HIBERNATE_INSTALLATION>\lib, where <HIBERNATE_INSTALLATION> refers to the installation of the Hibernate. Following jar files from the Hibernate distribution have to be maintained in the class-path of the application,
- cglib2.jar
- commons-collections
- commons-Logging
- dom4j.jar
- ehcache.jar
- jdbc2.0-stdext
- jta.jar
- log4j.jar
- odmg.jar
- xalan.jar
- xerces.jar
- xml-apis.jar
The other set of the jar files are for the Jdbc Driver, which is ‘mysql-connector-java-5.0.5-bin.jar’ in the case of MySQL Database. The above code establishes a Configuration object by calling new Configuration().configure() which scans the classpath for the presence of hibernate.cfg.xml file. It then creates an instance of SessionFactory object with the details populated from the Configuration File by calling Configuration.buildSessionFactory(). A new Session object is then created for persisting objects by calling SessionFactory.openSession().
A new Java object called ‘User’ is populated with some test values and the object is persisted into the Session object within a transactional context by calling Session.beginTransaction() which marks the beginning of the transaction. Remember that Session.save(object) only marks the object to be persisted. After a successful save operation and transaction is committed by calling Transaction.commit(), which means that the object will be synchronized with the database.
9) Conclusion
This article started with an introduction to the elementary concepts like persistence and Object-Relational Mapping. It then compared the various technologies like Java Database Connectivity (JDBC) and Entity Beans that existed before the evolution of Hibernate and their drawbacks. Then the various Core API’s like Configuration, Session and SessionFactory classes are given detailed discussion, followed by the structure and the representation of Hibernation Configuration File and the Hibernate Mapping Files. This article concluded with a sample application that shows how to interact with the database.
If you have any doubts on understanding the concept of Hibernate please post it in the comments section below. We will be happy to answer all your questions. If you are interested in receiving the future java articles and tips from us, please subscribe here. If you are looking for any book suggestions, please post it in the comments section.