Articles

Hibernate Tutorial

In Hibernate on 23/02/2011 by pier0w

What is Hibernate? Hibernate is an Object-relational Mapper or ORM. What this means is that hibernate will map database tables into Java objects.

So for example if we have the table “user” in our ASCIISQL database like so:

 --------------------------------------------
| id | first_name | last_name | age | gender |
| 1  | Some       | One       | 25  | MALE   |
 --------------------------------------------

Then hibernate could automatically map the values from that table into the following class:

package org.project.hibernate.domain.enums;

public enum GENDER {
    FEMALE,
    MALE
}

package org.project.hibernate.domain;

import org.project.hibernate.domain.enums.GENDER;

public class User {

    private Long id;
    private String firstName;
    private String lastName;
    private Long age;
    private GENDER gender;

    public User() {
    }

    public User(String firstName, String lastName, Long age, GENDER gender) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
        this.gender = gender;
    }

    public User(Long id, String firstName, String lastName, Long age, GENDER gender) {
        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
        this.gender = gender;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    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 Long getAge() {
        return age;
    }

    public void setAge(Long age) {
        this.age = age;
    }

    public GENDER getGender() {
        return gender;
    }

    public void setGender(GENDER gender) {
        this.gender = gender;
    }
}

This means that you can now try to ignore that fact that all your data is kept in a relational database. I mean TRY, because in reality that is something you will never succeed at.

But hibernate is still very helpful when it is working correctly.

So lets begin our first very simple hibernate project. So the first thing to do is create a project and then get git to track your changes.

Project naming suggestions:
groupId: org.project.hibernate
artifactId: hibernate
version: 1.0
package: hibernate

Once that is complete open the project in Intelij IDEA and create a package called “domain” under the “hibernate” package. This is where we will put any of the Java objects that are going to be mapped to their equivalent tables, these will be Domain Objects. Domain Objects are there to provide a representation of our applications data that is not tied to the way the data is stored. So basically if all the data just ends up in domain objects it can easily be used within logic code without the need to care how it got there. For this tutorial Hibernate is going to be what got it there.

Now in the domain directory create the User class that was mentioned above along with the GENDER enum. We now have a user domain object.

Next create a Hibernate class in the hibernate package and give it a main method. Also create a JUnit 4 unit test class for the Hibernate class. You may have to update the version of the JUnit dependency in your pom file. Lastly delete the App class and it’s test class.

Now that we have the beginning of our project lets add the hibernate dependencies.

...
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.7</version>
            <scope>test</scope>
        </dependency>

        <!--Hibernate and it's annotation dependencies.-->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate</artifactId>
            <version>3.2.7.ga</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>ejb3-persistence</artifactId>
            <version>1.0.2.GA</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-annotations</artifactId>
            <version>3.4.0.GA</version>
            <exclusions>
                <exclusion> <!-- Exclude included slf4j-api dependency to allow chosen version -->
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>javax.transaction</groupId>
            <artifactId>jta</artifactId>
            <version>1.1</version>
        </dependency>

        <!--Hibernates logging dependencies.-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.5.11</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.5.11</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.5.11</version>
        </dependency>

        <!--The embedded database that is going to be used.-->
        <dependency>
            <groupId>org.apache.derby</groupId>
            <artifactId>derby</artifactId>
            <version>10.4.2.0</version>
        </dependency>
    </dependencies>
...

Wow, that is a LOT of dependencies. You’ll notice that 3 of the dependencies are just logging api’s “slf4j“, hibernate seems to need you to include these dependencies your self. Don’t ask me why…

So now that we have all the dependencies we need lets configure hibernate so that we can start it up. How is that done? Well you create a hibernate configuration file of course. This is an XML file that tells hibernate where to find the database, how to log into the database, what type of SQL that database uses, where the domain classes are, what type of connection pooling should be used, if any, and loads of other stuff.

Create the resources directory and then a file within it called hibernate.cfg.xml and past the following into it.

<?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>
        <!-- Database connection settings -->
        <property name="connection.driver_class">org.apache.derby.jdbc.EmbeddedDriver</property>
        <property name="connection.url">jdbc:derby:derbydb;create=true</property>
        <!-- JDBC connection pool (use the built-in) -->
        <property name="connection.pool_size">1</property>
        <!-- SQL dialect -->
        <property name="dialect">org.hibernate.dialect.DerbyDialect</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>
        <!-- Boolean substitution -->
        <property name="query.substitutions">true 'T', false 'F'</property>

    </session-factory>
</hibernate-configuration>

This configuration will set up the database, but we are yet to configure any table mapping. Also it should be noted that this configuration will create the database by inferring it’s structure from the domain objects that we ask it to map. Since we haven’t asked it to map anything it won’t actually create a database at this stage.

Now lets start hibernate for the first time. To do this we use the hibernate configuration we just carried out to create a hibernate session factory. The session factory provides access to an instances of a hibernate session, the session is then used to carry out any request or updates to the database using the domain objects.

All of this must be done inside of a hibernate transaction, when in a transaction and before a transaction is started the session returned by the session factory will always be the same, but once a transaction has finished the session factory will return a brand new session.

So with that all as clear as mud lets start up hibernate and see if we can’t request our first session.

package org.project.hibernate;

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

public class Hibernate {

    private static final SessionFactory SESSION_FACTORY;

    static {
        // Create a session factory with the config file.
        SESSION_FACTORY = new Configuration().configure().buildSessionFactory();
    }

    public static void main(String[] args) {
        Session session = SESSION_FACTORY.getCurrentSession(); // Request for the current hibernate session.

        // YAY! we found a session or BOO! we didn't.
        System.out.println(session != null ? "YAY!" : "BOO!");
    }
}

Ok now that we know hibernate can be started lets map the User file with a hibernate mapping file. We could also map the file using annotations especially since we have the hibernate annotations dependency in our pom file. But I personally think that using mapping files is a better option for two reasons. First, mapping files provide a few features that have yet to be implemented for the annotations and secondly if you use annotations at some point you will find you need to use a custom hibernate annotation. This means you will have hibernate code within your code, so you will have some tight coupling to the hibernate framework which means if you ever want to use your domain classes somewhere else you will have to also bring in the hibernate dependencies which may not be necessary for the other project. Either that or you will have to create a second lot of identical code that just doesn’t contain the hibernate annotations. This of course is terribly bad for the maintainability of your code.

So here is the User mapping file. Create a directory called mapping within your resources directory then create a file called User.hbm.xml with that directory and past the following XML into it. We only create the mapping directory to keep the config files separate and tidy not for any strict reason.

<?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.project.hibernate.domain">

    <!-- Map the User class to the user_table table. It is called user_table -->
    <!-- because the word "user" is a key word in the Derby database. -->
    <class name="User" table="user_table">

        <id name="id">
            <!-- Map the id property for the User class and tell hibernate to -->
            <!-- automatically populate it with what ever method best -->
            <!-- suits the current database. -->
            <generator class="native"/>
        </id>

        <!-- Map the firstName property to the first_name column. -->
        <property name="firstName" column="first_name" not-null="true"/>

        <!-- Map the lastName property to the last_name column. -->
        <property name="lastName" column="last_name" not-null="true"/>

        <!-- Map the age property to the age column. If you don't ->
        <!-- give a column name hibernate will assume the column name -->
        <!-- matches the property name. -->
        <property name="age" not-null="true"/>

        <!-- Map the gender property to the gender column -->
        <property name="gender" not-null="true">
            <!-- Tell hibernate that this property is an enum and what type of enum it is. -->
            <type name="org.hibernate.type.EnumType">
                <param name="enumClass">org.project.hibernate.domain.enums.GENDER</param>
            </type>
        </property>

    </class>

</hibernate-mapping>

We now need to tell hibernate about this mapping file by adding a mapping reference to the hibernate config file.

<?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>
        <!-- Database connection settings -->
        <property name="connection.driver_class">org.apache.derby.jdbc.EmbeddedDriver</property>
        <property name="connection.url">jdbc:derby:derbydb;create=true</property>
        <!-- JDBC connection pool (use the built-in) -->
        <property name="connection.pool_size">1</property>
        <!-- SQL dialect -->
        <property name="dialect">org.hibernate.dialect.DerbyDialect</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>
        <!-- Boolean substitution -->
        <property name="query.substitutions">true 'T', false 'F'</property>

        <!-- Reference to the User mapping file -->
        <mapping resource="mapping/User.hbm.xml" />

    </session-factory>
</hibernate-configuration>

So now that hibernate knows how to map the User class we will be able to add and retrieve users from the database. Also because we have the hbm2ddl.auto property set to update hibernate will create the user_table table from the mapping information we have provided.

So since we can now do stuff with the database lets go and well… do stuff.

package org.project.hibernate;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.project.hibernate.domain.User;

import java.util.List;

import static org.project.hibernate.domain.enums.GENDER.MALE;

public class Hibernate {

    private static final SessionFactory SESSION_FACTORY;

    static {
        // Create a session factory with the config file.
        SESSION_FACTORY = new Configuration().configure().buildSessionFactory();
    }

    public static void main(String[] args) {
        Session session = SESSION_FACTORY.getCurrentSession(); // Request for the current hibernate session.

        if (session != null) {
            // YAY! we found a session.
            System.out.println("YAY!");

            // First when using Hibernate to access a database you must create a transaction.
            // When ever you work inside a transaction there is less chance you will corrupt
            // the data within your database. This is because any changes you make will not
            // be committed until the transaction completes. So if something breaks while in
            // the transaction any changes you made to the database will be rolled back.
            Transaction transaction = session.beginTransaction();

            // Now we create a new user to place within the database. Notice we are using
            // the constructor that does not set the "id" property. This is because in the
            // mapping file we told hibernate to set the "id" property for us.
            User user = new User("Some", "One", 25L, MALE);

            // Now save the new user to the database.
            session.save(user);

            // Complete/commit the transaction so the changes get saved to the database.
            // This will also close the session.
            transaction.commit();

            // Display the id of the user we just save to show that hibernate 
            // did in fact generate the id for us.
            System.out.println("User saved. ID set to (" + user.getId() + ")");
            System.out.println();

            // Now lets display the users in the database so that we can see the users that
            // accumulate within the database after each time we run this project.
            System.out.println("Users in the database:");

            // To access the database again we need to get a new session since we closed the last one.
            session = SESSION_FACTORY.getCurrentSession();
            // Also we will need to start a new transaction.
            transaction = session.beginTransaction();

            // Now we will request all the users from the database by using hibernates criteria API.
            // This provides a nice way of building up SQL queries problematically.
            // Notice we have to cast the list that is returned by hibernate into a list of Users. I really
            // do not see why this is required since we have told hibernate the class type that we are
            // looking for and the List that hibernate returns is actual contains Users.
            List users = (List) session.createCriteria(User.class).list();

            // We can now commit the transaction since we have all the data we want. Though if we
            // wanted to make changes to the users in the list we retrieved and then save those
            // changes to the database we would not be able to close the transaction here.
            transaction.commit();

            // Now we iterate through the users and display their values.
            for (User u : users) {
                System.out.println("User");
                System.out.println(" - id: " + u.getId());
                System.out.println(" - First Name: " + u.getFirstName());
                System.out.println(" - Last Name: " + u.getLastName());
                System.out.println(" - Age: " + u.getAge());
                System.out.println(" - Gender: " + u.getGender());
                System.out.println();
            }

        } else {
            // BOO! we didn't find a session.
            System.out.println("BOO!");
        }
    }
}

Well that’s it we have created a working hibernate application. If you run this application over and over again it will keep adding users to the database. You can clear the database by just deleting the derby directory that is created under the root directory of your project .

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: