Sunday 21 July 2013

Hibernate

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:
Some implementations require to add in the code of the POJO class some code support, like in the case of JPA and Annotation mapping, while others associate the entity elsewhere, like in the case of hbm.xml mappings.

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'?>
<!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'?>
<!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;


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">
  <class name="Event" table="EVENTS">
    <id name="id" column="EVENT_ID">
      <generator class="native"/>
    </id>
    <property name="date" type="timestamp" column="EVENT_DATE"/>
    <property name="title"/>
  </class>
</hibernate-mapping>

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>

  • 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>
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;
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 :

package org.hibernate.tutorial;


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.

    Visible Scopes

    Hibernate can access public, private, and protected accessor methods fields directly.

    Constructors

    Each entity requires a no-argument constructor, with a visibility of package or public.

    Document-Type Definition (DTD)

    Hibernate DTD file is included in hibernate-core.jar (and also in hibernate3.jar, if using the distribution bundle).

    Hibernate Types

    The types declared and used in the mapping files (the "type" property) are not Java data types or SQL database types. These types are  Hibernate mapping types. Hibernate will try to determine the correct conversion and mapping type itself if the type attribute is not present in the mapping. In some cases this automatic detection using Reflection on the Java class might not have the default you expect or need. For example, in the case of a "date" property, Hibernate cannot know if the property, which is of java.util.Date, should map to a SQL date, timestamp, or time column.

    No comments:

    Post a Comment