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 thread-safe and should by design be used by only one thread at a time.
The Hibernate notion of a session is something between connection and transaction. It may be easier to think of a session as a cache or collection of loaded objects relating to a single unit of work. Hibernate can detect changes to the objects in this unit of work. We sometimes call the Session
a persistence manager because it's also the interface for persistence-related operations such as storing and retrieving objects. Note that a Hibernate session has nothing to do with the Web-tier HttpSession
. When we use the word session, we mean the Hibernate session. We sometimes use user session to refer to the HttpSession
object.
SessionFactory interface
The application obtains Session
instances from a SessionFactory
. Compared to the Session
interface, this object is much less exciting.
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 generate 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 interface
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 interface
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 interfaces
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 object-oriented criteria queries.
To help make application code less verbose, Hibernate provides some shortcut methods on the Session
interface that let you invoke a query in one line of code. We won't use these shortcuts; instead, we'll always use the Query
interface.
A Query
instance is lightweight and can't be used outside the Session
that created it.
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 Hibernate team was heavily influenced by other ORM solutions that have similar callback interfaces. Later, they realized that having the persistent classes implement Hibernate-specific interfaces probably isn't a good idea, because doing so pollutes our persistent classes with nonportable code. Since these approaches are no longer favored, we don't discuss them.
The Interceptor
interface was introduced to allow the application to process callbacks without forcing the persistent classes to implement Hibernate-specific APIs. Implementations of the Interceptor
interface are passed to the persistent instances as parameters.
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:
- Primary key generation (
IdentifierGenerator
interface) - SQL dialect support (
Dialect
abstract class) - Caching strategies (
Cache
andCacheProvider
interfaces) - JDBC connection management (
ConnectionProvider
interface) - Transaction management (
TransactionFactory
,Transaction
, andTransactionManagerLookup
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.
By now, you can see that before we can start writing any code that uses Hibernate, we must answer this question: How do we get a Session
to work with?
Basic configuration
We've looked at an example application and examined Hibernate's core interfaces. 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. Generally, Hibernate is used in two- and three-tiered client-server applications, with Hibernate deployed only on the server. The client application is usually a Web browser, but Swing and SWT (Simple Widget Toolkit) client applications aren't uncommon. Although we concentrate on multitiered Web applications, our explanations apply equally to other architectures, such as command line applications. It's important to understand the difference in configuring Hibernate for managed and nonmanaged 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.
- Nonmanaged 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 standalone desktop or command line application is also considered nonmanaged. Nonmanaged environments don't provide automatic transaction or resource management or security infrastructure. The application itself manages database connections and demarcates transaction boundaries.
Hibernate attempts to abstract the environment in which it's deployed. In the case of a nonmanaged environment, Hibernate handles transactions and JDBC connections (or delegates to application code that handles these concerns).
In managed environments, Hibernate integrates with container-managed transactions and datasources. Hibernate can be configured for deployment in both environments. In both managed and nonmanaged environments, the first thing you must do is start Hibernate. In practice, doing so is very easy: You have to create a SessionFactory
from a Configuration
.
Creating a SessionFactory
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.
The following code starts Hibernate:
Configuration cfg = new Configuration(); cfg.addResource("hello/Message.hbm.xml"); cfg.setProperties( System.getProperties() ); SessionFactory sessions = cfg.buildSessionFactory();
The location of the mapping file, Message.hbm.xml
, is relative to the root of the application classpath. For example, if the classpath is the current directory, the Message.hbm.xml
file must be in the hello
directory. XML mapping files must be placed in the classpath. In this example, we also use the system properties of the virtual machine to set all other configuration options (which might have been set before by application code or as startup options).
Method chaining |
---|
Method chaining is a programming style supported by many Hibernate interfaces. This style is more popular in Smalltalk than in Java and is considered by some people to be less readable and more difficult to debug than the more accepted Java style. However, it's very convenient in most cases. Most Java developers declare setter or adder methods to be of type void, meaning they return no value. In Smalltalk, which has no void type, setter or adder methods usually return the receiving object. This would allow us to rewrite the previous code example as follows: SessionFactory sessions = new Configuration() .addResource("hello/Message.hbm.xml") .setProperties( System.getProperties() ) .buildSessionFactory(); Notice that we didn't need to declare a local variable for the |
By convention, Hibernate XML mapping files are named with the .hbm.xml
extension. Another convention is to have one mapping file per class, rather than have all your mappings listed in one file (which is possible but considered bad style). Our "Hello World" example had only one persistent class, but let's assume we have multiple persistent classes, with an XML mapping file for each. Where should we put these mapping files?
The Hibernate documentation recommends that the mapping file for each persistent class be placed in the same directory as that class. For instance, the mapping file for the Message
class would be placed in the hello
directory in a file named Message.hbm.xml
. If we had another persistent class, it would be defined in its own mapping file. We suggest that you follow this practice. The monolithic metadata files encouraged by some frameworks, such as the struts-config.xml
found in Struts, are a major contributor to "metadata hell." You load multiple mapping files by calling addResource()
as often as you have to. Alternatively, if you follow the convention just described, you can use the method addClass()
, passing a persistent class as the parameter:
SessionFactory sessions = new Configuration() .addClass(org.hibernate.auction.model.Item.class) .addClass(org.hibernate.auction.model.Category.class) .addClass(org.hibernate.auction.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.
We've demonstrated the creation of a single SessionFactory
, which is all that most applications need. If another SessionFactory
is needed—if there are multiple databases, for example—you repeat the process. Each SessionFactory
is then available for one database and ready to produce Session
s to work with that particular database and a set of class mappings.
Of course, there is more to configuring Hibernate than just pointing to mapping documents. You also need to specify how database connections are to be obtained, along with various other settings that affect the behavior of Hibernate at runtime. The multitude of configuration properties may appear overwhelming (a complete list appears in the Hibernate documentation), but don't worry; most define reasonable default values, and only a handful are commonly required.
To specify configuration options, you may use any of the following techniques: