Creating Your First Hibernate-Enabled Application

Table of Contents

Creating Your First Hibernate-Enabled Application

  Name Size Creator (Last Modifier) Creation Date Last Mod Date Comment  
ZIP Archive hibernate-tutorial.zip 6.30 MB Nils Lundquist Apr 01, 2011 Apr 01, 2011  

Hibernate is the de-facto Java object/relational persistence and query service framework, and the most significant influence on the JPA standard. In other words, Hibernate is a mature, powerful library that lets you efficiently create and manage a DB backend to your application via familiar features of OO languages like: composition, inheritance, polymorphism, collections, etc. Queries can be written in its own SQL extension (HQL), SQL, or an object-oriented Criteria/Example API. This tutorial is meant to teach the fundamental steps to configuration and interfacing with Hibernate/JPA data entities.

For this tutorial samples will be drawn from the supplied "Student Registration" demo application. The applications main function is to add, update, and delete dummy objects (students) from the database. Also, as an example of object relationships, the app includes an association between the students and some course objects.

The purpose of tutorial is to give a basic idea on how to connect and use Hibernate with a simple set of entities. Our sample defines its entities via vanilla JPA annotations in familiar POJOs, this allows easy porting among JPA implementations; although there're times a Hibernate-specific annotation or option may be more suited. Hibernate configurations in the real world often require more complex mappings and features that go beyond this tutorial. To learn more about advanced features, a good resource is the Hibernate user's guide.

Database Connection & Creation Defaults
  • This application is supplied with a configuration to use a HSQL embedded database, therefore the hsqldb.jar file must be present. If you wish to use a different DB, the schema and sample data is created by Hibernate, so it's only necessary to change the connection settings in hibernate.cfg.xml.
  • Notice the schema is generated and new dummy data added during the first connection to your application SessionFactory. That DB is than dropped when the SessionFactory associated with it is closed. This means in the supplied configuration, a server restart or a full application redeploy will reset the database state.

Basics to Creating a Hibernate Application

These instructions include the creation & access of POJOs that Hibernate can map to the database, and configuring a basic Hibernate connection. Snippets are drawn from the "Student Registration" application attached to this article. To prepare the project you should have the latest Hibernate release from www.hibernate.org and include the JAR files from the "/required" and "/jpa" directories of the release. Also hsqldb.jar should be included if you wish to use the embedded test database mentioned above.


Part 1: Create the Java Data Objects

A keystone feature of using a Object/Relational Mapping framework is that DB entities are progmatically available as POJOs.

Data Object Example:

Course.java
public class Course {
	private long courseId;
	private String courseName;
	private String description;

	private Set<Student> students = new HashSet<Student>();

	public Course() {}
	public Course(int courseId, String courseName, String description) ...

	public long getCourseId() {return courseId;}
	public void setCourseId(int courseId) {this.courseId = courseId;}
	public String getCourseName() {return courseName;}
	public void setCourseName(String courseName) {this.courseName = courseName;}
	public String getDescription() {return description;}          .
	public void setDescription(String description) {this.description = description;}
	public Set getStudents() {return students;}
	public void setStudents(Set students) {this.students = students;}

	public String clear() {...}
}

As you can see, it's a pretty straight forward POJO, aside from having a field for a numeric ID. In the next step, extra information is applied to data classes like Course, concerning how their instances will be mapped to entries in the database.

Part 2: Define Relational Mappings via Annotations on the Java Objects

Hibernate needs information about the fields that make up a class if it will be able to map it to tables(s) in the database. Ex: how will Hibernate store, cache & identify the classes, define database relationships with other mapped objects, and how those mappings in the db are named, are some of the most basic options and all need to be added via some metadata. For this Hibernate supports XML, property files and class annotations. The sample uses annotations as they`re default of the JPA2 standard and are lightweight.

Annotated Data Object Example:

Course.java
@Entity
@Table(name = "course")
public class Course {
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column(name="course_id")
	private long courseId;
	...
	@Column(name="course_name")
	private String courseName;
	...
	@ManyToMany
	@JoinTable(name="student_course", //New table name
		joinColumns=@JoinColumn //My column in the join table
		(name="course_id",referencedColumnName="course_id"),
        	inverseJoinColumns=@JoinColumn //Associated column in the join table
		(name="student_id", referencedColumnName="student_id")
	)
	private Set<Student> students = new HashSet<Student>();
	...

	//By default, constructors and getters/setters are not persisted.
   	public Course(int courseId, String courseName, String description){...}
	public long getCourseId() {return courseId;}
	public void setCourseId(int courseId) {this.courseId = courseId;}
	//...like this method
	@Transient
	public String clear() {
		courseId = 0;
		courseName = "";
		description = "";
		return "clear";
	}
}
@Entity

Entity indicates this data class should have persistent instances in the database (i.e mapped to records in a DB table). Entity is followed by Table, used to set attributes of the table being mapped; its presence is optional. Above we use it specify the name of the table. Take note that name isn't used in HQL queries, column names are only used in configuration.

@Id

Id is often used alongside GeneratedValue. GV provides a sequence of unique values to be set by Id as the Primary Key of this table. (i.e - A field guaranteed to be unique among the persisted records of this class.)The PK is a fundamental of database theory and is how the DB differentiates two class instances with the same mapped field values. Although most often DB schema use of numeric IDs, JPA has supports identifying records by a unique non-numeric column or a subset of columns.

@Column

Column annotation, similar to Table, is optional and exists to supply configuration attributes for the field it is attached to. Any un-annotated fields are persisted as if it they'd been marked with the defaults of the Basic and Column annotations. The options implied by Basic are configurable.

@ManyToMany

ManyToMany is the most complicated of the annotations demonstrated here along with the JoinTable/JoinColumn used. ManyToMany defines relationships where a class field can have references to any number other class instances, and those class instances, in inverse, are referenced by any number of instances of the original class. To define this relationship, above, we are supplying the name of the table that represents this association, the names of the identifying columns as they will be recorded in the association table, and the names of the identifying columns as they appear in the entity tables we're associating. The other side of the ManyToMany doesn't require a JoinTable, at Student for details.

@Transient

Transient is is used to denote class members that should not be part of the database record.

There are of course many optional attributes and annotations that we're avoiding. The annotations we've covered here are only a fraction of the standard relationships along with a minimum of config for naming.

Part 3: Hibernate Configuration File

The Hibernate config that's added in this part sets up the properties of the connection to the database and the location of our mappings. Other global Hibernate configuration is often done in here, but we just need a connection to our database. Our example used HSQL but there are many databases that will work with Hibernate. (Note: Derby support appears spotty between versions at best.)

There are many ways to set a Hibernate configuration: a hibernate.properties file, a more structured hibernate.cfg.xml file, a persistence.xml file, a framework like Spring, or a complete programmatic setup. For our application we used the uncomplicated hibernate.cfg.xml approach.

hibernate.cfg.xml
 
<?xml version='1.0' encoding='utf-8'?>
<!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:file:HibernateTutorialDB</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">create</property>
       
        <mapping class="com.icesoft.icefaces.tutorial.crud.hibernate.Student"/>       
        <mapping class="com.icesoft.icefaces.tutorial.crud.hibernate.Course"/>
    </session-factory>
</hibernate-configuration>

The session-factory tag denotes a configuration for the Hibernate SessionFactory java object. The first four elements define the DB connection. The dialect property element specifies the particular SQL variant Hibernate generates. The show_sql property outputs all the mapped SQL commands (apart from table generation) to the console window. We also remove caching and restrict the pool size for this demo. The last two tags identify the mapping files of entities in this database.

The hibernate.cfg.xml file must be located at the source directory root.

Step 4: HibernateUtil Helper Class

HibernateUtil is an often used helper class that's used to distribute single-threaded Session objects to concurrent threads of an application. It's a static object to hold the SessionFactory configured by the xml of the previous step, to generate those Session to give out.

HibernateUtil.java
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
        	SessionFactory f = new Configuration().configure().buildSessionFactory();
            return f;
        }
        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;
    }

}     

Step 5: Create an Access Class to Manage Data Objects

Hibernate can finally start using that database! The sample application does the legwork of accessing data objects via a class, RegisterManager.java. Provided an application was complex enough, you might decide to write a separate access class to modularize persistence functions for each entity.

Adding an Object Record to DB

RegisterManager.java
          public void addStudent(ActionEvent event){
                Session session = HibernateUtil.getSessionFactory().getCurrentSession();
                session.beginTransaction();
        
                Student s = newStudent("John","Smith","123 Fake St.");
        
                session.save(s);
        
                session.getTransaction().commit();
        
                s.clear();
                init();
         }

First a new Session is created via HibernateUtil. Using HibernateUtil to provide the SessionFactory ensures that only one Session executes on our data objects at time. If an application required, you could create more Sessions, each one representing a single sequence of SQL work. A Session SQL sequence is an update to the DB mapped tables with the changes, additions and deletions of data objects (saved/"persisted" via executeUpdate(), save(), etc.) that take place between its beginTransaction() and commit() methods. However, until commit() is run successfully no change actually takes place in the database.

Session provides several methods to manipulate the DB, in the sample app two are used. Above, addStudent() uses session.save(). This generates for Session an SQL command to insert that data entity instance into the database. Below, the alternative method is used.

Reading Data Objects From DB Records

RegisterManager.java
          private synchronized void init(){
                ...
                Session session = HibernateUtil.getSessionFactory().getCurrentSession();
                session.beginTransaction();
        	
                //Load all objects from tables
                List studentResult = session.createQuery("select s.studentId from " +
                        "Student as s").list();
                List courseResult = session.createQuery("select c.courseName from " +
                        "Course as c").list();        
                session.getTransaction().commit();
                studentItems.add(new SelectItem(""));
                ...
         }

init() creates a new session just like the addStudent() method, difference being that it uses the query-language oriented createQuery() method over an object-model oriented method like save(). CreateQuery() can work with SQL or HQL, and returns a list of data objects; the result of entity records that it found in the database.

Session's been used to add an object to the database without needing to write any SQL, as well as writing an SQL statement to load objects. Below the use of named parameters in query language is shown.

Deleting a Specified Object Record

RegisterManager.java
          public void deleteStudent(ActionEvent event){
                if(currentStudent != null){
                    int id = currentStudent.getStudentId();
                    Session session = HibernateUtil.getSessionFactory()
                                                    .getCurrentSession();
                    session.beginTransaction();
                    Query q = session.createQuery("delete from Student as s where " +
                            "s.studentId =:id");
                    q.setInteger("id",id);
                    int rowCount = q.executeUpdate();
                    session.getTransaction().commit();
                    System.out.println("Rows affected: " + rowCount);
                    currentStudent.clear();
                    studentItems.clear();
                    init();
                }
          }

Everything above is similar to before, although now the objective is deleting a student of a particular ID. To avoid building complex string compositions, or to allow use of pre-prepared queries, queries allow using parameters in variable positions. To denote a named parameter in a query language statement, prefix the name with a colon. Above, a Query object's been created to define the SQL and the parameter is supplied before executing. The use of a parameter above could be easily avoided, but for an example of where compositing a complex query is eased by parameters, examine the Student update function of RegisterManager.

Although the same object-model and query-language data access methods above can be applied to perform DB record updates; apart from that there's still a feature of the relational data object design not yet covered. The ManyToMany association we added between Course and Student needs the ability to add new reference pairs.

Adding an Association Between Records

RegisterManager.java
          public void addCourseToStudent(ActionEvent event){
                Session session = HibernateUtil.getSessionFactory().
                                                    getCurrentSession();
                session.beginTransaction();

                Student s = (Student)session.load(Student.class, 
                                            currentStudent.getStudentId());
                Course c = (Course)session.load(Course.class, 
                                            currentCourse.getCourseId());

                s.getCourses().add(c); // or .remove for delete
                session.getTransaction().commit();
         }

Most of above is familiar, although there is a difference in the origin of the data objects. They're loaded within the transaction, as opposed to working with our in-memory lists of Student and Course objects, only doing a save of the changes we just made. Referred to as automatic dirty checking, making updates to objects that are looked up by transaction is less efficient, but are easier to write as it doesn't require local instances or explicit info about how the updates should be made.


  • If you've successfully followed this tutorial till now, you should know the basics of:
    • creating a Hibernate configuration
    • creating data objects
    • session & transaction usage
    • manipulating the DB via the Hibernate Session object
  Name Size Creator (Last Modifier) Creation Date Last Mod Date Comment  
ZIP Archive hibernate-tutorial.zip 6.30 MB Nils Lundquist Apr 01, 2011 Apr 01, 2011  

Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.

© Copyright 2021 ICEsoft Technologies Canada Corp.