Monday 16 May 2011

Hibernate ORM


Hibernate is a powerful, high performance object/relational persistence and query service. Hibernate lets you develop persistent classes following object-oriented idiom – including association, inheritance, polymorphism, composition, and collections. Hibernate allows you to express queries in its own portable SQL extension (HQL), as well as in native SQL, or with an object-oriented Criteria and Example API.
For this tutorial we will be using the Student Registration example application. This applications main function is to add, update, and delete students from the database. Additional functionality includes being able to register courses to a student, this will help to show how Hibernate treats relationships between classes in the database.
This tutorial gives a basic idea on how to connect and use Hibernate with a database. Hibernate has many other functions and uses that go beyond this tutorial.

Steps to Create a Hibernate Application

The following steps will guide you on how to create a Hibernate application. We will be using the Student Register application to demonstrate the major steps. Before you begin you should or will have to download the latest Hibernate release from www.hibernate.org and include the jar files in the /lib directory of the download to your projects /lib directory.

Step 1: Create the Java Objects

The great feature about using Hibernate is that your java objects do not need any special treatment to become persistent. Below is and example of the code used for the Student object in the Student Register application.
package com.icesoft.icefaces.tutorial.crud.hibernate;

            import java.util.HashSet;
            import java.util.Set;

            /**
             * Represents a student object in the hibernate tutorial
             * Student Register example.
             */
            public class Student {
                // unique student id
                private int studentId;
                // first name of the student
                private String firstName;
                // last name of the student
                private String lastName;
                // address of the student
                private String address;
                // set of courses that the student is related/registered for
                private Set courses = new HashSet();

                public Student() {
                }

                /**
                 * Creates a new instance of Student.
                 * @param firstName first name.
                 * @param lastName last name.
                 * @param address address.
                 */
                public Student(String firstName, String lastName, String address){
                    this.firstName = firstName;
                    this.lastName = lastName;
                    this.address = address;
                }

                public int getStudentId(){
                    return studentId;
                }

                public void setStudentId(int studentId){
                    this.studentId = studentId;
                }

                public String getFirstName(){
                    return firstName;
                }

                public void setFirstName(String firstName){
                    this.firstName = firstName;
                }

                public String getLastName(){
                    return lastName;
                }

                public void setLastName(String lastName){
                    this.lastName = lastName;
                }

                public String getAddress(){
                    return address;
                }

                public void setAddress(String address){
                    this.address = address;
                }

                public Set getCourses(){
                    return courses;
                }

                public void setCourses(Set courses){
                    this.courses = courses;
                }

                public String clear(){
                    firstName="";
                    lastName="";
                    address="";
                    return "clear";
                }
            }
As you can see it is pretty straight forward.

Step 2: Create the Mapping Files for the Java Objects

For an object to be persisted to a database, Hibernate needs a mapping file for all of the objects that are to be persisted. This XML file lets Hibernate know how to load and store objects, what table in the database it has to access, and what columns in the table it should use. The following is an example mapping file from the Student Register application.
<?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="com.icesoft.icefaces.tutorial.crud.hibernate.Student" table="students">
                    <id name="studentId" column="student_id">
                        <generator/>
                    </id>

                    <property name="firstName" column="first_name"/>
                    <property name="lastName" column="last_name"/>
                    <property name="address"/>
                    <set name="courses" table="student_courses">
                        <key column="student_id"/>
                        <many-to-many column="course_id"/>
                    </set>
                </class>

            </hibernate-mapping>
The file must be saved with the hbm.xml extension. This lets Hibernate know that this is a mapping file. The file location must be in the same directory as the java file it is related to. Also be sure to add the Hibernate DTD at the top of the page for all mapping files.
Inside the hibernate-mapping tags is where all the mapping information will be included. The first element to be added is the class element. This will map a persistent class to a table in the database. With the class element you must define the name and table attributes. The name attribute will let Hibernate know the name of the Java class that you want to be persisted. The table attribute specifies the name of the table to be used in the database when persisting the Java object for this mapping.
Inside the class element is where you define the attributes of your Java object to columns in the database. The following is a description of the commonly used ones and their attributes:
    • <id> – When your mapping a Java object to a database an ID is required. This helps to eliminate any duplicate entries.
      • name – Specifies the name of the id.
      • column – The name of the database column for the id.
    • <generator> – This element is actually a sub-element of the idelement. It is used to generate a unique id every time an object is persisted to the database. It also specifies how the id element will be created by using the following built in generators:
      • increment – Used most often, each time the generator needs a new id it performs a select on the database to determine the current largest id and increments it to the next value.
      • native – Picks the best strategy depending on the configured database.
      • assigned – Used if you need to assign the id yourself in your application. Set the id by using the set<identifier> method of the Java class. The application assumes responsibility on keeping the id unique.
    • <property> – For every attribute in your Java class that you want persisted you need to define one of these tags for each one. The following are some commonly used attributes for theproperty element:
      • name – Specifies the name of the property. The first character must be lowercase.
      • column – Specifies the name of the column in the database that the attribute will be saved.
    • <set> – The most common collection mapping used in Hibernate.
      • name – Specifies the name of the set used in the Java class.
      • table – Specifies the name of the table to be used for the collection. In our example we have a many-to-many relationship with Student and Course. Therefore an association table needs to be used.
    • <key> – Sub-element of set. Used to indicate the property of the class.
column
      - Specifies the foreign key column.
  • <many-to-many> – Sub-element of set. Used to define the other class in the many-to-many relationship.
    • column – Specifies the column name of the other class.
    • class – Specifies the path of the other class.
There are of course many more elements and attributes that can be used. A few of them will be explain in brief detail later in the tutorial.

Step 3: Create the Hibernate Configuration File

Once the mapping files for the persistent objects have been completed, its now time to configure Hibernate. This will have all the information to connect Hibernate to a database. First we will need a database. For our example we used MySQL but there are many other databases that will work with Hibernate including:
  • MySQL
  • HSQL DB
  • Oracle
  • MS SQL Server
Consult with the Hibernate website to find an up-to-date listing.
To configure Hibernate there are a few ways that it can be done, you can use a simple hibernate.properties file, a more sophisticated hibernate.cfg.xml file, or a complete programmatic setup. For our application we will use the XML configuration file.
<!DOCTYPE hibernate-configuration PUBLIC
          "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
          "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

            <hibernate-configuration>

                <session-factory>

                    <!-- Database connection settings -->
                    <property name="connection.driver_class">org.gjt.mm.mysql.Driver</property>
                    <property name="connection.url">jdbc:mysql://localhost/register</property>
                    <property name="connection.username">test</property>
                    <property name="connection.password">test</property>

                    <!-- SQL dialect -->
                    <property name="dialect">org.hibernate.dialect.MySQLInnoDBDialect</property>

                    <!-- Echo all executed SQL to stdout -->
                    <property name="show_sql">true</property>

                    <property name="current_session_context_class">thread</property>

                    <mapping resource="com/icesoft/icefaces/tutorial/crud/hibernate/Course.hbm.xml"/>
                    <mapping resource="com/icesoft/icefaces/tutorial/crud/hibernate/Student.hbm.xml"/>

                </session-factory>

            </hibernate-configuration>
Note that this configuration uses a different DTD than the mapping files. All of the properties are inside the session-factory tags. The first four property elements contain the configuration for the MySQL connection. The dialect property element specifies the particular SQL variant Hibernate generates. The show_sql property element will, when set to true, output all the SQL commands to the console window. At the end is where the mapping files are included to the configuration.
The location of the file must be at the source directories root, Hibernate automatically looks for a file called hibernate.cfg.xml in the root of the class path on startup.

Step 4: Create the HibernateUtil Helper Class

Now a helper class is needed to get Hibernate up and running. This class creates a SessionFactory object which in turn can open up new Session’s. A session is a single-threaded unit of work, the SessionFactory is a thread-safe global object instantiated once. For our application the HibernateUtil class is implemented below:
package com.icesoft.icefaces.tutorial.crud.hibernate.util;

          import org.hibernate.SessionFactory;
          import org.hibernate.cfg.Configuration;

          public class HibernateUtil {

                private static final SessionFactory sessionFactory;

                static {
                    try {
                        // Create the SessionFactory from hibernate.cfg.xml
                        sessionFactory = 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;
                }

          }
Place HibernateUtil.java in a util package next to your main class package(s).

Step 5: Create a Class to use Persistence Objects

Now that the mapping and configuration files have been written its time to use the persistent objects in our application. Using Hibernate you can add, edit, delete objects to and from a database. In the Student Register example we use one class that facilitates all the functionality called RegisterManager.java. The following code portion from the RegisterManager class shows how we add a student to the database:
public void addStudent(ActionEvent event){
                Session session = HibernateUtil.getSessionFactory().getCurrentSession();
                session.beginTransaction();

                Student s = newStudent;

                session.save(s);

                session.getTransaction().commit();

                s.clear();
                init();
         }
First off we create a new Session instance which is returned from our helper class HibernateUtil. By getting it from the helper class we can be sure that only one thread is running at one time. You can, if your application deems it needed, create one or more Sessions with each one representing a single unit of work. The Hibernate Session object has many different methods that can be used to manipulate data to and from a database. In our addStudent() method we use the session.save(s) method. This actually creates an SQL command to insert the new student object into the database. After the object is saved the Session is then committed and closed.
How do read values from the database? Below is an example from the Student Register application:
private synchronized void init(){
                ...
                Session session = HibernateUtil.getSessionFactory().getCurrentSession();
                session.beginTransaction();

                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(""));
                ...
         }
The init() method creates a new session just like the addStudent() method, the only difference is that it uses the createQuery() method. This method is used to send SQL/HQL commands to the database. It then returns a list of the information that it found in the database leaving it open to be used how you please.
So far we have touched on how to use Session’s methods to automatically add an object to the database without needing to write any sql commands. Also how to write your own SQL command to access the database. Another subject that we will touch on is using a named parameter. In our example application we use this a few times. The following code is from the RegisterManager class’s deleteStudent() method:
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();
                }
          }
The method gets a Session like before but we now want to delete the student that has the value of id that was set before we created the Session object. To include a named parameter it must start with the colon character (:). As you can see in the above code we create a Query object to execute the SQL command. With the Query we set the named parameter so that Hibernate can use it.
Note that we call the executeUpdate() method to execute an update or delete statement. It also returns the row count of affected rows, which can help in debugging.

Other Mapping Elements

The following is a short listing of some other Hibernate mapping elements and a brief description of what they do:
  • <composite-id> – Used with a table with a composite key id.
  • <version> – Used on tables that contain versioned data.
  • <timestamp> – Used on tables that contain timestamped data.
  • <join> – Makes it possible to map properties of one class to several tables.
These are only a small portion of the many mapping elements available.

Class Associations In Hibernate

In our Student Register example the Student holds a set of Course objects. These Course objects are the courses that the student has registered for. Also each Course object also holds a set of Student objects which is made up of the Students that are registered in each course. This relationship is called a many-to-many relationship.
In a many-to-many relationship an association table is needed to hold the values for the relationship. This table is mapped in the Student and Course mapping files with the following code:
Student.hbm.xml
...
          <set name="courses" table="student_courses">
             <key column="student_id"/>
             <many-to-many column="course_id"/>
          </set>
          ...
Course.hbm.xml
...
          <set name="students" table="student_courses"  inverse="true">
             <key column="course_id"/>
             <many-to-many column="student_id"/>
          </set>
          ...
Once the mapping of the association table has been completed, we will now need to use these values. In our example we can add and remove courses assigned to a student. This data is saved in the association table under the students id and the course id. The following is the code used to add and remove a course from a student:
...
          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);
                setStudentCourses();
                session.getTransaction().commit();
         }
         ...
         public void removeCourseFromStudent(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().remove(c);
                setStudentCourses();
                session.getTransaction().commit();
        }
        ...
As you can see we start the methods like our other ones. The only major difference is we load the current Student and Course objects. Once they are loaded we then get the set of courses and now can use its add() or remove() methods. Hibernate automatically detects that the collection has been modified and needs to be updated, meaning we do not have to call an update() or save() method. This is called automatic dirty checking. The only catch is that the objects need to be loaded in the current session.