Hibernate 3.5 without JPA and Annotations: The Good Old Hibernate Mapping File
This tutorial takes a look at how we can map entities and properties to database tables and columns using Hibernate 3.5 and the traditional mapping file instead of JPA 2.0 annotations.
Hibernate 3.5 without JPA: The Hibernate Mapping File
We've been running a few tutorials that demonstrate how to work with the latest edition of Hibernate, version 3.5, and the JPA 2.0 annotations that Hibernate 3.5 implements.
Hibernate Made Easy WebSite
Video Tutorials on Learning Hibernate
Recommended Books for Learning Hibernate
Check out this updated Tutorial for Spring 3
The first tutorial simply explained how to configure and test an extremely simple Hibernate and Java development environment.
The second tutorial used the Hibernate API along with a JPA annotated class named the GameSummary to perform POJO based data persistence.
The third tutorial performed the same basic CRUD operations with the GameSummary class, but it used the Java Persistence API exclusively, without any reference to the Hibernate framework, although the Hibernate framework remained the JPA implementation under the covers.
But what about not using JPA at all? I mean, people have been Hibernating for years without any JPA at all. Just for sake of posterity, I wanted to demonstrate how to use the Hibernate API without any reference to the Java Persistence API framework whatsoever. We'll start off by revisiting the JPA annotated GameSummary class as it appeared in the second tutorial. We'll add a few properties to it, and then we'll see how we could eliminate our dependency on JPA, and just use a plain old Hibernate mapping file instead. Well, here it goes:
Updating the GameSummary Class: Adding Properties
The first iteration of our GameSummary class only contained two properties, an id of type Long, and a result of type String. However, our GameSummary is intended to represent a full summary of a Rock-Paper-Scissors game, and that means adding in a few more properties, namely two String objects representing what the client and server has chosen, and also a java.util.Date object that keeps track of when the game was played. So, here is our GameSummary class with the additional properties, along with the requisite setters and getters:
package com.mcnz.model;
import javax.persistence.*;
import org.hibernate.cfg.*;
import org.hibernate.tool.hbm2ddl.*;
@Entity
public class GameSummary {
@Id
@GeneratedValue
private Long id;
private String result, clientGesture, serverGesture;
private java.util.Date date;
public static void main(String args[]) {
AnnotationConfiguration config = new AnnotationConfiguration();
config.addAnnotatedClass(GameSummary.class);
config.configure();
new SchemaExport(config).create(true, true);
}
public String getClientGesture() { return clientGesture;}
public void setClientGesture(String clientGesture) {
this.clientGesture = clientGesture;
}
public String getServerGesture() {return serverGesture;}
public void setServerGesture(String serverGesture) {
this.serverGesture = serverGesture;
}
public java.util.Date getDate() {return date;}
public void setDate(java.util.Date date) {this.date = date;}
public Long getId() { return id;}
public void setId(Long id) {this.id = id;}
public String getResult() {return result;}
public void setResult(String result) {
this.result = result;
}
public String toString() {
return "id: " + id + " result: " + result ;
}
}
Running the Stand Alone Hibernate Application
The main method in this example passes the Hibernate configuration into the constructor of the SchemaExport, which will force the database to be recreated with columns for the new Date property, and the two new String properties. Here's the SQL output that gets generated from running the code above:
drop table if exists GameSummary
create table GameSummary (id bigint not null auto_increment, clientGesture varchar(255), date datetime, result varchar(255), serverGesture varchar(255), primary key (id))
And of course, if you inspected the database, you'd notice that three new columns had been added to the gamesummary table. It's also worth noting that the instance of the java.util.Date class was mapped to a DATETIME column.
Persisting Data to the Updated gamesummary Table
With the gamesummary table updated with the appropriate columns, adding a record with each of these fields populated is as easy as initializing in instance of the GameSummary class, and passing it to the save method of the Hibernate Session within the confines of an active Session.
public static void main(String args[]) {
AnnotationConfiguration config = new AnnotationConfiguration();
config.addAnnotatedClass(GameSummary.class);
config.configure();
//new SchemaExport(config).create(true, true);
GameSummary gs = new GameSummary();
gs.setClientGesture("rock");
gs.setServerGesture("paper");
gs.setResult("win");
gs.setDate(new java.util.Date());
SessionFactory sessionFactory = config.buildSessionFactory();
Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
session.save(gs);
session.getTransaction().commit();
}
Running this main method generates the following SQL statement to be written to the application's console window:
Hibernate: insert into GameSummary (clientGesture, date, result, serverGesture) values (?, ?, ?, ?)
As you can see, a new record, representing this instance of the GameSummary class is persisted to the database:
Hibernate 3.5 and hbm Mapping Files
While JPA annotations are all the rage, it should be noted that hbm mapping files are completely valid when working with Hibernate 3.5. So, rather than decorating our POJOs with JPA annotations, we could describe our POJO to database mappings in an XML file.
The following file, named GameSummary.hbm.xml, which needs to be saved in the same folder as the GameSummary.java file, effectively describes to the Hibernate framework how the POJO should be mapped to its corresponding gamesummary table:
<?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="com.mcnz.model">
<class name="GameSummary" table="gamesummary">
<id name="id" column="id" type="long" >
<generator class="native"/>
</id>
<property name="result" type="string" />
<property name="clientGesture" type="string" />
<property name="serverGesture" type="string" />
<property name="date" type="date" />
</class>
</hibernate-mapping>
Updating the hibernate.cfg.xml File
And once the mapping file is created, an entry must be made in the hibernate.cfg.xml, letting the Hibernate framework know about this mapping file. By the way, at this point, you could strip off the JPA annotations from the POJO, although leaving them on won't cause a problem either. Here's the entry that needs to be made to the hibernate.cfg.xml file.
<mapping resource="com/mcnz/model/GameSummary.hbm.xml"/>
And here's the entire hibernate.cfg.xml file with the mapping resource entry added:
<?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="connection.url">
jdbc:mysql://localhost/rps
</property>
<property name="connection.username">
root
</property>
<property name="connection.password">
password
</property>
<property name="connection.driver_class">
com.mysql.jdbc.Driver
</property>
<property name="dialect">
org.hibernate.dialect.MySQLDialect
</property>
<property name="current_session_context_class">
thread
</property>
<!-- this will show us all sql statements -->
<property name="hibernate.show_sql">
true
</property>
<mapping resource="com/mcnz/model/GameSummary.hbm.xml"/>
</session-factory>
</hibernate-configuration>
Using the Hibernate Configuration Object and Mapping Files
Once the mapping file has been created, and the hibernate.cfg.xml file has been updated with a reference to the mapping, you can begin using the Hibernate framework to persist the state of your POJO. Here's the code that is used to leverage mapping files, as opposed the code we have seen before that uses JPA annotated POJOs.
package com.mcnz.model;
import org.hibernate.*;
import org.hibernate.cfg.Configuration;
public class HbmRunner {
public static void main(String args[]){
Configuration config = new Configuration();
config.configure();
SessionFactory sessionFactory = config.buildSessionFactory();
GameSummary gs = new GameSummary();
gs.setResult("tie");
Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
session.save(gs);
session.getTransaction().commit();
}
}
This sample code demonstrates how the Hibernate Session is obtained from a Configuration object that leverages Hibernate mapping files. Once the Hibernate Session is obtained, the code for performing any Hibernate related operation will be exactly the same, regardless of whether you are using annotations or Hibernate mapping files.
The Entire, Monolithic HbmRunner
The tutorials on JPA and Hibernate's AnnotationConfiguration object all finished off with a great big Java class that did saves, deletes, updates and a couple of queries. As you can see, the difference in code between using the Hibernate AnnotationConfiguration object, and the the Configuration object that consumes hbm files is pretty minimal. However, just to make everything consistent, here's that monolithic Java class, written to use the basic Hibernate Configuration object.
package com.mcnz.model;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class HbmRunner {
public static void main(String args[]){
Configuration config = new Configuration();
config.configure();
SessionFactory sessionFactory = config.buildSessionFactory();
Long entityId = null;
{
GameSummary gameSummary = new GameSummary();
gameSummary.setResult("win");
System.out.println(gameSummary);
HibernateCrudRunner.save(sessionFactory, gameSummary);
entityId = gameSummary.getId();
System.out.println(gameSummary);
}
GameSummary gameSummary
= HibernateCrudRunner.findByPrimaryKey(sessionFactory, entityId);
gameSummary.setResult("tie");
HibernateCrudRunner.update(sessionFactory, gameSummary);
HibernateCrudRunner.findAll(sessionFactory);
HibernateCrudRunner.delete(sessionFactory, gameSummary);
HibernateCrudRunner.findAll(sessionFactory);
}
public static void save(SessionFactory sessionFactory, GameSummary gs) {
Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
session.save(gs);
session.getTransaction().commit();
}
public static void update(SessionFactory sessionFactory, GameSummary gs) {
Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
session.saveOrUpdate(gs);
session.getTransaction().commit();
}
public static java.util.List findAll(SessionFactory sessionFactory) {
Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
Query queryResult = session.createQuery("from GameSummary");
java.util.List allSummaries = queryResult.list();
for (int i=0;i<allSummaries.size();i++) {
GameSummary gs = (GameSummary) allSummaries.get(i);
System.out.println(gs.toString());
}
session.getTransaction().commit();
return allSummaries;
}
public static GameSummary findByPrimaryKey(SessionFactory sessionFactory, Long id) {
Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
GameSummary gs = (GameSummary) session.get(GameSummary.class, id);
session.getTransaction().commit();
return gs;
}
public static void delete(SessionFactory sessionFactory, GameSummary gs) {
Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
session.delete(gs);
session.getTransaction().commit();
}
}
Hibernate Made Easy WebSite
Video Tutorials on Learning Hibernate
Recommended Books for Learning Hibernate
Check out this updated Tutorial for Spring 3