More Books
Hibernate: A J2EE Developer's Guide
Hibernate: A J2EE™ Developer's Guide
Table of Contents
Copyright
Acknowledgments
About the Author
Preface
Required Skills
Roadmap
Chapter 1. Overview
Why Object/Relational Mapping?
What Is Hibernate?
Comparing JDBC to Hibernate
Hibernate's Mapping System
Other Java/Database Integration Solutions
How to Obtain and Install
Supported Databases
Chapter 2. Getting Oriented
Application Architecture
Mapping Files
Generating Java Source
Application Configuration
Web Application
JSP Interface
Chapter 3. Starting from Java
Java Object Model
Generated Mapping Files
Generated Schema
Working with Artifacts and Owners
Chapter 4. Starting from an Existing Schema
Initial Schema
Using Middlegen
Generated Mapping Files
Generated Java
Working with the Database
Chapter 5. Mapping Files
Basic Structure
Mapping File Reference
Chapter 6. Persistent Objects
Sessions
Objects and Identity
Life-Cycle Methods
Chapter 7. Relationships
Database Relationships
Java Collection Relationships
Java Class Relationships
Any-Based Relationships
Bi-directional Relationships
Chapter 8. Queries
HQL
HQL Reference
Select
From
Where
Group By
Having
Order By
Criteria Queries
Native SQL Queries
Chapter 9. Transactions
Introduction to Transactions
Optimistic and Pessimistic Locking
Chapter 10. Performance
Finding and Solving Problems
Queries
Inserts
Connection Pooling
Caching
Chapter 11. Schema Management
Updating an Existing Schema
Generating Update and Drop Scripts
Chapter 12. Best Practices, Style Guide, Tips and Tricks
Reducing Code with Inversion of Control
Reducing Session Creation Impact with ThreadLocal
Using Hibernate as an EJB BMP Solution
Integrating with Other Technologies
Applications That Use Hibernate
Strategies for Getting Started
Chapter 13. Future Directions
Hibernate 3.0
EJB 3.0
Here and Now
Index
SYMBOL
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R
S
T
U
V
W
X

Sessions

The core of database operations in Hibernate is the lightweight, not-thread-safe net.sf.hibernate.Session object. Session objects are obtained from instances of the heavyweight, thread-safe net.sf.hibernate.Session Factory. A SessionFactory is obtained from a Configuration, in turn a heavy, expensive-to-create, one-time use class. Typically, you will use a singleton pattern to obtain a SessionFactory, and the logic for initializing the SessionFactory will include the use of a Configuration object. For more details on the relationship between Configuration, Session Factory, Session, transactions, and the ordering of statements within a particular session, see Chapter 9.

Session object are lightweight and not-thread-safe, and typically are created and disposed of frequently.

Setting up the Configuration

A Configuration object is created once, typically as part of the launching process of your application. The primary task of a Configuration object is to load and process the various *.hbm.xml files, binding them to the relevant persistent code, and returning an appropriately configured SessionFactory.

The list of methods in Table 6.1 is not complete. For a complete list, refer to the Hibernate javadoc (Hibernate_install_dir \doc\api\index.html). The summary shown in Table 6.1 illustrates the range of options available for storing configuration data.

Table 6.1. Adding Mapping Files

addClass()

Uses the class name and package statement to load the *.hbm.xml.

addDirectory()

Loads all of the *.hbm.xml files from a particular directory.

addDocument()

Adds a mapping as already loaded by a DOM object.

addFile()

Reads the mapping from a particular file (may have an extension other than *.hbm.xml).

addInputStream()

Reads the mapping from a supplied java.io.InputStream.

addJar()

Reads the *.hbm.xml file from a specified JAR file.

addUrl()

Reads the mapping file from a specified URL.

addXML()

Reads the mapping as passed by an arbitrary String (useful for dynamic mapping generation, perhaps by a Hibernate-enabled tool).

configure()

Reads the mapping files and properties as specified by a hibernate.cfg.xml file.

configure(File)

Reads mapping files and properties as specified by a specific file (as per a hibernate.cfg.xml file).

configure(String)

Reads mapping files and properties as specified by a resource path (as per hibernate.cfg.xml file).

configure(URL)

Reads mapping files and properties as specified by a URL resource (as per hibernate.cfg.xml file).


If you are using a hibernate.cfg.xml (or one of the configure() methods), you will define the connectivity properties in that file. Otherwise, these properties may be set at runtime using a java.util.Properties object via Configuration.addProperties(), Configuration .setProperties(), or individually using Configuration.set Property().

Alternatively, the connectivity may be configured using a hibernate .properties file placed on the class path.

Tables 6.3 through 6.6 describe additional options for the configuration of Hibernate.

Listing 6.1. Sample Minimal Properties File
hibernate.connection.driver_class=com.mysql.jdbc.Driver
hibernate.connection.url=jdbc:mysql://localhost/hibernate
hibernate.connection.username=root
hibernate.connection.password=
hibernate.dialect=net.sf.hibernate.dialect.MySQLDialect
hibernate.show_sql=true

Table 6.3. JNDI Datasource Connectivity Configuration Properties

Property Name

Purpose

hibernate.connection.datasource

datasource JNDI name

hibernate.jndi.url

URL of the JNDI provider (optional)

hibernate.jndi.class

class of the JNDI InitialContextFactory (optional)

hibernate.connection.username

database user (optional)

hibernate.connection.password

database user password (optional)

hibernate.jndi.<propertyName>

Pass the property <propertyName> to the JNDI InitialContextFactory (optional)


Table 6.6. Supported JTA Transaction Managers

Application Server

Transaction Factory

JBoss

net.sf.hibernate.transaction.JBoss TransactionManagerLookup

Weblogic

net.sf.hibernate.transaction.Weblogic TransactionManagerLookup

WebSphere

net.sf.hibernate.transaction.WebSphere TransactionManagerLookup

Orion

net.sf.hibernate.transaction.Orion TransactionManagerLookup

Resin

net.sf.hibernate.transaction.Resin TransactionManagerLookup

JOTM

net.sf.hibernate.transaction.JOTM TransactionManagerLookup

JOnAS

net.sf.hibernate.transaction.JOnAS TransactionManagerLookup

JRun4

net.sf.hibernate.transaction. JRun4TransactionManagerLookup

Borland ES[*]

net.sf.hibernate.transaction .BESTransactionManagerLookup

JNDI[*]

net.sf.hibernate.transaction .JNDITransactionManagerLookup

Sun ONE Application Server 7[*]

net.sf.hibernate.transaction.Sun ONETransactionManagerLookup


[*] Available in Hibernate 2.1.5

If you wish to use Hibernate (perhaps in conjunction with a pooling driver, as described in Chapter 10) to manage obtaining and releasing connections, you will want to set the properties as shown in Table 6.2.

Table 6.2. JDBC Connectivity Configuration Properties

Property Name

Purpose

hibernate.connection.driver_class

JDBC driver class

hibernate.connection.url

JDBC URL

hibernate.connection.username

Database user account

hibernate.connection.password

Database user password

hibernate.connection.pool_size

Maximum number of pooled connections


If you wish to use Hibernate in the context of an application server, you will probably rely on the application server's built-in JNDI-based datasource mechanism for managing connections. Table 6.3 shows the properties that must be set to allow Hibernate to connect to JNDI datasources.

Hibernate also offers a wide variety of configurable options, as shown in Table 6.4. You can use them to provide additional database connectivity options or to set performance options.

Table 6.4. Miscellaneous Optional Hibernate Configuration Properties

Property Name

Purpose

Value

hibernate.dialect

The class name of a Hibernate Dialect enables certain platform-dependent features.

full.classname.of.Dialect (see Table 6.5)

hibernate.default_schema

Qualify unqualified table names with the given schema/table space in generated SQL.

SCHEMA_NAME

hibernate.session_factory_name

Bind this name to the SessionFactory.

jndi/composite/name

hibernate.use_outer_join

Enables outer join fetching. For more information on outer joins, see Chapter 8.

true | false

hibernate.max_fetch_depth

Set a maximum "depth" for the outer join fetch tree. For more information on outer joins, see Chapter 8.

recommended values between 0 and 3

hibernate.jdbc.fetch_size

A nonzero value determines the JDBC fetch size (call Statement.setFetchSize()).

 

hibernate.jdbc.batch_size

A nonzero value enables use of JDBC2 batch updates by Hibernate.

recommended values between 5 and 30

hibernate.jdbc.use_scrollable_resultset

Enables use of JDBC2 scrollable result sets by Hibernate. This property is only necessary when using user-supplied connections. In all other instances Hibernate uses connection metadata.

true | false

hibernate.jdbc. use_streams_for_binary

Use streams when writing / reading binary or serializable types to/from JDBC. System-level property.

true | false

hibernate.cglib.use_reflection_optimizer

Enables use of CGLIB instead of runtime reflection (system-level property, default is to use CGLIB where possible). Defaults to true. Setting this to false can sometimes be useful when troubleshooting.

true | false

hibernate.connection Isolation

Set the JDBC transaction isolation level (optional)

1, 2, 4, 8 (as defined by java.sql.Connection)

hibernate.connection.<propertyName>

Pass the JDBC property <propertyName> to DriverManager. getConnection().

 

hibernate.connection.provider_class

Class name of a custom ConnectionProvider.

Fully qualified class name of an implementation of net.sf.hibernate.connection.ConnectionProvider

hibernate.cache.provider_class

Class name of a custom CacheProvider. See Chapter 10 for more information

Fully qualified class name of an implementation of net.sf.hibernate. cache.CacheProvider

hibernate.cache. use_minimal_puts

Optimize second-level cache operation to minimize writes, at the cost of more frequent reads (useful for clustered caches). See Chapter 10 for more information.

TRue|false

hibernate.cache. use_query_cache

Enable the query cache.

true|false

hibernate.cache. region_prefix

Prefix to use for second-level cache region names.

prefix

hibernate.transaction.factory_class

Class name of a transactionFactory to use with Hibernate Transaction API.

Use JDBC Transactions: net.sf.hibernate.transaction.JDBCTransactionFactory To use JTA Transactions net.sf.hibernate. transaction. JTATransactionFactory

jta.UserTransaction

JNDI name used by JTATransactionFactory to obtain the JTA UserTransaction

jndi/composite/name

hibernate.transaction.manager_lookup_class

Class name of a TRansactionManager Lookuprequired when JVM-level caching is enabled in a JTA environment

A value from Table 6.6 or a custom implementation of net.sf.hibernate.transaction.Transaction ManagerLookup

hibernate.query.substitutions

Mapping from tokens in Hibernate queries to SQL tokens (tokens might be function or literal names, for example)

hqlLiteral=SQL_LITERAL, hqlFunction=SQLFUNC

hibernate.show_sql

Write all SQL statements to console (a minimal alternative to the use of the logging functionality as described in Chapter 10)

true | false

hibernate.hbm2ddl.auto

Automatically export schema DDL.

update | create | create-drop


Typically, Hibernate is able to automatically detect the proper SQL dialect to use based on the JDBC driver. You may wish to manually set the SQL dialect, either to ensure the proper dialect or for schema management (as described in Chapter 11). Table 6.5 shows the possible values for hibernate.dialect.

Table 6.5. Supported SQL Dialects

Database

Dialect

DB2

net.sf.hibernate.dialect.DB2Dialect

DB2400

net.sf.hibernate.dialect.DB2400Dialect

Firebird

net.sf.hibernate.dialect.FirebirdDialect

FrontBase

net.sf.hibernate.dialect.FrontBaseDialect

Generic

net.sf.hibernate.dialect.GenericDialect

HypersonicSQL

net.sf.hibernate.dialect.HSQLDialect

Informix

net.sf.hibernate.dialect.InformixDialect

Ingres

net.sf.hibernate.dialect.IngresDialect

Interbase

net.sf.hibernate.dialect.InterbaseDialect

Mckoi SQL

net.sf.hibernate.dialect.MckoiDialect

Microsoft SQL Server

net.sf.hibernate.dialect.SQLServerDialect

MySQL

net.sf.hibernate.dialect.MySQLDialect

Oracle 9

net.sf.hibernate.dialect.Oracle9Dialect

Oracle

net.sf.hibernate.dialect.OracleDialect

Pointbase

net.sf.hibernate.dialect.PointbaseDialect

PostgreSQL

net.sf.hibernate.dialect.PostgreSQLDialect

Progress

net.sf.hibernate.dialect.ProgressDialect

SAP DB

net.sf.hibernate.dialect.SAPDBDialect

Sybase Anywhere

net.sf.hibernate.dialect.SybaseAnywhereDialect

Sybase 11.9.2

net.sf.hibernate.dialect .Sybase11_9_2Dialect

Sybase

net.sf.hibernate.dialect.SybaseDialect


Similarly, Hibernate offers support for JTA to manage your transactions. Table 6.6 shows the supported JTA transaction managers.

Obtaining the Session

Listing 6.2 shows an example of the use of the Configuration object to obtain a Session.

Listing 6.2. Typical Configuration Initialization
Configuration myConfiguration = new Configuration();

myConfiguration.addClass(Exam.class);
myConfiguration.addClass(Examresult.class);
myConfiguration.addClass(Course.class);
myConfiguration.addClass(Student.class);

// Sets up the session factory (used in the rest
// of the application).
sessionFactory = myConfiguration.buildSessionFactory();

Once a SessionFactory has been obtained, you will store this object somewhere (typically in a static variable) and use it throughout the rest of your application.

Most commonly, openSession()is the only method of a SessionFactory that you will use. The no-argument version will use the mechanism as described by the Configuration properties to connect to the database (usually the preferred mechanism).

Once you have obtained a session, you have access to a variety of options for manipulating data, as shown in Figure 6.2.

Figure 6.2. Session


Generally speaking, the methods of a session are concerned with the traditional create, retrieve, update, and delete operations.

TRANSACTIONS

You'll want to wrap calls to the session using method beginTransaction() and then calling transaction.commit() when finished. Note that Hibernate will sometimes not actually flush SQL statements to the transaction until after the transaction is committed, which can on occasion lead to unwanted reordering of the SQL execution. For more information on flushing statements as part of a transaction, see Chapter 9.

Figure 6.1. SessionFactory


In other words, Hibernate may not execute the SQL in the order in which you issue the commands in the session. To avoid this, use the session.flush() when executing statements to ensure the proper order of execution (see Chapter 9 for more information).

As shown in examples throughout this book, make sure that you are using try/catch/finally blocks to properly commit or roll back transactions as needed and that sessions are closed properly.


Creating Objects

Creating objects in Hibernate is straightforward. Simply allocate the persistent objects using new, set the properties, and then use Session.save().

A complication may arise when you are creating objects with collections, however. The important thing to remember is that collection elements added to a parent where the collection is set to inverse="true" are not persisted automaticallyyou must manually save them and add them to the inverse="false" end of the relationship to persist them.

Unfortunately, Hibernate does not allow both sides of a collection to be set to inverse="false". This means that you must think carefully about your needs in order to decide which side of an association to map as inverse="false". To complicate matters somewhat, this is also true when objects are deletedit's easy to manually delete the associations, as long as the proper side of the relationship is used. You can work around this when working with the "wrong end" of a bi-directional association by using the outer-join-as-inverse-of-inner technique described in Chapter 8.

Session methods that are useful when you are saving objects are shown in Table 6.7.

Table 6.7. Object Creation

save(Object object)

Saves the object, and sets the id to the generated identifier.

save(Object object, Serializable id)

Saves the object using the specified identifier. Useful if you use an alternative mechanism for managing identifiers (for example, if you are relying on your EJB container for identifiers).

saveOrUpdate(Object object)

Will save the object if no identifier is present, or update the object if the identifier is valid.

saveOrUpdateCopy (Object object)

Will generate a new object either way, and will make a copy of the object from the current version if one already exists.

saveOrUpdateCopy(Object object, Serializable id)

Will generate a new object with the specified identifier either way, and will make a copy of the object from the current version if one already exists.


Finding Objects

Hibernate offers a wide variety of options for retrieving objects from the database, including HQL, the Criteria API, and using SQL directly (or even mixing different strategies). For more information, see Chapter 8.

The various methods available for retrieving objects via HQL (as described in Chapter 8) are shown in Table 6.8. These methods are routinely expected to return no results (for example, a query for the number of students in a class may legitimately return no students if there are none enrolled). If you wish to view the inability to retrieve an object as an application failure use load(), described under Refreshing Objects below, instead of get().

Table 6.8. Finding Objects via HQL

createQuery(String)

Use to retrieve a Query object based on an HQL query.

find(String)

Returns a java.util.List containing the results of the HQL query.

find(String query, Object[] values, Type[] types)

Returns a java.util.List, using the values and types provided as bound parameters.

find(String query, Object value, Type type)

Returns a java.util.List, using the value and type provided as a bound parameter.

get(Class, Serializable)

Retrieves a single persistent object, or null if not present.

get(Class, Serializable, LockMode)

Retrieves a single persistent object with the specified lock mode. Not normally necessaryHibernate manages lock modes automatically.

getNamedQuery(String)

Gets a query based on a query specified in the *.hbm.xml mapping file.

iterate(String)

Executes the query, returning the results lazily, loading each object with a new SELECT statement.

iterate(String query, Object[] values, Type[] types)

Executes the query, returning the results lazily, loading each object with a new SELECT statement. Values and types are used to set bound parameters.

iterate(String query, Object value, Type type)

Executes the query, returning the results lazily, loading each object with a new SELECT statement. Value and type are used to set a bound parameter.


Some of these methods return a net.sf.hibernate.Query object instead of an immediate set of results. The net.sf.hibernate.Query object can be used to bind parameters and configure other aspects of the query before execution. The advantages of using an intermediate Query object include:

  • Selecting a portion of the results (using setMaxResults() and set-FirstResult())

  • Required to use named query parameters

  • Retrieve the results as ScrollableResults

You may notice that there are two seemingly similar methods for retrieving results: find() and iterate(). The find() method executes immediately, whereas iterator() executes on demand. Therefore, find() methods typically perform better than iterator() methods. If you fear you may retrieve too many results with find(), you would probably be better off using create-Query() to configure the returned results.

ONE POSSIBLE GOOD USE FOR ITERATE

Technically, the iterate() method returns only the object identifiers and a proxy, whereas the find() method performs a full retrieval. Thus the one time an iterate() could outperform a find() is if you are only interested in the returned primary keys, not the full objects.


In addition to HQL, Chapter 8 describes the use of both the Hibernate Criteria API and raw SQL to access data. Table 6.9 shows these methods.

Table 6.9. Finding Objects via Criteria and SQL

createCriteria(Class)

Pass in a persistent class to use as the base of the criteria, and then use additional methods to modify the query, as shown in Chapter 8.

createSQLQuery(String sql, String[] returnAliases, Class[] returnClasses)

Use to execute a query with multiple alias and classes, as shown in Chapter 8.

createSQLQuery(String sql, String returnAlias, Class returnClass)

Use to execute a query with a single alias and class, as shown in Chapter 8.


Refreshing Objects

Sometimes, you know that an object exists, but only have part of the object's information. The most typical example would be a situation in which you know an object's identify reference but don't have the rest of the object information (this is shown in the sample application in Chapter 2). Given an identifier, the load() method can be used to retrieve the rest of the information about an object. The difference between a load() and a get() is that a load() will fail with an exception if the object is not found, whereas a get() will simply return null.

Within the context of a single session, you may expect that the mere act of updating or saving the object will modify the properties. For example, a trigger may modify the data written to a particular column when a record is saved. The refresh() method is used in the context of a single Session to retrieve these changes. In practice, you'll probably use the load() method much more frequently than the refresh() method. The refresh() method can also be used to reassociate objects across transactions with a session (converting a transient object to a persistent object). This is especially important if you wish to take advantage of cascading operations, because Hibernate may not recognize cascading associations if objects are not properly refreshed. For an example of this, see Listing 2.17, Deleting an Author.

Table 6.10. Obtaining the Current Data

load(Class clazz, Serializable id)

Use to load an object at any time, given only an identifier.

load(Object object, Serializable id)

As load, with a specific identifier.

load(Class clazz, Serializable id, LockMode lockMode)

As load, with a specific identifier and lock mode.

refresh(Object object)

Only usable if the object has already been loaded by this session.

refresh(Object obj, LockMode lockMode)

Only usable if the object has already been loaded by this session


Deleting Objects

Deleting an object is largely a simple matter of passing the object to the session.delete(). You can use HQL to identify a range of records for higher-performance bulk deletes.

Table 6.11. shows the methods available for deleting data.

Table 6.11. Deleting Data

delete(Object object)

Removes a persistent instance from the database.

delete(String query)

Deletes all objects returned by the HQL query.

delete(String query, Object[] values, Type[] types)

Deletes all objects returned by the HQL query.

delete(String query, Object value, Type type)

Deletes all objects returned by the HQL query.


Updating Objects

Much like creating an object, updating an object is a matter of simply modifying a persistent object and passing it to Session.update(). Table 6.12 shows the methods available for updating data.

Table 6.12. Deleting Data

update(Object object)

Updates the persistent instance with the identifier of the given transient instance.

update(Object object, Serializable id)

Updates the persistent instance with the identifier as specified. Particularly useful if using EJB to manage identity.