Hibernate -- An Object/Relational Mappijng Tool by HC121104001557

VIEWS: 0 PAGES: 19

									Hibernate -- An Object/Relational Mapping Tool

Acknowlegement & Reference:
         the Hibernate Reference Document v 3.2.2. This is in the doc/ folder within the Hibernate
distribution. READ CHAPTER 1. Much of these notes and all the code examples are paraphrased and
adapted from this source.


Introduction
"Working with object-oriented software and a relational database can be cumbersome and time consuming
in today's enterprise environments."

Hibernate is an object/relational mapping tool for Java environments. This means it maps object model of
your "business" data to a relational data model in (say) an SQL schema.
        - Java "Bean" classes are mapped to database tables
        - Java Data types are mapped to SQL data types
        - data is made "persistent" via SQL retrievals (SELECT queries) & updates which run "behind the
        scenes"

Hibernate can significantly reduce development time otherwise spent with manual data handling in SQL and
JDBC. "Hibernate's goal is to relieve the developer from 95 percent of common data persistence related
programming tasks."
 - It is most useful with object-oriented domain models and business logic in the Java-based middle-tier.
 - Hibernate can help you to remove or encapsulate vendor-specific SQL code
 - Helps with the common task of result set translation from a tabular representation to a graph of objects.


JavaBeans - a Reminder
-- a strongly encapsulated class (data are private, application programmer's interface is defined by public
methods: mostly get() and set() methods
-- An oject, an instance of the class represents an entity
-- The private data of the class are the attributes of the entity
-- For a data member (private) int attrib we provide "read-only" access in the API with a function public
getAttrib() { return attrib; }
-- For read/write access we also provide public void setAttrib(int a) { attrib = a; }
-- Similarly for other data types [a boolean attribitue running would have a get() method boolean
isRunning() {return running; }]




Reminder: Enterprise application architectures ....

A 3-tier application ...


                           HTTP
    client                request                                                     DB
  (browser)                                Web server                                server
                                                                JDBC
                          XHTML                               connection
                         response

42d56ea6-1cc4-4147-9d09-20571f22457c.doc           1                                     03/11/2012 17:33:00
n-tier applications ...




                                                  Inventory                              Goods
         Client(s)                                   DB                                purchasing
                                                    server                              system 1


                                                  Stock                                  Goods
                                                  control                              purchasing
                                                  server                                system 2

                                                                                       .    .
                                                                                       .    .
                                                                                       . Goods
                                                                                            .
                                                                                        purchasing
                                                                                            system n




                                                                         Application
                                                                           server
         Client(s)

                                                                         Application
                                                 Web server                server


                                           communication                     .
                                             with clients
                                                                             .
                                                                             .
                                                                         Application
                                                              Business      server
                                                                logic




42d56ea6-1cc4-4147-9d09-20571f22457c.doc                2                                  03/11/2012 17:33:00
Part 1 -- A First Hibernate Example
        This example is from the Reference documentation, chapter 1 (adapted to our Oracle DB
environment)
-- a small database application that can store events we want to attend, and information
about the hosts of these events.

First, we need Java (Bean) class to encapsulate an event:

package events;
import java.util.Date;

public class Event {
  private Integer id;
  private String title;
  private Date date;

    public Event() {}

    public Integer getId()       {return id;   }
    public void setId(Integer i) {this.id = i; }

    public Date getDate()       {return date;   }
    public void setDate(Date d) {this.date = d; }

    public String getTitle()        {return title;    }
    public void setTitle(String tl) {this.title = tl; }
}

Note the standard JavaBean naming conventions for Event attribute get and set methods, and private
visibility for the attribute fields -- recommended but not required. Hibernate can actually access the fields
directly.

Note the no-parameter constructor: this is required to enable Hibernate to make Event objects through the
Java reflection mechanism. (See eg
http://en.wikipedia.org/wiki/Reflection_(computer_science) or
http://beautifulcode.oreillynet.com/2007/08/javas_reflection_api.php)

The id attribute is a unique identifier value for a particular event. All persistent entity classes need such an
identifier property -- it corresponds to the primary key in the database. Note that the id is an Integer, not
an int: because Hibernate uses the Java reflection mechanism all attributes have to be object references,
not primitive data. They need to be passed by reference (rather than by value) to the Hibernate engine.

We usually don't change the id of an object, so the setter method should be private. Only Hibernate will
assign identifiers when an object is saved. You will see that Hibernate can access public, private, and
protected accessor methods, as well as (public, private, protected) fields directly.

We are going to make an application that creates and manipulates Events; it will use Hibernate to persist
these objects in a database using a table of events:
-- When our application constructs an Event, Hibernate will insert a row in the DB table.
-- When our application changes the state of an Event, Hibernate causes the relevant row of the DB table to
be updated.
-- At all times, the state of an Event object in the application = the state of a tuple (in the DB) with the
same primary key.


Apache Ant
         We shall use Apache Ant to build our application.



42d56ea6-1cc4-4147-9d09-20571f22457c.doc            3                                    03/11/2012 17:33:00
In an IDE such as JCreator or NetBeans or Eclipse you define a project and you register source code files
with the project. When you issue the build command the IDE determines which classes are "out of date" and
(re-)compiles them. Usually there is a GUI with a pane for editing source files, a pane for showing and
choosing the classes/files belonging to the project, and a pane for displaying the build/run/debug status.

Ant is likewise a manager of project building/running/debugging in this sense, but it does not have a GUI. It
runs at a command prompt and does jobs such as compiling, deploying, running which you have defined in a
"script".

The best way to see how ant works is to see how we build our Hibernate application with it.


Setting Up An Ant project for the Event Manager application
1. Create a project directory & subdirectories:

EventManagerProj\
   lib\
      [software resource .jar files]
   src\
      events\
         Event.java

2. Since will be using Hibernate, all the Hibernate resources (the jar files in the lib/ folder of the Hibernate
distribution) should be copied to the lib subdirectory.

  ant-1.6.5.jar, ant-antlr-1.6.5.jar, ant-junit-1.6.5.jar, ant-launcher-
1.6.5.jar, antlr-2.7.6.jar, ...., xerces-2.6.2.jar, xml-apis.jar.

3. To make Ant do work for us, we to create a build file, build.xml, in the project root directory,
EventManagerProj/. This file defines the various tasks we will ask Ant to do: for the present project
put this in EventManagerProj/build.xml:

<?xml version="1.0" encoding="utf-8"?>
<project name="hibernate-tutorial" default="compile">
  <!-- Set up properties containing important project directories -->
  <property name="sourcedir" value="${basedir}/src"/>
  <property name="targetdir" value="${basedir}/bin"/>
  <property name="librarydir" value="${basedir}/lib"/>

  <!-- Set up class path for compilation and execution -->
  <path id="libraries">
    <fileset dir="${librarydir}">
      <include name="*.jar"/>
    </fileset>
  </path>

  <target name="clean">
    <delete dir="${targetdir}"/>
    <mkdir dir="${targetdir}"/>
  </target>

  <target name="compile" depends="clean, copy-resources">
    <javac srcdir="${sourcedir}" destdir="${targetdir}"
           classpathref="libraries"/>
  </target>

  <target name="copy-resources">
    <copy todir="${targetdir}">
      <fileset dir="${sourcedir}">
        <exclude name="**/*.java"/>
      </fileset>
    </copy>
  </target>


42d56ea6-1cc4-4147-9d09-20571f22457c.doc            4                                    03/11/2012 17:33:00
  <target name="run" depends="compile">
    <java fork="true" classname="events.EventManager" classpathref="libraries">
      <classpath path="${targetdir}"/>
      < arg value="${action}"/>
    </java>
  </target>
</project>

Each target is a task we can ask Ant to do:
        >ant compile
                  compiles all the .java files in src/ with javac and puts the .class files in a new directory
                  EventManagerProj/bin/. All .jar files in lib/ are added to classpath.

         >ant clean
               deletes an exisiting bin and makes a new one

         >ant copy-resources
               copies source files other than *.java from src/ to bin/. We shall see a need for this below.

         >ant run
               runs the application.

Note the dependencies: a run does a compile first; a compile does a clean and a copy-resources first.


Configuring Hibernate for our application
          So far, all we have done by way of building our application is write a source file Event.java and
put it in src/events/ (in the subdirectory because it is in package events). We thus have a class to
encapsulate our event entities. We still have to write some java to create, store, retrieve and manipulate event
records, and we have to build in the Hibernate infrastructure to make Event object persistent in the database.

Let us add the Hibernate persistence stuff first.


The Hibernate Mapping File
       We have written the Java Bean events.Event and stored its source as
src/events/Event.java. We need a file to tell Hibernate what table in the database (and what
columns) it should access to load and store Event objects. It does this via a mapping file
Event.hbm.xml, which is stored in src/events/ alongside Event.java:

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

In general, we require one mapping file for each persistent entity class in our application.


Hibernate Configuration
     We also need a "master" Hibernate configuration file for our project. It is called
hibernate.cfg.xml and lives in src/ (not in src/events/):

42d56ea6-1cc4-4147-9d09-20571f22457c.doc              5                                      03/11/2012 17:33:00
<?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>
    <!-- DB connection settings -->
    <property name="connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
    <property name="connection.url">jdbc:oracle:thin:@CMAPP6:1521:ceis</property>
    <property name="connection.username">cgmb3</property>
    <property name="connection.password">password</property>

     <!-- JDBC connection pool (use the built-in one) -->
     <property name="connection.pool_size">1</property>

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

     <!-- Enable Hibernate's auto session context management -->
     <property name="current_session_context_class">thread</property>

     <!-- Disable 2nd-level cache -->
     <property name="cache.privider_class">org.hibernate.cache.NoCacheProvider</property>

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

     <!-- drop and re-create db schema on startup -->
     <property name="hbm2ddl.auto">create</property>

    <mapping resource="events/Event.hbm.xml"/>
  </session-factory>
</hibernate-configuration>

Note that this file defines our JDBC connectivity, and also the dialect of SQL we will be using.

NB There should be a copy of the jar containing the JDBC driver (eg ojdbc14.jar) in the lib/ folder.

The Application

The last job is to write some java to create/store/load Events, in particular, making them persistent in the
database.

First some infrastructure code. We have to start up Hibernate. This start-up includes building a global
SessionFactory object and storing it somewhere for easy access in application code. A
SessionFactory can open up new Sessions. A Session represents a single-threaded unit of work.

The SessionFactory is a thread-safe global object, instantiated once.
The util.HibernateUtil helper class takes care of start-up and makes accessing a
SessionFactory convenient:

package util;
import org.hibernate.*;
import org.hibernate.cfg.*;

public class HibernateUtil {
  private static final SessionFactory sessionFactory;

  static {
    try {
      //create session factory from hibernate.cfg.xml
      sessionFactory = new Configuration().configure().buildSessionFactory();
    } catch (Throwable ex) {
      System.err.println("Initial session factory creation failed " + ex);
      throw new ExceptionInInitializerError(ex);

42d56ea6-1cc4-4147-9d09-20571f22457c.doc            6                                     03/11/2012 17:33:00
        }
    }

    public static SessionFactory getSessionFactory() {return sessionFactory; }
}

Put this source in path src/util/HibernateUtil.java. The project directory now looks like

EventManagerProj\
   lib\
      [software resource .jar files]
   src\
      events\
         Event.java
         Event.hbm.xml
      util\
         HibernateUtil.java
      hibernate.cfg.xml
   build.xml



The Application Main Class - events.EventManager:

package events;
import org.hibernate.Session;
import java.util.Date;
import 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();
    }
}

This is in package events and so goes in src/events/EventManager.java.

This application creates a new Event object and hands it over to Hibernate. Hibernate takes it and executes
an SQL INSERT on the database.

Note the session and transaction handling in method createAndStoreEvent(...).

A Session is a single unit of work. Lets assume Hibernate Sessions correspond 1-to-1 with database
transactions. To shield our code from the actual underlying transaction system (JDBC) we use the
Transaction API that is available on the Hibernate Session.

What does sessionFactory.getCurrentSession() do? First, you can call it as many times and
anywhere you like, once you get hold of your SessionFactory (made easy by HibernateUtil). The
getCurrentSession() method always returns the "current" unit of work.


42d56ea6-1cc4-4147-9d09-20571f22457c.doc          7                                   03/11/2012 17:33:00
Because the configuration option for this mechanism (in hibernate.cfg.xml) is "thread", the current
unit of work is bound to the current Java thread that executes the application. However, this is not the full
picture, you also have to consider scope: when a unit of work begins and when it ends.

A Session begins when it is first needed, when the first call to getCurrentSession() is made. It is
then bound by Hibernate to the current thread. When the transaction ends, either through commit or rollback,
Hibernate automatically unbinds the Session from the thread and closes it for you. If you call
getCurrentSession() again, you get a new Session and can start a new unit of work. This thread-
bound programming model is the most popular way of using Hibernate.

Related to the unit of work scope: should the Hibernate Session be used to execute one or several database
operations? The above example uses one Session for one operation. The scope of a Hibernate Session is
flexible but you should never design your application to use a new Hibernate Session for every database
operation. So even if you see it a few more times in the following (very trivial) examples, consider session-
per-operation an anti-pattern.


Compiling and Running the Application
        Once a compilation has been done (ant compile) the projects directory should look as below
(can you see the relevance of the ant copy-resources task now?):

EventManagerProj\
   lib\
      [software resource .jar files]
   bin\
      events\
         Event.class
         Event.hbm.xml
         EventManager.class
      util\
         HibernateUtil.class
   src\
      events\
         Event.java
         Event.hbm.xml
         EventManager.java
      util\
         HibernateUtil.java
      hibernate.cfg.xml
   build.xml

You should be able to run the application with the command
> ant run –Daction=”store”
       (which will in fact do the compile step first).

It is a good idea first to copy the file log4j.properties from the etc/ folder of the Hibernate
distribution to the src/ folder. This provides resources for console display of Hibernate information as the
application runs.


This first application, configured to run in Oracle in our computing environment, is to be found in
Project1.zip (except for the contents of the lib folder, which you should copy separately).




42d56ea6-1cc4-4147-9d09-20571f22457c.doc           8                                     03/11/2012 17:33:00
Listing Events in the Database

The application main class, events.EventManager, in Project1.zip actually has also a function to list
all the Events stored in the Events table:

package events;
import org.hibernate.Session;
import java.util.Date;
import 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());
    }
    else if (args[0].equals("list")) {
      System.out.println("------------------------------------------------");
      List events = mgr.listEvents();
      if (events.isEmpty())
        System.out.println("NO EVENTS");
      else {
        Iterator i = events.iterator();
        while (i.hasNext()) {
          Event theEvent = (Event) i.next();
          System.out.println(">>Event: " + theEvent.getTitle() +
                              " Time: " + theEvent.getDate());
        }
      }
      System.out.println("------------------------------------------------");
    }

        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();
    }

    private List listEvents() {
      Session session = HibernateUtil.getSessionFactory().getCurrentSession();
      session.beginTransaction();
      List result = session.createQuery("from Event").list();
      session.getTransaction().commit();
      return result;
    }
}

Experiment: You can get a list of Events by running >ant run –Daction=list. Of course, if you do this
before running >ant run –Daction=store, you will get an empty list. Try-
    - Commenting out the <property name="hbm2ddl.auto">create</property> line of
        src/hibernate.cfg.xml. This will stop the table being dropped and rebuilt from empty every
        time you do a run. Then -
    - Do >ant run –Daction=store several times, to store several Events to the table. Then –
    - Do >ant run –Daction=list. You should see a listing of all the strored Events.




42d56ea6-1cc4-4147-9d09-20571f22457c.doc        9                                 03/11/2012 17:33:00
Part 2 -- Mapping Associations (Relationships)

Let us introduce a new entity: a Person:

package events;
import java.util.*;

public class Person {
  private Integer id;
  private int age;
  private String firstname;
  private String lastname;

    public Person() {}

    public Integer getId()        {return id;   }
    private void setId(Integer i) {this.id = i; }

    public int getAge()       {return age;   }
    public void setAge(int a) {this.age = a; }

    public String getFirstname()       {return firstname; }
    public void setFirstname(String n) {firstname = n; }

    public String getLastname()       {return lastname; }
    public void setLastname(String n) {lastname = n; }
}


This source file (Person.java) goes in src/events/ and alongside it, an accompanying Hibernate
mapping file, Person.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>

    <class name="events.Person" table="PERSON">
      <id name="id" column="PERSON_ID">
        <generator class="native"/>
      </id>
      <property name="age" />
      <property name="firstname"/>
      <property name="lastname"/>
    </class>

</hibernate-mapping>

We need to add a line to the Hibernate configuration file hibernate.cfg.xml:

      ....
      <mapping resource="events/Event.hbm.xml"/>
      <mapping resource="events/Person.hbm.xml"/>
    </session-factory>

With these 3 steps we have added a new class of persistent objects to our Event Manager project.

We want to implement a many-to-many relationship between Persons and Events – an Event has
participants, a Person has the set of his/her Events.

One way of doing this is to add to the Person class a set of Events (Person.java):



42d56ea6-1cc4-4147-9d09-20571f22457c.doc           10                                 03/11/2012 17:33:00
package events;
import java.util.*;

public class Person {
  private Integer id;
  private int age;
  private String firstname;
  private String lastname;
  private Set<Event> events = new HashSet<Event>();

    public Person() {}

    public Integer getId()        {return id;   }
    private void setId(Integer i) {this.id = i; }
    public int getAge()       {return age;    }
    public void setAge(int a) {this.age = a; }
    public String getFirstname()       {return firstname; }
    public void setFirstname(String n) {firstname = n; }
    public String getLastname()       {return lastname; }
    public void setLastname(String n) {lastname = n; }

    protected Set<Event> getEvents()       {return events; }
    protected void setEvents(Set<Event> s) {events = s;    }

    public void addToEvent(Event evt) {
      events.add(evt);
      evt.getParticipants().add(this);
    }

    public void removeFromEvent(Event evt) {
      events.remove(evt);
      evt.getParticipants().remove(this);
    }

    public void listEvents() {
      System.out.println("Events participated in by person " + id + ":");
      for (Event e: events)
        System.out.println(e);
    }
}

... and likewise add to the Events class a set of participants:

package events;
import java.util.*;

public class Event {
  private Integer id;
  private String title;
  private Date date;

    private Set<Person> participants;

    public Event() {}

    public Integer getId()        {return id;   }
    private void setId(Integer i) {this.id = i; }
    public Date getDate()       {return date;   }
    public void setDate(Date d) {this.date = d; }
    public String getTitle()        {return title;    }
    public void setTitle(String tl) {this.title = tl; }
    protected Set<Person> getParticipants()              {return participants; }
    protected void        setParticipants(Set<Person> s) {participants = s; }

    public void addToParticipants(Person p) {
      participants.add(p);
      p.getEvents().add(this);
    }

    public void removeFromParticipants(Person p) {

42d56ea6-1cc4-4147-9d09-20571f22457c.doc            11                  03/11/2012 17:33:00
        participants.remove(p);
        p.getEvents().remove(this);
    }

    public void listParticipants() {
      System.out.println("Participants in event " + id + ":");
      for (Person p: participants)
        System.out.println(p);
    }
}

Lastly, to implement the many-many relationship, we need to get Hibernate to support these sets. This will
happen -- Hibernate will ensure that at all times the Sets within each Person or Event are correctly
populated -- provided appropriate <set> clauses are added to the mapping files. First,
Person.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>
  <class name="events.Person" table="PERSON">
    <id name="id" column="PERSON_ID">
      <generator class="native"/>
    </id>
    <property name="age" />
    <property name="firstname"/>
    <property name="lastname"/>

    <set name="events" table="PERSON_EVENT">
      <key column="PERSON_ID"/>
      <many-to-many column="EVENT_ID" class="events.Event"/>
    </set>
  </class>
</hibernate-mapping>



... and second, 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>
  <class name="events.Event" table="EVENTS">
    <id name="id" type="int" column="EVENT_ID">
      <generator class="native"/>
    </id>
    <property name="date" type="timestamp" column="EVENT_DATE"/>
    <property name="title"/>

    <set name="participants" table="PERSON_EVENT" inverse="true">
      <key column="EVENT_ID"/>
      <many-to-many column="PERSON_ID" class="events.Person"/>
    </set>
</class>
</hibernate-mapping>

This causes Hibernate to not only construct tables in the DB or Events and of Persons, but also a table
corresponding to a link entity, PERSON_EVENT. As usual, with many-many relationships we end up with a
DB schema like this:




42d56ea6-1cc4-4147-9d09-20571f22457c.doc         12                                   03/11/2012 17:33:00
     EVENTS                                    PERSON_EVENT                                  PERSON
  * EVENT_ID                               *     EVENT_ID                                * PERSON_ID
                                      *                            *
    EVENT_DATE                             *     PERSON_ID                                 AGE
    TITLE                                                                                  FIRSTNAME
                                                                                           LASTNAME



Some points to note
1. Event class has methods to add and remove Persons from the participants set, and Person class has
methods to add and remove Events from the events set. For consistency, it is best to code these so that
adding a Person to an Event’s participants set always causes also the Event to be added to the
Person’s events set; and vice versa; and similarly for removing.

2. Note the inverse="true" clause in Event’s mapping file. For you, and for Java, a bi-directional link is
simply a matter of setting the references on both sides correctly. Hibernate however doesn't have enough
information to correctly arrange SQL INSERT and UPDATE statements (to avoid constraint violations), and
needs some help to handle bidirectional associations properly. Making one side of the association inverse
tells Hibernate to basically ignore it, to consider it a mirror of the other side. That's all that is necessary for
Hibernate to work out all of the issues when transformation a directional navigation model to a SQL
database schema. The rules you have to remember are straightforward: All bi-directional associations need
one side as inverse. In a one-to-many association it has to be the many-side, in many-to-many association
you can pick either side, there is no difference.
We can put this to work by adding an addPersonToEvent(...) method to EventManager:

package events;
import org.hibernate.Session;
import java.util.Date;
import 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());
    }
    else if (args[0].equals("addpersontoevent")) {
      Integer eventId = mgr.createAndStoreEvent("My Event", new Date());
      Integer personId = mgr.createAndStorePerson(16, "Foo", "Bar");
      mgr.addPersonToEvent(personId, eventId);
      System.out.println("Added person " + personId + " to event " + eventId);
    }

      HibernateUtil.getSessionFactory().close();
  }

  private Integer 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();
    return theEvent.getId();
  }

  private Integer createAndStorePerson(int age, String firstname, String lastname) {
    Session session = HibernateUtil.getSessionFactory().getCurrentSession();
    session.beginTransaction();
    Person thePerson = new Person();
    thePerson.setAge(age);
    thePerson.setFirstname(firstname);
    thePerson.setLastname(lastname);

42d56ea6-1cc4-4147-9d09-20571f22457c.doc             13                                      03/11/2012 17:33:00
        session.save(thePerson);
        session.getTransaction().commit();
        return thePerson.getId();
    }

    private void addPersonToEvent(Integer personId, Integer eventId) {
      Session session = HibernateUtil.getSessionFactory().getCurrentSession();
      session.beginTransaction();

        Person aPerson = (Person)session.load(Person.class, personId);
        Event anEvent = (Event)session.load(Event.class, eventId);
        aPerson.getEvents().add(anEvent);
        aPerson.listEvents();
        anEvent.listParticipants();

        session.getTransaction().commit();
    }
}


After loading a Person and an Event, simply modify the collection using the normal collection methods. As
you can see, there is no explicit call to update() or save(), Hibernate automatically detects that the
collection has been modified and needs to be updated. This is called automatic dirty checking, and you can
also try it by modifying the name or the date property of any of your objects. As long as they are in
persistent state, that is, bound to a particular Hibernate Session (i.e. they have been just loaded or saved in
a unit of work), Hibernate monitors any changes and executes SQL in a write-behind fashion. The process of
synchronizing the memory state with the database, usually only at the end of a unit of work, is called
flushing. In our code, the unit of work ends with a commit (or rollback) of the database transaction - as
defined by the thread configuration option for the CurrentSessionContext class.


Adding a Collection of Values
        We can give each Person a set of e-mail addresses (of type String) – this will be an attribute of
Person of type Set<String> not referencing another entity:

package events;
import java.util.*;

public class Person {
  private Integer id;
  private int age;
  private String firstname;
  private String lastname;

    private Set<Event> events = new HashSet<Event>();
    private Set<String> emailAddresses = new HashSet<String>();

    ......other methods .....

    public Set<String> getEmailAddresses()       {return emailAddresses; }
    public void setEmailAddresses(Set<String> s) {emailAddresses = s;    }
}

Add Hibernate infrastructure for this by adding a <set> clause to Person.hbm.xml:

...
<hibernate-mapping>
  <class name="events.Person" table="PERSON">
    <id name="id" column="PERSON_ID">
      <generator class="native"/>
    </id>
    <property name="age" />
    <property name="firstname"/>
    <property name="lastname"/>

        <set name="events" table="PERSON_EVENT">


42d56ea6-1cc4-4147-9d09-20571f22457c.doc           14                                    03/11/2012 17:33:00
         <key column="PERSON_ID"/>
         <many-to-many column="EVENT_ID" class="events.Event"/>
       </set>

     <set name="emailAddresses" table="PERSON_EMAIL_ADDR">
       <key column="PERSON_ID"/>
       <element type="string" column="EMAIL_ADDR"/>
     </set>
   </class>
 </hibernate-mapping>

 The difference compared with the earlier mapping is the element part, which tells Hibernate that the
 collection does not contain references to another entity, but a collection of elements of type String (the
 lowercase name tells you it's a Hibernate mapping type/converter). Once again, the table attribute of the
 set element determines the table name for the collection. The key element defines the foreign-key column
 name in the collection table. The column attribute in the element element defines the column name where
 the String values will actually be stored.


   EVENTS                       PERSON_EVENT                    PERSON
* EVENT_ID                     * EVENT_ID                   * PERSON_ID
  EVENT_DATE             *     * PERSON_ID     *              AGE                          PERSON_EMAIL_ADDR
  TITLE                                                       FIRSTNAME                  * PERSON_ID
                                                              LASTNAME                   * ENAIL_ADDR




 You can see that the primary key of the collection table is in fact a composite key, using both columns. This
 also implies that there can't be duplicate email addresses per person, which is exactly the semantics we need
 for a set in Java.

 You can now try and add elements to this collection, just like we did before by linking persons and events.
 It's the same code in Java:

 package events;
 import org.hibernate.Session;
 import java.util.Date;
 import 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());
     }
     else if (args[0].equals("addpersontoevent")) {
       Integer eventId = mgr.createAndStoreEvent("My Event", new Date());
       Integer personId = mgr.createAndStorePerson(16, "Foo", "Bar");
       mgr.addPersonToEvent(personId, eventId);
       System.out.println("Added person " + personId + " to event " + eventId);

         mgr.addEmailToPerson(personId, "addr1@xxx");
         mgr.addEmailToPerson(personId, "addr2@xxx");
       }
       HibernateUtil.getSessionFactory().close();
   }

   private Integer createAndStoreEvent(String title, Date theDate) { ... }
   private Integer createAndStorePerson(int age, String firstname, String lastname) {
     .... }
   private void addPersonToEvent(Integer personId, Integer eventId) { .....}

   private void addEmailToPerson(Integer personId, String emailAddress) {
     Session session = HibernateUtil.getSessionFactory().getCurrentSession();
     session.beginTransaction();

 42d56ea6-1cc4-4147-9d09-20571f22457c.doc          15                                   03/11/2012 17:33:00
        Person aPerson = (Person)session.load(Person.class, personId);
        aPerson.getEmailAddresses().add(emailAddress);

        session.getTransaction().commit();
    }
}



This second application, configured to run in Oracle in our computing environment, is to be found in
Project2.zip (except for the contents of the lib folder, which you should copy separately).




42d56ea6-1cc4-4147-9d09-20571f22457c.doc        16                                 03/11/2012 17:33:00
Part 3 –An EventManager Web Application)

A Hibernate web application uses Session and Transaction almost like a standalone application. Here
now is an EventManagerServlet. This servlet can list all events stored in the database, and it provides an
HTML form to enter new events. It handles HTTP GET requests only.

The pattern we are applying here is called session-per-request. When a request hits the servlet, a new
Hibernate Session is opened through the first call to getCurrentSession() on the SessionFactory. Then
a database transaction is started—all data access as to occur inside a transaction, no matter if data is read or
written (we don't use the auto-commit mode in applications).

Do not use a new Hibernate Session for every database operation. Use one Hibernate Session that is
scoped to the whole request. Use getCurrentSession(), so that it is automatically bound to the current Java
thread.

package events;

import    java.io.*;
import    java.util.*;
import    javax.servlet.*;
import    javax.servlet.http.*;
import    java.text.SimpleDateFormat;
import    util.HibernateUtil;

public class EventManagerServlet extends HttpServlet {
  protected void doGet(
    HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {

     SimpleDateFormat dateFormatter = new SimpleDateFormat("dd.MM.yyyy");

     try {
       //Begin unit of work --
       HibernateUtil.getSessionFactory().getCurrentSession().beginTransaction();

         //Process request & render page --
         PrintWriter out = response.getWriter();
         out.println("<html><head><title>Event Manager</title></head><body>");

         //Handle actions
         if ("store".equals(request.getParameter("action"))) {
           String eventTitle = request.getParameter("eventTitle");
           String eventDate = request.getParameter("eventDate");

             if ("".equals(eventTitle) || "".equals(eventDate)) {
               out.println("<b><i>Please enter event title and date.</i></b>");
             } else {
               createAndStoreEvent(eventTitle, dateFormatter.parse(eventDate));
               out.println("<b><i>Added event.</i></b>");
             }
         }

         //Print page
         printEventForm(out);
         listEvents(out, dateFormatter);

         //Write HTML footer
         out.println("</body></html>");
         out.flush();
         out.close();

         //End unit of work
         HibernateUtil.getSessionFactory().getCurrentSession()
           .getTransaction().commit();
     }


42d56ea6-1cc4-4147-9d09-20571f22457c.doc           17                                    03/11/2012 17:33:00
      catch (Exception ex) {
        HibernateUtil.getSessionFactory().getCurrentSession()
          .getTransaction().rollback();
        throw new ServletException(ex);
      }
    } //end goGet()

    private void printEventForm(PrintWriter out) {
      out.println("<h2>Add new event:</h2>");
      out.println("<form>");
      out.println("Title: <input name='eventTitle' length='50'/><br/>");
      out.println("Date (eg 24.12.2009): <input name='eventDate' length='10'/><br/>");
      out.println("<input type='submit' name='action' value='store'/>");
      out.println("</form>");
    }

    private void listEvents(PrintWriter out, SimpleDateFormat dateFormatter) {
      List result = HibernateUtil.getSessionFactory().getCurrentSession()
        .createCriteria(Event.class).list();

      if (result.size() > 0) {
        out.println("<h2>Events in database:</h2>");
        out.println("<table border='1'>");
        out.println("<tr>");
        out.println("<th>Event title</th>");
        out.println("<th>Event date</th>");
        out.println("</tr>");
        for (Iterator it = result.iterator(); it.hasNext();) {
          Event evt = (Event)it.next();
          out.println("<tr>");
          out.println("<td>" + evt.getTitle() + "/td>");
          out.println("<td>" + dateFormatter.format(evt.getDate()) + "/td>");
          out.println("</tr>");
        }
        out.println("/table");
      }
    } //end listEvents()

    protected void createAndStoreEvent(String title, Date theDate) {
      Event theEvent = new Event();
      theEvent.setTitle(title);
      theEvent.setDate(theDate);

        HibernateUtil.getSessionFactory().getCurrentSession().save(theEvent);
    }

}


The listEvents() method uses the Hibernate Session bound to the current thread to execute a query.

Finally, the store action is dispatched to the createAndStoreEvent() method, which also uses the
Session of the current thread.

A request to the servlet will be processed in a single Session and Transaction. As earlier in the standalone
application, Hibernate can automatically bind these objects to the current thread of execution. This gives you
the freedom to layer your code and access the SessionFactory in any way you like. Usually you'd use a
more sophisticated design and move the data access code into data access objects (the DAO pattern). See the
Hibernate Wiki for more examples.


Deploying the Servlet and Running the Webapp

We need are deployment desciptor, web.xml, in the root directory of the project:




42d56ea6-1cc4-4147-9d09-20571f22457c.doc          18                                   03/11/2012 17:33:00
<?xml version="1.0" encoding="utf-8"?>
<web-app version="2.4"
  xmlns="http://java.sun.com/xml/ns/j2ee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemeLocation="http://jva.sun.com/xml/ns/j2ee
    http://jva.sun.com/xml/ns/j2ee/web_app_2_4.xsi">

  <servlet>
      <servlet-name>Event Manager</servlet-name>
      <servlet-class>events.EventManagerServlet</servlet-class>
   </servlet>

   <servlet-mapping>
      <servlet-name>Event Manager</servlet-name>
      <url-pattern>/eventmanager</url-pattern>
   </servlet-mapping>
</web-app>

 We can manually copy the deployment descriptor, servlet class and helper classes, and all the lib resources,
into the usual webapp folder structure. An easier way is to use Ant’s ability to package a webapp into a .war
archive (like a .jar, a folder tree archived with the zip algorithm). Add this target to Ant’s build.xml file:

  <target name="war" depends="compile">
    <war destfile="hibernate-tutorial.war" webxml="web.xml">
      <lib dir="${librarydir}">
        <exclude name="jsdk*.jar"/>
      </lib>

      <classes dir="${targetdir}"/>
    </war>
  </target>


This target creates a file called hibernate-tutorial.war in your project directory. It packages all libraries
and the web.xml deployment descriptor.

As with the stand-alone application, ensure there is a copy of log4j.properties in the project base
directory. You may also need a copy of javaee.jar (for compiling servlets) in lib/.

To build and deploy call >ant war in your project directory and copy the hibernate-tutorial.war file
into Tomcat’s webapp directory.

Once deployed and Tomcat is running, access the application at
http://localhost:8080/hibernate-tutorial/eventmanager.

Make sure you watch the Tomcat log to see Hibernate initialize when the first request hits your servlet (the
static initializer in HibernateUtil is called) and to get the detailed output if any exceptions occur.

This web application, configured to run in Oracle in our computing environment, is to be found in
Project3.zip (except for the contents of the lib folder, which you should copy separately).




42d56ea6-1cc4-4147-9d09-20571f22457c.doc           19                                   03/11/2012 17:33:00

								
To top