1. Hibernate Entities
A Hibernate entity consists of:- A simple POJO class
- A mapping strategy (aka. chosen implementation)
- Hibernate APIs.
2. Hibernate implementations
Hibernate supports several implementations to map POJO classes to a storage, and thus define ORM mappings:- Hibernate APIs and "hbm.xml" xml mappings
- Hibernate APIs and Annotation Mappings
- Java Persistence API (JPA)
- Envers
- OSGi
3. Example-1: Mapping strategy using the hbm.xml file
The "hbm.xml" mapping strategy consists of the following:
- Session Configuration file - Configures the Hibernate session factory. Usually it is called "hibernate.cfg.xml"
- Entities - POJO classes that represent the database tables
- Mapping files - Maps POJO entities to Database tables. Usually they are called "<class-name>.hbm.xml"
- Optionally Session Factory helper infrastructure classes
- Optionally Logging configuration
3.1 Session Factory Configuration file
SessionFactory is a global factory responsible for a particular database.
<?xml version='1.0' encoding='utf-8'?>
<hibernate-configuration>
<!-- JDBC connection pool (use the built-in) -->
<!-- SQL dialect -->
<!-- Enable Hibernate's automatic session context management -->
<!-- Disable the second-level cache -->
<!-- Echo all executed SQL to stdout -->
<!-- Drop and re-create the database schema on startup -->
<mapping resource="org/hibernate/tutorial/domain/Event.hbm.xml"/>
</session-factory>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- Database connection settings -->
<property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
<property name="connection.url">jdbc:hsqldb:hsql://localhost</property>
<property name="connection.username">sa</property>
<property name="connection.password"></property>
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">1</property>
<!-- SQL dialect -->
<property name="dialect">org.hibernate.dialect.HSQLDialect</property>
<!-- Enable Hibernate's automatic session context management -->
<property name="current_session_context_class">thread</property>
<!-- Disable the second-level cache -->
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>
<!-- Drop and re-create the database schema on startup -->
<property name="hbm2ddl.auto">update</property>
<mapping resource="org/hibernate/tutorial/domain/Event.hbm.xml"/>
</session-factory>
</hibernate-configuration>
Note: The "hbm2ddl.auto" option turns on automatic generation of database schemas directly into the database.
Lets examine the configuration file:
- First of all we start from a template:
<?xml version='1.0' encoding='utf-8'?>
<hibernate-configuration>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
</session-factory>
</hibernate-configuration>
- We add database connection settings:
<!-- Database connection settings -->
<property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
<property name="connection.url">jdbc:hsqldb:hsql://localhost</property>
<property name="connection.username">sa</property>
<property name="connection.password"></property>
- We add connection pool settings:
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">1</property>
Caution: The built-in Hibernate connection pool is in no way intended for production use. It lacks several features found on any decent connection pool.
- We define Hibernate's SQL dialect:
<!-- SQL dialect -->
<property name="dialect">org.hibernate.dialect.HSQLDialect</property>
The "dialect" property element specifies the particular SQL variant Hibernate generates.
- We define the session context strategy:
<!-- Enable Hibernate's automatic session context management -->
<property name="current_session_context_class">thread</property>
- We define the cache policy
<!-- Disable the second-level cache -->
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
- We define the log level of the executed queries:
<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>
- We define a helper property for developement, to ease the association relationships:
<!-- Drop and re-create the database schema on startup -->
<property name="hbm2ddl.auto">update</property>
The "hbm2ddl.auto" option turns on automatic generation of database schemas directly into the database.
- And lastly we define the mapping file(s) for persistent classes:
<mapping resource="org/hibernate/tutorial/domain/Event.hbm.xml"/>
3.2 Entity class
The Event class below is just a simple POJO class:
package org.hibernate.tutorial.domain;
public Date getDate() {
public void setDate(Date date) {
public String getTitle() {
public void setTitle(String title) {
import java.util.Date;
public class Event {
private Long id;
private String title;
private Date date;
public Event() {}
public Long getId() {
return id;
}
private void setId(Long id) {
this.id = id;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
Lets examine the class:
- id - The id property holds a unique identifier value for a particular event. We do not want to manually assign it a unique value, but prefer it to be generated automatically by the infrastructure. Hence it has a private setter method and a public getter method.
- title - Maps to a db table column in the "hbm.xml" mapping file
- date - Maps to a db table column in the "hbm.xml" mapping file.
3.3 Event.hbm.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 package="org.hibernate.tutorial.domain">
</hibernate-mapping>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.hibernate.tutorial.domain">
<class name="Event" table="EVENTS">
<id name="id" column="EVENT_ID">
<generator class="native"/>
</id>
</class>
<generator class="native"/>
</id>
<property name="date" type="timestamp" column="EVENT_DATE"/>
<property name="title"/>
Lets examine the mapping file:
- First of all it begins with a template:
<?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 package="org.hibernate.tutorial.domain">
[...]
</hibernate-mapping>
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.hibernate.tutorial.domain">
[...]
</hibernate-mapping>
- Then we add association to a database table:
<class name="Event" table="EVENTS">
</class>
The above association tells Hibernate how to persist and load object of class Event to the table EVENTS. Each instance is now represented by a row in that table.
- We map the unique identifier property to the tables primary key. We configure Hibernate's identifier generation strategy for a surrogate primary key column:
<id name="id" column="EVENT_ID">
<generator class="native"/>
</id>
</id>
In the above code we chose a generation strategy of "native", which offers a level of portability depending on the configured database dialect.
Note: "native" is no longer consider the best strategy in terms of portability. There are specifically 2 bundled enhanced generators:
- org.hibernate.id.enhanced.SequenceStyleGenerator
- org.hibernate.id.enhanced.TableGenerator
For further discussion, see Section 28.4, “Identifier generation”
- Lastly, we need to tell Hibernate about the remaining entity class properties. By default, no properties of the class are considered persistent:
<property name="date" type="timestamp" column="EVENT_DATE"/>
<property name="title"/>
In the snippet above, Hibernate will also search for getDate(), setDate(), getTitle() and setTitle() methods.
Note: The date property mapping include the "column" attribute. Hibernate by default uses the property name as the column name. The word "date" is reserved, and thus it needs to map it to a different name.
Note: The date property mapping include the "type" attribute. Hibernate will try to determine the correct conversion and mapping type itself if the "type" attribute is not present in the mapping. In this case Hibernate cannot know if the "date" property should map to a SQL date, timestamp, or time column, and thus we need to define it the "type" property.
3.4 Optionally Session Factory Helper Infrastructure Classes
Hibernate is controlled and started by a org.hibernate.SessionFactory object. It is a thread-safe global object that is instantiated once. It is used to obtain org.hibernate.Session instances, that represents a single-threaded unit of work.
You (the Hibernate architect) need to store the org.hibernate.SessionFactory somewhere for easy access in application code.
Example: SessionFactory utility
package org.hibernate.tutorial.util;
import org.hibernate.SessionFactory;
public class HibernateUtil {
private static SessionFactory buildSessionFactory() {
public static SessionFactory getSessionFactory() {
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class HibernateUtil {
private static final SessionFactory sessionFactory = buildSessionFactory();
private static SessionFactory buildSessionFactory() {
try {
// Create the SessionFactory from hibernate.cfg.xml
return new Configuration().configure().buildSessionFactory();
}
catch (Throwable ex) {
// Make sure you log the exception, as it might be swallowed
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}
We create a HibernateUtil helper class that takes care of startup and makes accessing the org.hibernate.SessionFactory more convenient. This class not only produces the global org.hibernate.SessionFactory reference in its static initializer; it also hides the fact that it uses a static Singleton.
In an application server we can use instead JNDI lookup for convenience.
If you give the org.hibernate.SessionFactory a name in your configuration, Hibernate will try to bind it to JNDI under that name after it has been built.
Another, better option is to use a JMX deployment and let the JMX-capable container instantiate and bind a HibernateService to JNDI. Such advanced options are discussed later.
3.5 Optionally Logging Configuration
You need to configure a logging system. Hibernate uses commons logging and provides two choices:
Most developers prefer Log4j: From the Hibernate distribution you can copy the "log4j.properties" file from the "etc/" directory to your "src" directory, next to "hibernate.cfg.xml".
4. Loading and storing objects
Lets create a startup point for our application - a class with a main() method - the EventManager class :
Lets examine the class above:
package org.hibernate.tutorial;
private void createAndStoreEvent(String title, Date theDate) {
import org.hibernate.Session;
import java.util.*;
import org.hibernate.tutorial.domain.Event;
import org.hibernate.tutorial.util.HibernateUtil;
public class EventManager {
public static void main(String[] args) {
EventManager mgr = new EventManager();
if (args[0].equals("store")) {
mgr.createAndStoreEvent("My Event", new Date());
}
HibernateUtil.getSessionFactory().close();
}
private void createAndStoreEvent(String title, Date theDate) {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
Event theEvent = new Event();
theEvent.setTitle(title);
theEvent.setDate(theDate);
session.save(theEvent);
session.getTransaction().commit();
}
}
Lets examine the class above:
- We define a "main" entry point for the class:
public static void main(String[] args) {
EventManager mgr = new EventManager();
if (args[0].equals("store")) {
mgr.createAndStoreEvent("My Event", new Date());
}
HibernateUtil.getSessionFactory().close();
}
The main method receives an argument of the requested operation (args[0])
- We define the "createAndStoreEvent()" method. It does the following:
- Get a sessin for the current operation
- Begin a transaction for the current operation
- Create a new
Event
object - Persist it on the session object, i.e. hand it over to Hibernate. At that point, Hibernate takes care of the SQL and executes an
INSERT
on the database. - Commit the transaction
Primary-Key / Unique-Identifier Generation Strategy
All persistent entity classes need to hold a unique identifier value. This identifier is designated a generation strategy, either a default one, or specific one defined by the Hibernate architect. There mainly two types of generation strategy:- Without generation strategy (Default) - The id is assigned manually by the programmer
- With generation strategy - The id is created automatically by the Hibernate infrastructure.
Hibernate supports several generation strategies. It supports database generated, globally unique, as well as application assigned, identifiers. Identifier value generation is also one of Hibernate's many extension points and you can plugin in your own strategy.
The idea behind these generators is to port the actual semantics of the identifer value generation to the different databases. For example, the org.hibernate.id.enhanced.SequenceStyleGenerator mimics the behavior of a sequence on databases which do not support sequences by using a table.