This chapter covers
2.1 Starting a Hibernate project
2.1.3 Hibernate configuration and startup
The regular way of initializing Hibernate is to build a SessionFactory object from a Configuration object. If you like, you can think of the Configuration as an object representation of a configuration file (or a properties file) for Hibernate.
Let’s look at some variations before we wrap it up in the HibernateUtil class.
Building a SessionFactory
This is an example of a typical Hibernate startup procedure, in one line of code, using automatic configuration file detection:
SessionFactory sessionFactory =
new Configuration().configure().buildSessionFactory();
Wait—how did Hibernate know where the configuration file was located and which one to load?
When new Configuration() is called, Hibernate searches for a file named hibernate.properties in the root of the classpath. If it’s found, all hibernate.*
properties are loaded and added to the Configuration object.
When configure() is called, Hibernate searches for a file named hiber-nate.cfg.xml in the root of the classpath, and an exception is thrown if it can’t be found. You don’t have to call this method if you don’t have this configuration file, of course. If settings in the XML configuration file are duplicates of proper-ties set earlier, the XML settings override the previous ones.
The location of the hibernate.properties configuration file is always the root of the classpath, outside of any package. If you wish to use a different file or to have Hibernate look in a subdirectory of your classpath for the XML configura-tion file, you must pass a path as an argument of the configure() method:
SessionFactory sessionFactory = new Configuration() .configure("/persistence/auction.cfg.xml") .buildSessionFactory();
Finally, you can always set additional configuration options or mapping file loca-tions on the Configuration object programmatically, before building the Ses-sionFactory:
SessionFactory sessionFactory = new Configuration() .configure("/persistence/auction.cfg.xml")
.setProperty(Environment.DEFAULT_SCHEMA, "CAVEATEMPTOR") .addResource("auction/CreditCard.hbm.xml")
.buildSessionFactory();
Many sources for the configuration are applied here: First the hibernate.proper-ties file in your classpath is read (if present). Next, all settings from /persistence/
auction.cfg.xml are added and override any previously applied settings. Finally, an additional configuration property (a default database schema name) is set pro-grammatically, and an additional Hibernate XML mapping metadata file is added to the configuration.
You can, of course, set all options programmatically, or switch between different XML configuration files for different deployment databases. There is effectively no
limitation on how you can configure and deploy Hibernate; in the end, you only need to build a SessionFactory from a prepared configuration.
NOTE 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 conve-nient in many cases, such as for the configuration snippets you’ve seen in this section. Here is how it works: Most Java developers declare setter or adder methods to be of type void, meaning they return no value; but in Smalltalk, which has no void type, setter or adder methods usually return the receiving object. We use this Smalltalk style in some code examples, but if you don’t like it, you don’t need to use it. If you do use this coding style, it’s better to write each method invocation on a different line. Oth-erwise, it may be difficult to step through the code in your debugger.
Now that you know how Hibernate is started and how to build a SessionFactory, what to do next? You have to create a configuration file for Hibernate.
Creating an XML configuration file
Let’s assume you want to keep things simple, and, like most users, you decide to use a single XML configuration file for Hibernate that contains all the configura-tion details.
We recommend that you give your new configuration file the default name hibernate.cfg.xml and place it directly in the source directory of your project, out-side of any package. That way, it will end up in the root of your classpath after compilation, and Hibernate will find it automatically. Look at the file in listing 2.4.
<!DOCTYPE hibernate-configuration SYSTEM
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">
org.hsqldb.jdbcDriver </property>
<property name="hibernate.connection.url">
jdbc:hsqldb:hsql://localhost </property>
<property name="hibernate.connection.username">
sa </property>
Listing 2.4 A simple Hibernate XML configuration file
<property name="hibernate.dialect">
org.hibernate.dialect.HSQLDialect </property>
<!-- Use the C3P0 connection pool provider -->
<property name="hibernate.c3p0.min_size">5</property>
<property name="hibernate.c3p0.max_size">20</property>
<property name="hibernate.c3p0.timeout">300</property>
<property name="hibernate.c3p0.max_statements">50</property>
<property name="hibernate.c3p0.idle_test_period">3000</property>
<!-- Show and print nice SQL on stdout -->
<property name="show_sql">true</property>
<property name="format_sql">true</property>
<!-- List of XML mapping files -->
<mapping resource="hello/Message.hbm.xml"/>
</session-factory>
</hibernate-configuration>
The document type declaration is used by the XML parser to validate this document against the Hibernate configuration DTD. Note that this isn’t the same DTD as the one for Hibernate XML mapping files. Also note that we added some line breaks in the property values to make this more readable—you shouldn’t do this in your real configuration file (unless your database username contains a line break).
First in the configuration file are the database connection settings. You need to tell Hibernate which database JDBC driver you’re using and how to connect to the database with a URL, a username, and a password (the password here is omitted, because HSQLDB by default doesn’t require one). You set a Dialect, so that Hibernate knows which SQL variation it has to generate to talk to your database;
dozens of dialects are packaged with Hibernate—look at the Hibernate API docu-mentation to get a list.
In the XML configuration file, Hibernate properties may be specified without the hibernate prefix, so you can write either hibernate.show_sql or just show_sql. Property names and values are otherwise identical to programmatic configuration properties—that is, to the constants as defined in org.hiber-nate.cfg.Environment. The hibernate.connection.driver_class property, for example, has the constant Environment.DRIVER.
Before we look at some important configuration options, consider the last line in the configuration that names a Hibernate XML mapping file. The Config-uration object needs to know about all your XML mapping files before you build the SessionFactory. A SessionFactory is an object that represents a particular
Hibernate configuration for a particular set of mapping metadata. You can either list all your XML mapping files in the Hibernate XML configuration file, or you can set their names and paths programmatically on the Configuration object. In any case, if you list them as a resource, the path to the mapping files is the relative location on the classpath, with, in this example, hello being a package in the root of the classpath.
You also enabled printing of all SQL executed by Hibernate to the console, and you told Hibernate to format it nicely so that you can check what is going on behind the scenes. We’ll come back to logging later in this chapter.
Another, sometimes useful, trick is to make configuration options more dynamic with system properties:
...
<property name="show_sql">${displaysql}</property>
...
You can now specify a system property, such as with java -displaysql=true, on the command line when you start your application, and this will automatically be applied to the Hibernate configuration property.
The database connection pool settings deserve extra attention.
The database connection pool
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 connections.
Each application thread that needs to do work on the database requests a connec-tion from the pool and then returns it to the pool when all SQL operations have been executed. The pool maintains the connections and minimizes the cost of opening and closing connections.
There are three reasons for using a pool:
■ Acquiring a new connection is expensive. Some database management sys-tems even start a completely new server process for each connection.
■ Maintaining many idle connections is expensive for a database manage-ment system, and the pool can optimize the usage of idle connections (or disconnect if there are no requests).
■ Creating prepared statements is also expensive for some drivers, and the connection pool can cache statements for a connection across requests.
Figure 2.2 shows the role of a connection pool in an unmanaged application run-time environment (that is, one without any application server).
With no application server to provide a connection pool, an application either implements its own pooling algorithm or relies on a third-party library such as the open source C3P0 connection pooling software. Without Hibernate, the applica-tion code calls the connecapplica-tion pool to obtain a JDBC connection and then exe-cutes SQL statements with the JDBC programming interface. When the application closes the SQL statements and finally closes the connection, the pre-pared statements and connection aren’t destroyed, but are returned to the pool.
With Hibernate, the picture changes: It acts as a client of the JDBC connection pool, as shown in figure 2.3. The application code uses the Hibernate Session and Query API for persistence operations, and it manages database transactions (probably) with the Hibernate TransactionAPI.
Hibernate defines a plug-in architecture that allows integration with any con-nection-pooling software. However, support for C3P0 is built in, and the software comes bundled with Hibernate, so you’ll use that (you already copied the c3p0.jar file into your library directory, right?). Hibernate maintains the pool for you, and configuration properties are passed through. How do you configure C3P0 through Hibernate?
main()
Nonmanaged JSE environment
Figure 2.2 JDBC connection pooling in a nonmanaged environment
main()
Nonmanaged JSE environment
Figure 2.3 Hibernate with a connection pool in a nonmanaged environment
One way to configure the connection pool is to put the settings into your hibernate.cfg.xml configuration file, like you did in the previous section.
Alternatively, you can create a hibernate.properties file in the classpath root of the application. An example of a hibernate.properties file for C3P0 is shown in listing 2.5. Note that this file, with the exception of a list of mapping resources, is equivalent to the configuration shown in listing 2.4.
hibernate.connection.driver_class = org.hsqldb.jdbcDriver
This is the minimum number of JDBC connections that C3P0 keeps ready at all times.
This is the maximum number of connections in the pool. An exception is thrown at runtime if this number is exhausted.
You specify the timeout period (in this case, 300 seconds) after which an idle con-nection is removed from the pool.
A maximum of 50 prepared statements will be cached. Caching of prepared state-ments is essential for best performance with Hibernate.
This is the idle time in seconds before a connection is automatically validated.
Specifying properties of the form hibernate.c3p0.* selects C3P0 as the connec-tion pool (the c3p0.max_size option is needed—you don’t need any other switch to enable C3P0 support). C3P0 has more features than shown in the previous example; refer to the properties file in the etc/ subdirectory of the Hibernate dis-tribution to get a comprehensive example you can copy from.
The Javadoc for the class org.hibernate.cfg.Environment also documents every Hibernate configuration property. Furthermore, you can find an up-to-date table with all Hibernate configuration options in the Hibernate reference
Listing 2.5 Using hibernate.properties for C3P0 connection pool settings
B
documentation. We’ll explain the most important settings throughout the book, however. You already know all you need to get started.
FAQ Can I supply my own connections? Implement the org.hibernate.connec-tion.ConnectionProvider interface, and name your implementation with the hibernate.connection.provider_class configuration option.
Hibernate will now rely on your custom provider if it needs a database connection.
Now that you’ve completed the Hibernate configuration file, you can move on and create the SessionFactory in your application.
Handling the SessionFactory
In most Hibernate applications, the SessionFactory should be instantiated once during application initialization. The single instance should then be used by all code in a particular process, and any Session should be created using this single SessionFactory. The SessionFactory is thread-safe and can be shared; a Ses-sion is a single-threaded object.
A frequently asked question is where the factory should be stored after cre-ation and how it can be accessed without much hassle. There are more advanced but comfortable options such as JNDI and JMX, but they’re usually available only in full Java EE application servers. Instead, we’ll introduce a pragmatic and quick solution that solves both the problem of Hibernate startup (the one line of code) and the storing and accessing of the SessionFactory: you’ll use a static global variable and static initialization.
Both the variable and initialization can be implemented in a single class, which you’ll call HibernateUtil. This helper class is well known in the Hibernate com-munity—it’s a common pattern for Hibernate startup in plain Java applications without Java EE services. A basic implementation is shown in listing 2.6.
package persistence;
import org.hibernate.*;
import org.hibernate.cfg.*;
public class HibernateUtil {
private static SessionFactory sessionFactory;
static { try {
sessionFactory=new Configuration() .configure()
Listing 2.6 The HibernateUtil class for startup and SessionFactory handling
.buildSessionFactory();
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
} }
public static SessionFactory getSessionFactory() { // Alternatively, you could look up in JNDI here return sessionFactory;
}
public static void shutdown() {
// Close caches and connection pools getSessionFactory().close();
} }
You create a static initializer block to start up Hibernate; this block is executed by the loader of this class exactly once, on initialization when the class is loaded. The first call of HibernateUtil in the application loads the class, builds the Session-Factory, and sets the static variable at the same time. If a problem occurs, any Exception or Error is wrapped and thrown out of the static block (that’s why you catch Throwable). The wrapping in ExceptionInInitializerError is mandatory for static initializers.
You’ve created this new class in a new package called persistence. In a fully featured Hibernate application, you often need such a package—for example, to wrap up your custom persistence layer interceptors and data type converters as part of your infrastructure.
Now, whenever you need access to a Hibernate Session in your application, you can get it easily with HibernateUtil.getSessionFactory().openSession(), just as you did earlier in the HelloWorld main application code.
You’re almost ready to run and test the application. But because you certainly want to know what is going on behind the scenes, you’ll first enable logging.
Enabling logging and statistics
You’ve already seen the hibernate.show_sql configuration property. You’ll need it continually when you develop software with Hibernate; it enables logging of all generated SQL to the console. You’ll use it for troubleshooting, for performance tuning, and to see what’s going on. If you also enable hibernate.format_sql, the output is more readable but takes up more screen space. A third option you haven’t set so far is hibernate.use_sql_comments—it causes Hibernate to put
comments inside all generated SQL statements to hint at their origin. For exam-ple, you can then easily see if a particular SQL statement was generated from an explicit query or an on-demand collection initialization.
Enabling the SQL output to stdout is only your first logging option. Hiber-nate (and many other ORM implementations) execute SQL statements asynchro-nously. An INSERT statement isn’t usually executed when the application calls session.save(), nor is an UPDATE immediately issued when the application calls item.setPrice(). Instead, the SQL statements are usually issued at the end of a transaction.
This means that tracing and debugging ORM code is sometimes nontrivial. In theory, it’s possible for the application to treat Hibernate as a black box and ignore this behavior. However, when you’re troubleshooting a difficult problem, you need to be able to see exactly what is going on inside Hibernate. Because Hibernate is open source, you can easily step into the Hibernate code, and occa-sionally this helps a great deal! Seasoned Hibernate experts debug problems by looking at the Hibernate log and the mapping files only; we encourage you to spend some time with the log output generated by Hibernate and familiarize yourself with the internals.
Hibernate logs all interesting events through Apache commons-logging, a thin abstraction layer that directs output to either Apache Log4j (if you put log4j.jar in your classpath) or JDK 1.4 logging (if you’re running under JDK 1.4 or above and Log4j isn’t present). We recommend Log4j because it’s more mature, more popu-lar, and under more active development.
To see output from Log4j, you need a file named log4j.properties in your class-path (right next to hibernate.properties or hibernate.cfg.xml). Also, don’t forget to copy the log4j.jar library to your lib directory. The Log4j configuration exam-ple in listing 2.7 directs all log messages to the console.
# Direct log messages to stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE}
➥%5p %c{1}:%L - %m%n
# Root logger option
log4j.rootLogger=INFO, stdout
# Hibernate logging options (INFO only shows startup messages) log4j.logger.org.hibernate=INFO
Listing 2.7 An example log4j.properties configuration file
# Log JDBC bind parameter runtime arguments log4j.logger.org.hibernate.type=INFO
The last category in this configuration file is especially interesting: It enables the logging of JDBC bind parameters if you set it to DEBUG level, providing information you usually don’t see in the ad hoc SQL console log. For a more comprehensive example, check the log4j.properties file bundled in the etc/ directory of the Hibernate distribution, and also look at the Log4j documentation for more infor-mation. Note that you should never log anything at DEBUG level in production, because doing so can seriously impact the performance of your application.
You can also monitor Hibernate by enabling live statistics. Without an applica-tion server (that is, if you don’t have a JMX deployment environment), the easiest way to get statistics out of the Hibernate engine at runtime is the SessionFactory:
Statistics stats =
HibernateUtil.getSessionFactory().getStatistics();
stats.setStatisticsEnabled(true);
...
stats.getSessionOpenCount();
stats.logSummary();
EntityStatistics itemStats =
stats.getEntityStatistics("auction.model.Item");
itemStats.getFetchCount();
The statistics interfaces are Statistics for global information, Entity-Statistics for information about a particular entity, CollectionStatistics for a particular collection role, QueryStatistics for SQL and HQL queries, and
The statistics interfaces are Statistics for global information, Entity-Statistics for information about a particular entity, CollectionStatistics for a particular collection role, QueryStatistics for SQL and HQL queries, and