Hibernate supports several different systems for building applications. For example, you can start with Java objects (as shown in Chapter 3) or with an existing database (as shown in Chapter 4). If, as sometimes happens, you “inherit” a system, this may force your hand by making it necessary to start from an existing database.
In this chapter, we'll look at a few available technologies and solutions for reducing or improving your code. They aren't required but may make your life with Hibernate easier. They include:
Reducing code with inversion of control.
Reducing session-creation impact with ThreadLocal
.
Using Hibernate as an EJB BMP Solution.
Integrating additional tools and technologies.
Reviewing applications that use Hibernate.
Strategies for getting started.
Hibernate is a popular technology, so watch the Hibernate Web site and forum (http://www.hibernate.org/) closely; there's always something interesting going on.
Perhaps you wish to use Hibernate in a JSP application (as described in Chapter 2) but are unhappy with the copy-and-paste booking shown for managing transactions. If so, you can use anonymous inner classes to reduce bookkeeping while preserving control. This is popularly referred to as inversion of control, or IoC.
The term inversion of control refers to the notion that your code is handed to another class to actually be executed. Whenever you write code intended to run inside another application, you are, in a sense, inverting (or transferring) control of the execution environment to another system. In particular, the use in Java of anonymous inner classes in conjunction with a framework is dubbed “inversion of control.”
Listing 12.1 shows the use of an anonymous inner class to actually perform the database access. Two independent operations are shown. Each operation is performed by an anonymous HibernateSessionWrapper()
implementation of the task()
method. The first operation demonstrates access to the database, the second is an example of error handling. Note that the code shows no exception-handling strategy; it is subsumed into the underlying HibernateSession Wrapper
class.
Example 12.1. Inversion of Control Example
package com.cascadetg.ch12; import java.util.Iterator; public class IoCExample { public static void main(String[] args) { // Need one global configuration HibernateSessionWrapper.initialization(); // Create our task as an anonymous inner class. // Note, no exception handling needed, implicit // availability of a session. HibernateSessionWrapper myTask = new HibernateSessionWrapper() { Object task(Object in) throws Exception { net.sf.hibernate.Query myQuery = session .createQuery((String) in); Iterator result = myQuery.list().iterator(); session.flush(); return result; } }; // Execute a bit of HQL and get the results Iterator result = (Iterator) myTask .perform("from Author"); while (result.hasNext()) { System.out.println(result.next().toString()); System.out.flush(); } // Create our task as an anonymous inner class. // Note, no exception handling needed, implicit // availability of a session. HibernateSessionWrapper myTask2 = new HibernateSessionWrapper() { Object task(Object in) throws Exception { throw new Exception(); } }; // Do the task. if (myTask2.perform() == null) System.out.println(myTask2.getException()); } }
The anonymous inner class in Listing 12.1 is a subclass of the abstract class shown in Listing 12.2. Only the task()
method must be overridden. If the task()
method fails because of an exception, the method automatically returns null, and additional methods are provided to determine the precise nature of the exception.
Example 12.2. Inversion of Control Example
package com.cascadetg.ch12; import net.sf.hibernate.*; import net.sf.hibernate.cfg.*; public abstract class HibernateSessionWrapper { /** This method must be overridden to actually do anything */ abstract Object task(Object in) throws Exception; Session session = null; Transaction transaction = null; boolean stale = false; Exception problem = null; /** * Used to identify that a stale object was encountered as * part of the transaction. */ public boolean isStaleOperation() { return stale; } public Exception getException() { return problem; } /** We use this session factory to create our sessions */ public static SessionFactory sessionFactory; /** * Loads the Hibernate configuration information, sets up the * database and the Hibernate session factory. * * Should be customized for your application. */ public static void initialization() { try { Configuration myConfiguration = new Configuration(); myConfiguration .addClass(com.cascadetg.ch02.Post.class); myConfiguration .addClass(com.cascadetg.ch02.Author.class); // Sets up the session factory (used in the rest // of the application). sessionFactory = myConfiguration .buildSessionFactory(); } catch (Exception e) { e.printStackTrace(); } } public Object perform() { return perform(null); } /** * Actually does the set-up for the session, as well as * execute the task. Handles all of the exceptions (you may * wish to customize this per your login preference) */ public Object perform(Object in) { Object result = null; try { session = sessionFactory.openSession(); transaction = session.beginTransaction(); result = task(in); transaction.commit(); } catch (StaleObjectStateException staleException) { stale = true; try { transaction.rollback(); } catch (Exception e2) { e2.printStackTrace(); } } catch (Exception e) { result = null; problem = e; e.printStackTrace(); try { transaction.rollback(); } catch (Exception e2) { // Notify of a failure to roll the transaction // back e.printStackTrace(); } } finally { try { if (session != null) session.close(); } catch (Exception e) { // Silent failure of session close } } return result; } }
If you find the inversion of control style of development interesting, you may wish to investigate the Spring framework (http://www.springframework.org/). Spring is a more sophisticated IoC framework than the one shown in Listing 12.2; it provides a number of additional services (both specific to Hibernate and helpful for application development in general).
You can reduce the impact of retrieving a new Session
object for each thread by reusing the Session for a given object. As described in Chapter 6 and Chapter 9, a Session
is not sharable across threads. The code in Listing 12.3 shows a Hibernate-recommended mechanism for maintaining a per-thread Session
variable. The real work of this code is done in the java.lang.ThreadLocal
class, which simplifies the use of a per-thread variable.
Example 12.3. ThreadLocal
Example
import net.sf.hibernate.*; import net.sf.hibernate.cfg.*; public class HibernateUtil { private static final SessionFactory sessionFactory; static { try { sessionFactory = new Configuration().configure() .buildSessionFactory(); } catch (HibernateException ex) { throw new RuntimeException( "Exception building SessionFactory: " + ex.getMessage(), ex); } } public static final java.lang.ThreadLocal session = new java.lang. ThreadLocal(); public static Session currentSession() throws HibernateException { Session s = (Session) session.get(); // Open a new Session, if this Thread has none yet if (s == null) { s = sessionFactory.openSession(); session.set(s); } return s; } public static void discardSession() throws HibernateException { Session s = (Session) session.get(); session.set(null); if (s != null) s.close(); } }
With luck, you should already have your Session
management contained in a specific class with a static method, as shown in Chapter 2. The biggest difficulty with the pattern shown in Listing 12.3 is that a Session
object can be rendered into an untenable state if a transaction-related exception is shown (Hibernate explicitly does not allow reuse of a Session
object in this situation). This is why an additional discardSession()
method is shown above; you need to ensure that the Session
object is discarded if your application throws a transaction-related exception, as shown in the discardSession()
method.
Hibernate is intended to work in a wide variety of environments, and works well as a bean-managed persistence (BMP) solution when used in a variety of J2EE application servers. In fact, JBoss has indicated that a future version of its software will likely use Hibernate as the basis for container-managed persistence (CMP).
The development process for BMP and EJB varies tremendously from development environment to environment and therefore is beyond the scope of this text. Conceptually, however, you are relying on your J2EE application server to provide a variety of services, such as identity management, clustering, and JDBC connection pooling, when you use Hibernate to handle the actual generated persistence (specifically, the generated SQL). This affords several advantages, such as the ability to unit test your database access logic outside the context of a full application server and also the ability to understand the persistence performance characteristics of an application independent of the application server. Let's say, for example, that you develop an application intended to target three different application servers. By using Hibernate as your persistence mechanism, you can test your application against your target databases with a greater sense of independence for the tests with the different application servers.
In any event, Hibernate should at most be considered an alternative to EJB container-managed persistence, not the full EJB specification.
If you are interested in using Hibernate in conjunction with EJB, you will find descriptions of the general integration at http://hibernate.org/82.html and http://hibernate.org/166.html. A description of how to add Hibernate as a service to your JBoss installation can be found at http://hibernate.org/66.html.
Hibernate is a very popular package, and several developers have posted tips and tricks for integrating Hibernate with other packages. Table 12.1 shows a list, up to date as of this writing, of some of the products and technologies that in one form or another have been integrated with Hibernate. For the latest information, consult the http://hibernate.org/ Web site.
Table 12.1. Technologies Integrated with Hibernate
Technology | Use | Page |
---|---|---|
Eclipse | Java IDE | |
HiberClipse | Eclipse Plugin | |
Hibernate JSP Tags | JSP Tags | |
HibernateSynchronizer | Eclipse Plugin | |
Hibernator | Eclipse Plugin | |
IntelliJ IDEA | Java IDE | |
JUnit | Unit Test Framework | |
Log4j | Logging Toolkit | |
Lucerne | Free Text Search Engine | |
Maven | Project Management | |
Pico | Lightweight Container | |
Spring | Java IoC Framework | |
Struts | Web MVC Framework | |
Tangosol Coherence | Distributed Cache | |
Tapestry | Web Application Framework | |
Tomcat | Application Server | |
WebSphere Application Developer (WASD) 5.x | IBM Java IDE |
Table 12.2 shows some applications that make use of Hibernate. If you wish to examine applications larger than those that can conveniently be included in a book like this one, you may wish to download and peruse the code for the open-source projects (and you may get ideas or useful tips from both the open-source and commercial products).
Table 12.2. Products That Use Hibernate
Application | Use | Page |
---|---|---|
DeepBlack | Weblog | |
Flock | News aggregator | |
Jagzilla | Bugzilla Interface | |
JasperReports | Reporting Tool | |
JavaLobby Community Platform | Portal | |
jboard | Bulletin Board | |
jBpm | Business Process Management | |
Liferay | Enterprise Portal | |
openEF/J | Enterprise Application Framework | |
OpenReports | Reporting Tool | |
PersonalBlog | Weblog | |
Phoo | Java Bot Suite | |
Roller Weblogger | Weblog | |
TM4J | Topic Map Engine |
Throughout this text, I've tried to provide as balanced a set of recommendations as possible. In this section, I offer some general guidelines for those who are still unsure about the proper approach. Consider this section to reflect opinion only—and keep in mind that since I don't know your application's requirements, these recommendations should be taken with a grain of salt.
My approach for working with Hibernate depends on whether or not there is an existing database. If you are starting with an existing database, use Middlegen (as described in Chapter 4). If the database schema is extremely stable, you may wish to use Middlegen to generate a first pass at the *.hbm.xml
files, and then hand-tune them as needed. If you believe the schema is potentially subject to change, follow the guidelines for Middlegen as described in Chapter 4.
If you are starting from scratch, use the *.hbm.xml
mapping as your canonical format, and generate both your Java and database schema based on that (as described in Chapter 2 and Chapter 11). I find that starting from Java (as described in Chapter 3) is most comfortable for developers who are accustomed to using XDoclet as a refuge from the complexity of EJB. I find it much easier to generate persistent objects from my mapping file than to rely on generating get/set methods in bulk and then inspecting the generated *.hbm.xml
files.
Make sure that you understand the many-to-one and one-to-many relationships before working on the more esoteric types shown in Chapter 7. The vast majority of databases can be modeled relatively conveniently using only these two basic relationships. Class and the more sophisticated relationships are useful tools, but you should feel comfortable with the basic relationships before tackling them.
When you are first starting out with Hibernate, the relationship between queries and lazy loading of objects can be confusing. You will probably find it especially difficult to understand the implications of the different data-loading strategies. Use the tools shown in Chapter 10 to inspect and time the SQL generated by Hibernate.
Put another way, make sure that you understand how your object graph is going to be traversed. How do you expect to access your data? Make sure that you understand as many as possible of the creation, retrieval, update, and delete (CRUD) operations on your objects.
18.222.116.233