Recipe10.4.Integrating Struts with Hibernate


Recipe 10.4. Integrating Struts with Hibernate

Problem

You want to use Hibernate for object/relational mapping in your Struts application.

Solution

Use a servlet filter such as the Persistence class shown in Example 10-11.

Example 10-11. Servlet filter for Hibernate sessions
package com.jadecove.util; import java.io.*; import javax.servlet.*; import net.sf.hibernate.*; import net.sf.hibernate.cfg.Configuration; /**  * Filter which manages a ThreadLocal hibernate session.  Obtain the session  * by calling Persistance.getSession( ).  */ public class Persistence implements Filter {     /**      * Holds the current hibernate session, if one has been created.      */     protected static ThreadLocal hibernateHolder = new ThreadLocal( );         protected static SessionFactory factory;        public void init(FilterConfig filterConfig) throws ServletException {         // Initialize hibernate         try {             doInit( );         }         catch (HibernateException ex) {              throw new ServletException(ex);          }           }         /**      * This method should only be called when this class is used directly -       * that is, when using this class outside of the servlet container.      * @throws HibernateException      */     public static void doInit( ) throws HibernateException {         factory = new Configuration( ).configure( ).buildSessionFactory( );     }     public void doFilter(ServletRequest request,                           ServletResponse response,                           FilterChain chain)                   throws IOException, ServletException {         if (hibernateHolder.get( ) != null)             throw new IllegalStateException(                 "A session is already associated with this thread!  "                 + "Someone must have called getSession( ) outside of                                                          the context "                 + "of a servlet request.");                    try {               chain.doFilter(request, response);         }         finally {             Session sess = (Session)hibernateHolder.get( );             if (sess != null) {                 hibernateHolder.set(null);                                try {                     sess.close( );                 }                 catch (HibernateException ex) {                      throw new ServletException(ex);                 }             }         }     }        /**      * ONLY ever call this method from within the context of a servlet request      * (specifically, one that has been associated with this filter).  If you      * want a Hibernate session at some other time, call getSessionFactory( )      * and open/close the session yourself.      *      * @return an appropriate Session object      */     public static Session getSession( ) throws HibernateException {         Session sess = (Session)hibernateHolder.get( );                if (sess == null) {             sess = factory.openSession( );             hibernateHolder.set(sess);         }               return sess;     }        /**      * @return the hibernate session factory      */     public static SessionFactory getSessionFactory( ) {       return factory;     }        /**      * This is a simple method to reduce the amount of code that needs      * to be written every time hibernate is used.      */     public static void rollback(net.sf.hibernate.Transaction tx) {         if (tx != null) {             try {                 tx.rollback( );             }             catch (HibernateException ex) {                 // Probably don't need to do anything - this is likely being                 // called because of another exception, and we don't want to                 // mask it with yet another exception.             }         }     }     public void destroy( ) {         // Nothing necessary     } }

Declare the filter in your web.xml file with a filter mapping configured for all URLs that need access to persistent data. Here the filter mapping is set to all URLs:

<filter>     <filter-name>PersistenceFilter</filter-name>     <filter-class>com.jadecove.util.Persistence</filter-class> </filter>      <filter-mapping>     <filter-name>PersistenceFilter</filter-name>     <url-pattern>/*</url-pattern> </filter-mapping>

You get a session from the filter by calling the static getSession( ) method. Here's an example method that you can use in your own DAOs:

protected Session getSession(  ) throws PersistenceException {     Session session = null;     try {         session = Persistence.getSession( );     } catch (HibernateException e) {         log.error("Exception accessing persistence session");         throw new PersistenceException(               "Exception accessing persistence session", e);     }     return session; }

Discussion

If you haven't heard of Hibernate, then you've obviously been sleeping in a cave. Hibernate is one of the most popular object/relational mapping frameworks in use today. It can be used to provide transparent persistence for Java objects, populating and storing object properties using data stored in a relational database.

The Persistence filter shown in Example 10-11 provides a Hibernate session for every servlet request that goes through the filter. It uses a ThreadLocal variable to hold the Hibernate session (see Sidebar 10-1). Each HTTP request is a separate thread; therefore, each request has its own Hibernate session. The Hibernate session is automatically closed when the request is completethat is, immediately before the HTTP response is sent. The finally block in the doFilter( ) method closes the Hibernate session and sets the value for the session in the ThreadLocal variable to null.

Thread Local Variables Explained

If you look at the filter in Example 10-11, it may appear that only on Hibernate session will be for the application because the session is held in a static THReadLocal variable. While it's true that there will be only one THReadLocal variable, that variable will hold multiple object instances, Hibernate sessions in this case. When an object is added to a THReadLocal variable via the ThreadLocal.set( ) method, that object is implicitly associated with the current thread. In that sense, a ThreadLocal object is like a hash-table entry. The implicit entry key is the current thread, and the entry value, accessible via the ThreadLocal.get( ) method, is the Hibernate session.


A class that needs to load or store persistent data gets a Hibernate session by calling the static Persistence.getSession( ) method.

Hibernate can defer the loading of specific object properties from the database. The database won't be accessed until that property's accessor method is explicitly called. This feature is known as lazy-loading. Since Hibernate persists a graph of objects, lazy-loading can allow you to retrieve a complex object without necessarily paying the performance price of retrieving all the data from the database. The one caveat to this feature is that a lazy-loaded property can only be retrieved if that object is associated with an open Hibernate session.

Without using the Persistence filter, a Hibernate session would have to remain open if you needed to lazy-load a property on a JSP page. However, it is dangerous to leave a Hibernate sessionessentially a database connectionopen between user requests. If you close the Hibernate session before forwarding to a JSP page, attempting to access a lazy-loaded property will fail because an open Hibernate session isn't available. The Persistence filter solves this problem. Because the Hibernate session is opened by a filter; the session remains open until the request is complete. JSP pages and other web resources can successfully read persistent object properties, even if they are lazy-loaded.

For unit testing, the Persistence filter can be used outside of the application server. In this case, you use the Persistence class to initialize Hibernate and retrieve a Hibernate session factory. Example 10-12 shows an abstract JUnit test base class that uses the Persistence class.

Example 10-12. Abstract persistence base test case
package com.jadecove.facet; import junit.framework.TestCase; import net.sf.hibernate.Session; import com.jadecove.util.Persistence; public abstract class PersistenceTestCase extends TestCase {     public PersistenceTestCase(String name) {         super(name);     }     protected void setUp( ) throws Exception {         super.setUp( );         Persistence.doInit( );         openSession( );         doSetUp( );     }          /**      * Override this method to provide test-specific set-up      */     protected void doSetUp( ) throws Exception {     }     /**      * Override this method to provide test-specific tear-down      */     protected void doTearDown( ) throws Exception {     }     protected void tearDown( ) throws Exception {         super.tearDown( );         session.close( );         doTearDown( );     }     protected void openSession( ) throws Exception {         session = Persistence.getSessionFactory( ).openSession( );     }     protected void closeSession( ) throws Exception {         session.close( );     }     protected Session session; }

See Also

The Persistence filter of the Solution is based on a recommended practice presented on the Hibernate web site. The relevant page can be found at http://www.hibernate.org/43.html.

If you were to search the Internet for "Struts Hibernate" you'd probably come upon the HibernatePlugin for Struts. This class can be used as a Hibernate session factory; however, it doesn't have the advantages afforded by the Persistence filter. This class can also be found on the Hibernate web site at http://www.hibernate.org/105.html.



    Jakarta Struts Cookbook
    Jakarta Struts Cookbook
    ISBN: 059600771X
    EAN: 2147483647
    Year: 2005
    Pages: 200

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net