What is ORM?
In a nutshell, object/relational mapping is the automated (and transparent) persistence of objects in a Java application to the tables in a relational database, using metadata that describes the mapping between the objects and the database. ORM, in essence, works by (reversibly) transforming data from one representation to another.
Why ORM?
A supposed advantage of ORM is that it “shields” developers from “messy” SQL. This view holds that object-oriented developers can’t be expected to understand SQL or relational databases well and that they find SQL somehow offensive.
Hibernate
Hibernate is an open source ORM tool for Java. Hibernate lets you develop persistent classes following common Java idiom - including association, inheritance, polymorphism, composition and the Java collections framework.
Hibernate not only takes care of the mapping from Java classes to database tables (and from Java data types to SQL data types), but also provides data query and retrieval facilities and can significantly reduce development time otherwise spent with manual data handling in SQL and JDBC.
Hibernates goal is to relieve the developer from 95 percent of common data persistence related programming tasks.
Lets look at some of the benefits of ORM and Hibernate
- Productivity
Persistence-related code can be perhaps the most tedious code in a Java application. Hibernate eliminates much of the grunt work (more than you’d expect) and lets you concentrate on the business problem. No matter which application development strategy you prefer—top-down, starting with a domain model; or bottomup, starting with an existing database schema—Hibernate used together with the appropriate tools will significantly reduce development time.
- Maintainability
Fewer lines of code (LOC) makes the system more understandable since it emphasizes business logic rather than plumbing. Most important, a system with less code is easier to refactor.
- Performance
A common claim is that hand-coded persistence can always be at least as fast, and can often be faster, than automated persistence. This is true in the same sense that it’s true that assembly code can always be at least as fast as Java code, or a handwritten parser can always be at least as fast as a parser generated by YACC or ANTLR—in other words, it’s beside the point. The unspoken implication of the claim is that hand-coded persistence will perform at least as well in an actual application. But this implication will be true only if the effort required to implement at-least-as-fast hand-coded persistence is similar to the amount of effort involved in utilizing an automated solution.
Given a persistence task, many optimizations are possible. Some (such as query hints) are much easier to achieve with hand-coded SQL/JDBC. Most optimizations, however, are much easier to achieve with automated ORM. In a project with time constraints, hand-coded persistence usually allows you to make some optimizations, some of the time. Hibernate allows many more optimizations to be used all the time. Furthermore, automated persistence improves developer productivity so much that you can spend more time hand-optimizing the few remaining bottlenecks.
- Vendor Independence
An ORM abstracts your application away from the underlying SQL database and SQL dialect. In addition, database independence helps in development scenarios where developers use a lightweight local database but deploy for production on a different database.
Understanding the Architecture
The programming interfaces are the first thing you have to learn about Hibernate in order to use it in the persistence layer of your application.The Hibernate interfaces shown in figure may be approximately classified as follows:
- Interfaces called by applications to perform basic CRUD and querying operations.
These interfaces are the main point of dependency of application business/control logic on Hibernate. They include Session, Transaction, and Query.
- Interfaces called by application infrastructure code to configure Hibernate,
most importantly the Configuration class.
- Callback interfaces that allow the application to react to events occurring
inside Hibernate, such as Interceptor, Lifecycle, and Validatable.
- Interfaces that allow extension of Hibernate’s powerful mapping functionality,
such as UserType, CompositeUserType, and IdentifierGenerator. These interfaces are implemented by application infrastructure code (if necessary).
Hibernate makes use of existing Java APIs, including JDBC), Java Transaction API (JTA, and Java Naming and Directory Interface (JNDI). JDBC provides a rudimentary level of abstraction of functionality common to relational databases, allowing almost any database with a JDBC driver to be supported by Hibernate. JNDI and JTA allow Hibernate to be integrated with J2EE application servers. In this section, we don’t cover the detailed semantics of Hibernate
The Core Interfaces:
- Session
The Session interface is the primary interface used by Hibernate applications. An instance of Session is lightweight and is inexpensive to create and destroy. This is important because your application will need to create and destroy sessions all the time, perhaps on every request. Hibernate sessions are not threadsafe and should by design be used by only one thread at a time.
- Session Factory
The application obtains Session instances from a SessionFactory.
The SessionFactory is certainly not lightweight! It’s intended to be shared among many application threads. There is typically a single SessionFactory for the whole application—created during application initialization, for example. However, if your application accesses multiple databases using Hibernate, you’ll need a SessionFactory for each database.
The SessionFactory is certainly not lightweight! It’s intended to be shared among many application threads. There is typically a single SessionFactory for the whole application—created during application initialization, for example. However, if your application accesses multiple databases using Hibernate, you’ll need a SessionFactory for each database.
The SessionFactory caches generated SQL statements and other mapping metadata that Hibernate uses at runtime. It also holds cached data that has been read in one unit of work and may be reused in a future unit of work (only if class and collection mappings specify that this second-level cache is desirable).
- Configuration
The Configuration object is used to configure and bootstrap Hibernate. The application uses a Configuration instance to specify the location of mapping documents and Hibernate-specific properties and then create the SessionFactory.
Even though the Configuration interface plays a relatively small part in the total scope of a Hibernate application, it’s the first object you’ll meet when you begin using Hibernate.
- Transaction
The Transaction interface is an optional API. Hibernate applications may choose not to use this interface, instead managing transactions in their own infrastructure code. A Transaction abstracts application code from the underlying transaction implementation—which might be a JDBC transaction, a JTA UserTransaction, or even a Common Object Request Broker Architecture (CORBA) transaction— allowing the application to control transaction boundaries via a consistent API. This helps to keep Hibernate applications portable between different kinds of execution environments and containers.
- Query and Criteria
The Query interface allows you to perform queries against the database and control how the query is executed. Queries are written in HQL or in the native SQL dialect of your database. A Query instance is used to bind query parameters, limit the number of results returned by the query, and finally to execute the query.
The Criteria interface is very similar; it allows you to create and execute objectoriented criteria queries.
CallBack Interfaces:
Callback interfaces allow the application to receive a notification when something interesting happens to an object—for example, when an object is loaded, saved, or deleted. Hibernate applications don’t need to implement these callbacks, but they’re useful for implementing certain kinds of generic functionality, such as creating audit records.
The Lifecycle and Validatable interfaces allow a persistent object to react to events relating to its own persistence lifecycle. The persistence lifecycle is encompassed by an object’s CRUD operations.
The Interceptor interface was introduced to allow the application to process callbacks without forcing the persistent classes to implement Hibernate-specific APIs.
The Lifecycle and Validatable interfaces allow a persistent object to react to events relating to its own persistence lifecycle. The persistence lifecycle is encompassed by an object’s CRUD operations.
The Interceptor interface was introduced to allow the application to process callbacks without forcing the persistent classes to implement Hibernate-specific APIs.
Types:
A fundamental and very powerful element of the architecture is Hibernate’s notion of a Type. A Hibernate Type object maps a Java type to a database column type (actually, the type may span multiple columns). All persistent properties of persistent classes, including associations, have a corresponding Hibernate type. This design makes Hibernate extremely flexible and extensible.
There is a rich range of built-in types, covering all Java primitives and many JDK classes, including types for java.util.Currency, java.util.Calendar, byte, and java.io.Serializable.
Even better, Hibernate supports user-defined custom types. The interfaces UserType and CompositeUserType are provided to allow you to add your own types. You can use this feature to allow commonly used application classes such as Address, Name, or MonetaryAmount to be handled conveniently and elegantly. Custom types are considered a central feature of Hibernate, and you’re encouraged to put them to new and creative uses!
Extension Interfaces:
Much of the functionality that Hibernate provides is configurable, allowing you to choose between certain built-in strategies. When the built-in strategies are insufficient, Hibernate will usually let you plug in your own custom implementation by implementing an interface. Extension points include:
Much of the functionality that Hibernate provides is configurable, allowing you to choose between certain built-in strategies. When the built-in strategies are insufficient, Hibernate will usually let you plug in your own custom implementation by implementing an interface. Extension points include:
- Primary key generation (IdentifierGenerator interface)
- SQL dialect support (Dialect abstract class)
- Caching strategies (Cache and CacheProvider interfaces)
- JDBC connection management (ConnectionProvider interface)
- Transaction management (TransactionFactory, Transaction, and TransactionManagerLookup
interfaces)
- ORM strategies (ClassPersister interface hierarchy)
- Property access strategies (PropertyAccessor interface)
- Proxy creation (ProxyFactory interface)
Hibernate ships with at least one implementation of each of the listed interfaces, so you don’t usually need to start from scratch if you wish to extend the built-in functionality.
Basic Configuration
To use Hibernate in an application, you need to know how to configure it. Hibernate can be configured to run in almost any Java application and development environment.
It’s important to understand the difference in configuring Hibernate for managed and non-managed environments:
It’s important to understand the difference in configuring Hibernate for managed and non-managed environments:
- Managed environment—Pools resources such as database connections and
allows transaction boundaries and security to be specified declaratively (that is, in metadata). A J2EE application server such as JBoss, BEA WebLogic, or IBM WebSphere implements the standard (J2EE-specific) managed environment for Java.
- Non-managed environment—Provides basic concurrency management via
thread pooling. A servlet container like Jetty or Tomcat provides a nonmanaged server environment for Java web applications. A stand-alone desktop or command-line application is also considered non-managed. Nonmanaged environments don’t provide automatic transaction or resource management or security infrastructure. The application itself manages database connections and demarcates transaction boundaries.
Creatinng a Session Factory:
In order to create a SessionFactory, you first create a single instance of Configuration during application initialization and use it to set the location of the mapping files. Once configured, the Configuration instance is used to create the SessionFactory. After the SessionFactory is created, you can discard the Configuration class.
Configuration cfg = new Configuration();cfg.addResource("hello/ Message.hbm.xml");cfg.setProperties( System.getProperties() );SessionFactory sessions = cfg.buildSessionFactory();The following code starts Hibernate:
Alternarte way of Configuration
SessionFactory sessions = new Configuration().addClass(org.hibernate.auctio n.model.Item.class).addClass(org.hibernate.auctio n.model.Category.class).addClass(org.hibernate.auctio n.model.Bid.class).setProperties( System.getProperties() ).buildSessionFactory();
The addClass() method assumes that the name of the mapping file ends with the .hbm.xml extension and is deployed along with the mapped class file.
Configuration in a non_managed environment:In a non-managed environment, such as a servlet container, the application is responsible for obtaining JDBC connections. Hibernate is part of the application, so it’s responsible for getting these connections. You tell Hibernate how to get (or create new) JDBC connections. Generally, it isn’t advisable to create a connection each time you want to interact with the database. Instead, Java applications should use a pool of JDBC connections. There are three reasons for using a pool:
- Acquiring a new connection is expensive.
- Maintaining many idle connections is expensive.
- Creating prepared statements is also expensive for some drivers.
Figure 1 shows the role of a JDBC connection pool in a web application runtime environment. Since this non-managed environment doesn’t implement connection pooling, the application must implement its own pooling algorithm or rely upon a third-party library such as the open source C3P0 connection pool. Without Hibernate, the application code usually calls the connection pool to obtain JDBC connections and execute SQL statements.
With Hibernate, the picture changes: It acts as a client of the JDBC connection pool, as shown in figure 2. The application code uses the Hibernate Session and Query APIs for persistence operations and only has to manage database transactions, ideally using the Hibernate Transaction API.
Using a Connection Pool:
Hibernate defines a plugin architecture that allows integration with any connection pool. However, support for C3P0 is built in, so we’ll use that. Hibernate will set up the configuration pool for you with the given properties. An example of a hibernate.properties file using C3P0 is shown here.
Using a Connection Pool:
Hibernate defines a plugin architecture that allows integration with any connection pool. However, support for C3P0 is built in, so we’ll use that. Hibernate will set up the configuration pool for you with the given properties. An example of a hibernate.properties file using C3P0 is shown here.
hibernate.connection.driver_ class = org.postgresql.Driverhibernate.connection.url = jdbc:postgresql://localhost/ auctiondbhibernate.connection.username = auctionuser
hibernate.connection.password = secret
hibernate.dialect = net.sf.hibernate.dialect.Postg reSQLDialecthibernate.c3p0.min_size=5hibernate.c3p0.max_size=20hibernate.c3p0.timeout=300hibernate.c3p0.max_statements= 50hibernate.c3p0.idle_test_ period=3000
hibernate.connection.password = secret
hibernate.dialect = net.sf.hibernate.dialect.Postg
This code’s lines specify the following information, beginning with the first line:
- The name of the Java class implementing the JDBC Driver (the driver JAR
file must be placed in the application’s classpath).
- A JDBC URL that specifies the host and database name for JDBC connections.
- The database user name.
- The database password for the specified user.
- A Dialect for the database. Despite the ANSI standardization effort, SQL is
implemented differently by various databases vendors. So, you must specify a Dialect. Hibernate includes built-in support for all popular SQL databases, and new dialects may be defined easily.
- The minimum number of JDBC connections that C3P0 will keep ready.
- The maximum number of connections in the pool. An exception will be
thrown at runtime if this number is exhausted.
- The timeout period (in this case, 5 minutes or 300 seconds) after which an
idle connection will be removed from the pool.
- The maximum number of prepared statements that will be cached. Caching
of prepared statements is essential for best performance with Hibernate.
- The idle time in seconds before a connection is automatically validated.
Figure 1. JDBC connection pooling in a non-managed environment
Figure 2. Hibernate with a connection pool in a non-managed environment
Configuration in a managed environment:A managed environment handles certain cross-cutting concerns, such as application security (authorization and authentication), connection pooling, and transaction management. J2EE application servers are typical managed environments. Although application servers are generally designed to support EJBs, you can still take advantage of the other managed services provided, even if you don’t use EJB entity beans.
Hibernate is often used with session or message-driven EJBs, as shown in figure 3. EJBs call the same Hibernate APIs as servlets, JSPs, or stand-alone applications: Session, Transaction, and Query. The Hibernate-related code is fully portable between non-managed and managed environments. Hibernate handles the different connection and transaction strategies transparently.
Hibernate is often used with session or message-driven EJBs, as shown in figure 3. EJBs call the same Hibernate APIs as servlets, JSPs, or stand-alone applications: Session, Transaction, and Query. The Hibernate-related code is fully portable between non-managed and managed environments. Hibernate handles the different connection and transaction strategies transparently.
Figure 3.Hibernate in a managed environment with an application server
An application server exposes a connection pool as a JNDI-bound datasource, an instance of javax.jdbc.Datasource. You need to tell Hibernate where to find the datasource in JNDI, by supplying a fully qualified JNDI name. An example Hibernate configuration file for this scenario is shown here.
hibernate.connection.datasourc e = java:/comp/env/jdbc/AuctionDBhibernate.transaction.factory_ class = \net.sf.hibernate.transaction.J TATransactionFactoryhibernate.transaction.manager_ lookup_class = \net.sf.hibernate.transaction.J BossTransactionManagerLookuphibernate.dialect = net.sf.hibernate.dialect.Postg reSQLDialect
Hibernate Example
Hibernate applications define persistent classes that are “mapped” to database tables. Our “Hello World” example consists of one class and one mapping file. Let’s see what a simple persistent class looks like, how the mapping is specified, and some of the things we can do with instances of the persistent class using Hibernate.
The objective of our sample application is to store messages in a database and to retrieve them for display. The application has a simple persistent class, Message, which represents these printable messages.
Message.java: A Simple Persistent Class
package hello;public class Message { private Long id; private String text; private Message nextMessage;
private Message() {}
public Message(String text) { this.text = text; } public Long getId() { return id; } private void setId(Long id) { this.id = id; } public String getText() { return text; } public void setText(String text) { this.text = text; } public Message getNextMessage() { return nextMessage; } public void setNextMessage(Message nextMessage) { this.nextMessage = nextMessage; }}
private Message() {}
public Message(String text) { this.text = text; } public Long getId() { return id; } private void setId(Long id) { this.id = id; } public String getText() { return text; } public void setText(String text) { this.text = text; } public Message getNextMessage() { return nextMessage; } public void setNextMessage(Message nextMessage) { this.nextMessage = nextMessage; }}
Our Message class has three attributes: the identifier attribute, the text of the message, and a reference to another Message.Let’s save a new Message to the database:
Session session = getSessionFactory().openSessio n();Transaction tx = session.beginTransaction();Message message = new Message("Hello World");session.save(message);tx.commit();session.close();
This code calls the Hibernate Session and Transaction interfaces.It results in the execution of something similar to the following SQL:
insert into MESSAGES (MESSAGE_ID, MESSAGE_TEXT, NEXT_MESSAGE_ID)values (1, 'Hello World', null)
The next example retrieves all messages from the database, in alphabetical order, and prints them.
Session newSession = getSessionFactory().openSessio n();Transaction newTransaction = newSession.beginTransaction();List messages =newSession.find("from Message as m order by m.text asc");System.out.println( messages.size() + " message(s) found:" );for ( Iterator iter = messages.iterator(); iter.hasNext(); ) {Message message = (Message) iter.next();System.out.println( message.getText() );}newTransaction.commit();newSession.close();
The literal string "from Message as m order by m.text asc" is a Hibernate query, expressed in Hibernate’s own object-oriented Hibernate Query Language (HQL). This query is internally translated into the following SQL when find() is called:
select m.MESSAGE_ID, m.MESSAGE_TEXT, m.NEXT_MESSAGE_IDfrom MESSAGES morder by m.MESSAGE_TEXT asc
The code fragment prints
1 message(s) found:Hello World
If you’ve never used an ORM tool like Hibernate before, you were probably expecting to see the SQL statements somewhere in the code or metadata. They aren’t there. All SQL is generated at runtime (actually at startup, for all reusable SQL statements).
To allow this magic to occur, Hibernate needs more information about how the Message class should be made persistent. This information is usually provided in an XML mapping document. The mapping document defines, among other things, how properties of the Message class map to columns of the MESSAGES table. Let’s look at the mapping document listed here.
xml version="1.0"?>
"-//Hibernate/Hibernate Mapping DTD//EN" "http://hibernate. sourceforge.net/hibernate- mapping-2.0.dtd">
name="hello.Message" table="MESSAGES">
name="id" column="MESSAGE_ID">
class
"-//Hibernate/Hibernate Mapping DTD//EN" "http://hibernate.
Hibernate mapping files can be automatically generated from attributes directly embedded in the Java source code.
XDoclet is implemented as an Ant task that generates code or XML metadata as part of the build process. Creating the Hibernate XML mapping document with XDoclet is straightforward; instead of writing it by hand, we mark up the Java source code of our persistent class with custom Javadoc tags.
XDoclet is implemented as an Ant task that generates code or XML metadata as part of the build process. Creating the Hibernate XML mapping document with XDoclet is straightforward; instead of writing it by hand, we mark up the Java source code of our persistent class with custom Javadoc tags.
/**
*
* @hibernate.class
* table="MESSAGES"
*/
public class Message{.../**
* @hibernate.id
**generator-class="increment"
* column="MESSAGE_ID"
*/
public Long getId() {return id;}.../**
* @hibernate.property
*/
public String getText() {return text;}...}
*
* @hibernate.class
* table="MESSAGES"
*/
public class Message{.../**
* @hibernate.id
**generator-class="increment"
* column="MESSAGE_ID"
*/
public Long getId() {return id;}.../**
* @hibernate.property
*/
public String getText() {return text;}...}
Typical Hibernate programs begin with configuration that is required for Hibernate. Hibernate can be configured in two ways. Programmatically and Configuration file based. In Configuration file based mode, hibernate looks for configuration file “hibernate.cfg.xml” in the claspath. Based on the resource mapping provided hibernate creates mapping of tables and domain objects. In the programmatic configuration method, the details such as JDBC connection details and resource mapping details etc are supplied in the program using Configuration API.
Following example shows programmatic configuration of hibernate:
Following example shows programmatic configuration of hibernate:
Configuration config = new Configuration().addResource("hello/Message. hbm.xml")
Configuration config = new Configuration().addClass(org.hibernate.auctio n.model.Message.class).setProperty("hibernate. dialect", "org.hibernate.dialect. MySQLMyISAMDialect").setProperty("hibernate. connection.driver_class", " org.gjt.mm.mysql.Driver"). . . SessionFactory sessions = config.buildSessionFactory();
Following example shows Configuration file based configuration of hibernate:
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge. net/hibernate-configuration-3. 0.dtd">
name="show_sql">true</ property>name="hibernate.dialect">org. hibernate.dialect. MySQLMyISAMDialect name="hibernate.connection. driver_class">org.gjt.mm. mysql.Driver name="hibernate.connection. url">jdbc:mysql://localhost: 3306/applabs name="hibernate.connection. username">root name="hibernate.connection. password">r00Tp@$wd
resource="hello/Message.hbm. xml"/>
"http://hibernate.sourceforge.
Ways of Retrieving Objects in Hibernate
Retrieving persistent objects from the database is one of the most interesting (and complex) parts of working with Hibernate. Hibernate provides the following ways to get objects out of the database:
Retrieving Objects by Identifier:
The following Hibernate code snippet retrieves a User object from the database:
Retrieving Objects by Identifier:
The following Hibernate code snippet retrieves a User object from the database:
User user = (User) session.get(User.class, userID);
The get() method is special because the identifier uniquely identifies a single instance of a class. Hence it’s common for applications to use the identifier as a convenient handle to a persistent object. Retrieval by identifier can use the cache when retrieving an object, avoiding a database hit if the object is already cached.
Hibernate also provides a load() method:
User user = (User) session.load(User.class, userID);
The load() method is older; get() was added to Hibernate’s API due to user request. The difference is trivial:
- If load() can’t find the object in the cache or database, an exception is
thrown. The load() method never returns null. The get() method returns null if the object can’t be found.
Retrieving Objects using HQL(Hibernate Query Language):
Most of the time, you’ll only need to retrieve objects of a particular class and restrict by the properties of that class. For example, the following query retrieves a user by first name:
Query q = session.createQuery("from User u where u.firstname = :fname");q.setString("fname", "Max");List result = q.list();
After preparing query q, we bind the identifier value to a named parameter, fname. The result is returned as a List of User objects.
Retrieving Objects using QBC:
The Hibernate query by criteria (QBC) API lets you build a query by manipulating criteria objects at runtime. This approach lets you specify constraints dynamically without direct string manipulations, but it doesn’t lose much of the flexibility or power of HQL. On the other hand, queries expressed as criteria are often less readable than queries expressed in HQL.
Retrieving a user by first name is easy using a Criteria object:
Criteria criteria = session.createCriteria(User.cl ass);criteria.add( Expression.like("firstname", "Max") );List result = criteria.list();
A Criteria is a tree of Criterion instances. The Expression class provides static factory methods that return Criterion instances. Once the desired criteria tree is built, it’s executed against the database.
Many developers prefer QBC, considering it a more object-oriented approach. They also like the fact that the query syntax may be parsed and validated at compile time, whereas HQL expressions aren’t parsed until runtime.
Retrieving Objects using QBE:
As part of the QBC facility, Hibernate supports query by example (QBE). The idea behind QBE is that the application supplies an instance of the queried class with certain property values set (to nondefault values). The query returns all persistent instances with matching property values. QBE isn’t a particularly powerful approach, but it can be convenient for some applications. The following code snippet demonstrates a Hibernate QBE:
Retrieving Objects using QBE:
As part of the QBC facility, Hibernate supports query by example (QBE). The idea behind QBE is that the application supplies an instance of the queried class with certain property values set (to nondefault values). The query returns all persistent instances with matching property values. QBE isn’t a particularly powerful approach, but it can be convenient for some applications. The following code snippet demonstrates a Hibernate QBE:
User exampleUser = new User();exampleUser.setFirstname("Max" );Criteria criteria = session.createCriteria(User.cl ass);criteria.add( Example.create(exampleUser) );List result = criteria.list();
A typical use case for QBE is a search screen that allows users to specify a range of property values to be matched by the returned result set. This kind of functionality can be difficult to express cleanly in a query language; string manipulations would be required to specify a dynamic set of constraints.