What it's Hibernate
Hibernate is an object/relational mapping tool for Java environments. The term object/relational mapping (ORM) refers to the technique of mapping a data representation from an object model to a relational data model with a SQL-based schema.Requirements
- Download and unpack the Hibernate distribution from the Hibernate website.
- Make sure you have JDK 5.0 installed.
Set up your environment
Set up your classpath:
- Copy all Hibernate3 core and required 3rd party library files (see lib/README.txt in Hibernate distribution folder).
A simple mapping
Suppose we want to persist the following JavaBean class:
package events;
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;
}
}
The basis structure of a mapping file looks like this:
<?xml version="1.0"?>Between the two hibernate-mapping tags, include a class element:
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
[...]
</hibernate-mapping>
<hibernate-mapping>
<class name="events.Event" table="EVENTS">
...
</class>
</hibernate-mapping>
Now, we must map the primary key and the rest of the properties (this will be event.hbm.xml file):
<hibernate-mapping>
<class name="events.Event" table="EVENTS">
<id name="id" column="EVENT_ID">
<generator class="identity"/>
</id>
<property name="date" type="timestamp" column="EVENT_DATE"/>
<property name="title" type="string" column="TITLE"/>
</class>
</hibernate-mapping>
With the <id>tag, you map the object property with the table primary key. The <generator> tag, define the generation strategy for that primary key and must match with the defined in the table. The generator class can be: identity, sequence, assigned as the common choices. For other generator class please refer to the hibernate documentation.
The <property> tag define the rest of columns mapped with the respectively object attribute.
When a key is composite, we must use the <composite-id> tag as the following example, where a person in an event can have a position in a room:
package events;
public class SeatPosition {
private SeatPositionPK id;
// properties
private int color;
// Accessor methods for all properties, private setter for 'id'
}
The definition of the primary key class:
package events;
public class SeatPositionPK {
// composite key properties
private int room;
private int seat;
// Accessor methods for all properties
}
The mapping file will looks like:
<hibernate-mapping>
<class name="events.SeatPosition" table="SEAT_POSITION">
<composite-id name="id" type="events.SeatPositionPK" column="id">
<key-property name="room" column="ROOM" type="integer"/>
<key-property name="seat" column="SEAT" type="integer"/>
</composite-id>
<property name="color" type="integer"/>
</class>
</hibernate-mapping>
Maping Associations
A database will have more than "simple" tables and you will need map different kinds of relationship between tables.
Many-To-Many
Given the following class definition
package events;
public class Person {
private Long id;
private int age;
private String firstname;
private String lastname;
public Person() {}
// Accessor methods for all properties, private setter for 'id'
}
And the following given mapping file (for person, Person.hbm.xml):
<hibernate-mapping>
<class name="events.Person" table="PERSON">
<id name="id" column="PERSON_ID">
<generator class="identity"/>
</id>
<property name="age"/>
<property name="firstname"/>
<property name="lastname"/>
</class>
</hibernate-mapping>
Suppose that a person can participate in one or more events, so we must add a collection that contains Event objects in a Person instance.
public class Person {
// Properties definition
...
private Set events = new HashSet();
public Set getEvents() {
return events;
}
public void setEvents(Set events) {
this.events = events;
}
}
And the mapping file change like this:
<hibernate-mapping>
<class name="events.Person" table="PERSON">
<id name="id" column="PERSON_ID">
<generator class="identity"/>
</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>
We have mapped the database schema:
_____________ __________________
| | | | _____________
| EVENTS | | PERSON_EVENT | | |
|_____________| |__________________| | PERSON |
| | | | |_____________|
| *EVENT_ID | <--> | *EVENT_ID | | |
| EVENT_DATE | | *PERSON_ID | <--> | *PERSON_ID |
| TITLE | |__________________| | AGE |
|_____________| | FIRSTNAME |
| LASTNAME |
|_____________|
If you want to do a bidirectional relationship, where from a event you can get the event participants. First, add a collection of participants to the Event Event class:
private Set<Person> participants = new HashSet<Person>();
public Set<Person> getParticipants() {
return participants;
}
public void setParticipants(Set<Person> participants) {
this.participants = participants;
}
Now map this side of the association too, in Event.hbm.xml.
<set name="participants" table="PERSON_EVENT" inverse="true">
<key column="EVENT_ID"/>
<many-to-many column="PERSON_ID" class="events.Person"/>
</set>
One-To-Many
If a person can have one or more email addresses:
private Set<String> emailAddresses = new HashSet();
public Set<String> getEmailAddresses() {
return emailAddresses;
}
public void setEmailAddresses(Set<String> emailAddresses) {
this.emailAddresses = emailAddresses;
}
The mapping of this Set:
<set name="emailAddresses" table="PERSON_EMAIL">
<key column="PERSON_ID"/>
<element type="string" column="EMAIL_ADDR"/>
</set>
Look at the updated schema:
_____________ __________________
| | | | _____________
| EVENTS | | PERSON_EVENT | | | ______________
|_____________| |__________________| | PERSON | | |
| | | | |_____________| | PERSON_EMAIL |
| *EVENT_ID | <--> | *EVENT_ID | | | |______________|
| EVENT_DATE | | *PERSON_ID | <--> | *PERSON_ID | <--> | *PERSON_ID |
| TITLE | |__________________| | AGE | | *EMAIL_ADDR |
|_____________| | FIRSTNAME | |______________|
| LASTNAME |
|_____________|
One-To-One
Suppose you want to know who the owner of a specific vehicle is:
public class Vehicle {
private String id;
private String model;
private String brand;
private int year;
private Person owner;
public Vehicle(){}
// Accessor methods for all properties, private setter for 'id'
}
The mapping file (Vehicle.hbm.xml) will be like:
<hibernate-mapping>
<class name="events.Vehicle" table="VEHICLE">
<id name="id" column="VEHICLE_ID">
<generator class="identity"/>
</id>
<property name="brand"/>
<property name="year"/>
<property name="model"/>
<one-to-one name="owner" class="Person" constrained="true"/>
</class>
</hibernate-mapping>
Configuring Hibernate
After known how to map JavaBeans with database tables its time to make this mapping works. First than all, we must specify how Hibernate will connect to the database. The principal configuration file is hibernate.cfg.xml, and this is an example:
<?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>
<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>
<property name="connection.pool_size">1</property>
<property name="dialect">org.hibernate.dialect.HSQLDialect</property>
<property name="current_session_context_class">thread</property>
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
<property name="show_sql">true</property>
<property name="hbm2ddl.auto">create</property>
<mapping resource="events/Event.hbm.xml"/>
<mapping resource="events/Person.hbm.xml"/>
<mapping resource="events/SeatPosition.hbm.xml"/>
</session-factory>
</hibernate-configuration>
It's time to load and store some objects, but first we have to complete the setup with some infrastructure code. We have to startup Hibernate. This startup includes building a global SessionFactory object and to store it somewhere for easy access in application code. A SessionFactory can open up new Session's. A Session represents a single-threaded unit of work, the SessionFactory is a thread-safe global object, instantiated once.
We'll create a HibernateUtil helper class which takes care of startup and makes accessing a SessionFactory convenient. Let's have a look at the implementation:
package util;The hibernate configuration file (hibernate.cfg.xml) must be in the root of the application classpath. Every mapping file must be with its respective class. This is an example of application structure for the whole example that we have been working on:
import org.hibernate.*;
import org.hibernate.cfg.*;
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;
}
}
+ lib
<Hibernate and third-party libraries>
+scr
hibernate.cfg.xml
+events
Event.java
Event.hbm.xml
Person.java
Person.hbm.xml
SeatPosition.java
+classes // it’s a copy of src, but with binary files (except xml of course)
hibernate.cfg.xml
+events
Event.class
Event.hbm.xml
Person.class
Person.hbm.xml
SeatPosition.class
Loanding and storing object
Now, the following example show how to use the session object. Remember that on N-Layer Application the persistence layer will use the services of sessionFactory and session objects.
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();
}
}
Querying
Another important functionality that hibernate provide, beside persist objects, it’s the querying capabilities. Hibernate provide a cross-platform language, very similar than SQL named Hibernate Query Language (HQL). For programmatic query creation, Hibernate supports a sophisticated Criteria and Example query feature (QBC and QBE). You may also express your query in the native SQL of your database, with optional support from Hibernate for result set conversion into objects.
Executing Queries
HQL and native SQL queries are represented with an instance of org.hibernate.Query. This interface offers methods for parameter binding, result set handling, and for the execution of the actual query. You always obtain a Query using the current Session:
List cats = session.createQuery(
"from Cat as cat where cat.birthdate < ?")
.setDate(0, date)
.list();
List mothers = session.createQuery(
"select mother from Cat as cat join cat.mother as mother where cat.name = ?")
.setString(0, name)
.list();
List kittens = session.createQuery(
"from Cat as cat where cat.mother = ?")
.setEntity(0, pk)
.list();
Cat mother = (Cat) session.createQuery(
"select cat.mother from Cat as cat where cat = ?")
.setEntity(0, izi)
.uniqueResult();]]
Query mothersWithKittens = (Cat) session.createQuery(
"select mother from Cat as mother left join fetch
other.kittens");
Set uniqueMothers = new HashSet(mothersWithKittens.list());
Bind Parameters
Methods on Query are provided for binding values to named parameters or JDBC-style ? parameters. Contrary to JDBC, Hibernate numbers parameters from zero. Named parameters are identifiers of the form :name in the query string. The advantages of named parameters are:
- named parameters are insensitive to the order they occur in the query string
- they may occur multiple times in the same query
- they are self-documenting
//named parameter (preferred)
Query q = sess.createQuery("from DomesticCat cat where cat.name = :name");
q.setString("name", "Fritz");
Iterator cats = q.iterate();
//positional parameter
Query q = sess.createQuery("from DomesticCat cat where cat.name = ?");
q.setString(0, "Izi");
Iterator cats = q.iterate();
//named parameter list
List names = new ArrayList();
names.add("Izi");
names.add("Fritz");
Query q = sess.createQuery("from DomesticCat cat where cat.name in (:namesList)");
q.setParameterList("namesList", names);
List cats = q.list();
Summary
- Hibenate it’s a object/relational mapping tool, for object persisting and querying.
- The mapping from an object to a table is defined in a xml file, usually one file per class.
- The hibernate configuration is keeped in hibernate.cfg.xml file.
- <class> tag defines which object will map an table.
- <id> tag defines which object property will map the table primary-key.
- <property> tag defines all non-primary-key attributes of the object.
- <composite-id> is used, when the table that will be mapped, have a composite primary key. To use this tag, you must define a helper class that wraps all the attributes that compound the primary key.
- <many-to-many> tag defines a collection that will hold the 1-n relationship from one direction in a N-M relationship between tables. For a bidirectional link, you must define a many-to-many tag in the other class.
- <one-to-many> tag defines a collections that will hold the 1-n relationship between two tables.
- <one-to-one> tag defines a object that will hold the link for a 1-1 relationship between two tables.
- For specific queries, you can use the createQuery service from session object, and obtain an org.hibernate.Query object. Its recommended use the HQL, but its possible use native SQL.
External Links
Parts of this HOW-TO had been extracted from: http://www.hibernate.org/hib_docs/v3/reference/en/html/index.html


0 comments:
Post a Comment