You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@turbine.apache.org by ep...@apache.org on 2003/05/28 05:47:14 UTC

cvs commit: jakarta-turbine-2/xdocs/howto hibernate-howto.xml index.xml

epugh       2003/05/27 20:47:14

  Modified:    xdocs/howto index.xml
  Added:       xdocs/howto hibernate-howto.xml
  Log:
  Adding first cut of documentation about integrating the HibernateService.
  
  Revision  Changes    Path
  1.7       +1 -0      jakarta-turbine-2/xdocs/howto/index.xml
  
  Index: index.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-turbine-2/xdocs/howto/index.xml,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- index.xml	30 Jan 2003 00:32:12 -0000	1.6
  +++ index.xml	28 May 2003 03:47:14 -0000	1.7
  @@ -25,6 +25,7 @@
           <li><a href="url-rewriting-howto.html">URL rewriting Howto</a></li>
           <li><a href="velocity-site-howto.html">Velocity Site Howto</a></li>
           <li><a href="context-howto.html">Velocity Context Howto</a></li>
  +        <li><a href="hibernate-howto.html">Hibernate OM Howto</a></li>
           <li><a href="http://nagoya.apache.org/wiki/apachewiki.cgi?CategoryJakartaTurbine2HowTo">
               Additional Howtos from the Apache Wiki</a></li>
         </ul>
  
  
  
  1.1                  jakarta-turbine-2/xdocs/howto/hibernate-howto.xml
  
  Index: hibernate-howto.xml
  ===================================================================
  <?xml version="1.0"?>
  
  <document>
    <properties>
      <title>Hibernate Howto</title>
      <author email="epugh@upstate.com">Eric Pugh</author>
    </properties>
  
    <body>
  
      <section name="Introduction">
        <p>
          This is a HOWTO on implementing Hibernate as the database OM layer.  The motivating factors for extending TurbineUser
          are:
          <ol>
            <li>
        You just use POJO (Plain Old Java Objects) versus generated objects.
            </li>
            <li>
        You can either start with Java objects and create your database schema from there, or 
        start with a database schema, and map the Java objects to the schema.
            </li>
            <li>
        Extensive performance optimizations built into the Hibernate engine.
            </li>          
            <li>
        Leverage Avalon component use in Turbine 2.3!
            </li>                    
          </ol>
        </p>
        <p>
          Please review the content available from the Hibernate homepage.  This howto assumes
          you already are confortable with the various Hibernate concepts.
        </p>
        <p>
      In this example, we will take a simple schema with a single table and integrate Hibernate into
      a Turbine action.  We will talk about best practices for Hibernate and Turbine.
          <ol>
            <li>
          Configure project dependencies.
            </li>        
            <li>
          Directory Structures
            </li>
      
            <li>
          Setup Avalon component configuration.
            </li>
            
            <li>
            Setup Hibernate mapping files.
            </li>
            <li>
            Create static ServiceLocator class.
            </li>          
            <li>
              Create filter that manages the Hibernate Session.
            </li>
            <li>
              Setup PersitenceException handling
            </li>          
          </ol>
        </p>
  
      </section>
  
      <section name="Directory Structures">
        <p>
        While you can organize your directory structure however you like, I tend to have structure like:
        </p>
        <source>
        /src/java/com/my/project/bizobj business objects reside
        /src/java/com/my/project/om POJO objects mapped to database reside
        /src/java/com/my/project/om/persist manager objects that faciliate retrieve POJO objects
        /src/java/com/my/project/om/filter Servlet Filter lives that maintains the Hibernate Session 
        /src/java/ where hibernate.hbm.xml and hibernate.cfg.xml files live
        
      </source>
  
      </section>
  
      <section name="Configure project dependencies" >
        <p>
          The easiest way to maintain a project is to use Maven.  Here are the sample
          dependencies for your project.xml.
        </p>
        <source>
  <![CDATA[
      <dependency>
        <id>hibernate</id>      
        <version>2.0-beta6</version>
        <properties>        
        <war.bundle.jar>true</war.bundle.jar>
        </properties>
      </dependency>  
      <dependency>
        <id>hibernate:hibernate-avalon</id>
        <version>0.1</version>
        <properties>        
        <war.bundle.jar>true</war.bundle.jar>
        </properties>              
      </dependency>  
      <dependency>
        <id>dom4j</id>      
        <version>1.4</version>
        <properties>        
        <war.bundle.jar>true</war.bundle.jar>
        </properties>
      </dependency>       
      <dependency>
        <id>cglib</id>      
        <version>rc2-1.0</version>
        <properties>        
        <war.bundle.jar>true</war.bundle.jar>
        </properties>
      </dependency>                          
      <dependency>
        <id>jcs</id>      
        <version>1.0-dev</version>
        <properties>        
        <war.bundle.jar>true</war.bundle.jar>
        </properties>
      </dependency>                       
  ]]>
        </source>     
        <p>
          The key jar file is the hibernate-avalon.jar file, it contains the Avalon wrapper
          for Hibernate, and can be used with any Avalon container.
        </p>
          
      </section>
  
      <section name="Setting up Avalon Configuration" >
  
          <p>
        We leverage the Avalon based HibernateService that is hosted as part of the
        HibernateExt project.  You must specify the name of the service, as well as the implementing
        service in your componentConfiguration.xml file.  Other configuration parameters can be passed
        in as well.
          </p>
        <source>
  <![CDATA[
  
  <my-system>
    <component
      role="net.sf.hibernate.avalon.HibernateService"
      class="net.sf.hibernate.avalon.HibernateServiceImpl">
    </component>
  </my-system>
                        
  ]]>
        </source>     
  
          <p>
        The componentRoles file also defines how to access the component.  Here is a 
        very simple componentRoles.xml file:
          </p>
        <source>
  <![CDATA[
  
  <role-list>
   <role
      name="net.sf.hibernate.avalon.HibernateService"
      shorthand="hibernate"
      default-class="net.sf.hibernate.avalon.HibernateServiceImpl"/>   
  </role-list>
                        
  ]]>
        </source>             
      </section>
        
        
  
      <section name="Setup Hibernate mapping files">
        <p>
      This section will not go into depth on how to configure the hibernate.cfg.xml and
      hibernate.hbm.xml files.  However, if you don't specify in the componentConfiguration.xml
      file where they are located, then they should be placed in your src/java/ directory.
        </p>
        <p>
          The exact configuration is dependent on the environment Turbine is running in, however
          I highly recommend that you use the JNDI look up of the datasource, and use DBCP for your
          connection pooling.  I have found that when using Hibernate in cactus tests, you may need to
          increase the pool size required in order to not run out of connections, however this doesn't
          seem to apply to running as a web application.
        </p>
      </section>
  
  
  
      <section name="Create static ServiceLocator class.">
        <p>
      Currently this class is not integrated into Turbine, so you will need to provide an implemation yourself.
      This provides a static method of retrieving your Hibernate Session.
        </p>
       
          <source>
  <![CDATA[
  package com.upstate.cellculture.om.persist;
  
  import net.sf.hibernate.HibernateException;
  import net.sf.hibernate.JDBCException;
  import net.sf.hibernate.Session;
  import net.sf.hibernate.SessionFactory;
  
  import org.apache.commons.logging.Log;
  import org.apache.commons.logging.LogFactory;
  
  /**
   * This class is used to get Hibernate Sessions and may
   * also contain methods (in the future) to get DBConnections
   * or Transactions from JNDI.
   */
  public class ServiceLocator
  {
      //~ Static fields/initializers =============================================
      public final static String SESSION_FACTORY = "hibernate/sessionFactory";
      public static final ThreadLocal session = new ThreadLocal();
      private static SessionFactory sf = null;
      private static ServiceLocator me;
      private static Log log = LogFactory.getLog(ServiceLocator.class);
  
      static {
          try
          {
              me = new ServiceLocator();
          }
          catch (Exception e)
          {
              log.fatal("Error occurred initializing ServiceLocator");
              e.printStackTrace();
          }
      }
  
      //~ Constructors ===========================================================
  
      private ServiceLocator() throws HibernateException, JDBCException
      {}
  
      //~ Methods ================================================================
  
      public static Session currentSession() throws PersistenceException
      {
          Session s = (Session) session.get();
  
          if (s == null)
          {
              s = PersistenceManager.openSession();
              if (log.isDebugEnabled())
              {
                  log.debug("Opened hibernate session.");
              }
  
              session.set(s);
          }
  
          return s;
      }
  
      public static void closeSession() throws HibernateException, JDBCException
      {
          Session s = (Session) session.get();
          session.set(null);
  
          if (s != null)
          {
              if (s.isOpen())
              {
                  s.flush();
                  s.close();
  
                  if (log.isDebugEnabled())
                  {
                      log.debug("Closed hibernate session.");
                  }
              }
          }
          else
          {
              log.warn("Hibernate session was inadvertently already closed.");
  
          }
      }
  }
  ]]>
        </source>
      </section>
  
      <section name="Create Servlet Filter.">
        <p>
          The servlet Filter is required if you use lazy loading of objects because you need to open a Hibernate sesion, and keeps it open through the view layer. 
          This ensures that each user get's their own Hibernate Session.  ?? Should this be done
          through some sort of SessionValidator?
  
        </p>
       
          <source>
  <![CDATA[        
  package com.upstate.cellculture.filter;
  
  import java.io.IOException;
  
  import javax.servlet.Filter;
  import javax.servlet.FilterChain;
  import javax.servlet.FilterConfig;
  import javax.servlet.ServletException;
  import javax.servlet.ServletRequest;
  import javax.servlet.ServletResponse;
  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.HttpServletResponse;
  import javax.servlet.http.HttpSession;
  
  import net.sf.hibernate.Session;
  
  import org.apache.commons.logging.Log;
  import org.apache.commons.logging.LogFactory;
  
  import com.upstate.cellculture.om.persist.PersistenceException;
  import com.upstate.cellculture.om.persist.ServiceLocator;
  
  public class ActionFilter implements Filter
  {
      //~ Static fields/initializers =============================================
  
      //~ Instance fields ========================================================
  
      /**
       * The <code>Log</code> instance for this class
       */
      private Log log = LogFactory.getLog(ActionFilter.class);
      private FilterConfig filterConfig = null;
  
      //~ Methods ================================================================
  
      public void init(FilterConfig filterConfig) throws ServletException
      {
          this.filterConfig = filterConfig;
  
      }
  
      /**
       * Destroys the filter.
       */
      public void destroy()
      {
          filterConfig = null;
      }
  
      public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException
      {
          // cast to the types I want to use
          HttpServletRequest request = (HttpServletRequest) req;
          HttpServletResponse response = (HttpServletResponse) resp;
          HttpSession session = request.getSession(true);
  
          Session ses = null;
          boolean sessionCreated = false;
  
          try
          {
              chain.doFilter(request, response);
          }
          finally
          {
              try
              {
                  ServiceLocator.closeSession();
              }
              catch (Exception exc)
              {
                  log.error("Error closing hibernate session.", exc);
                  exc.printStackTrace();
              }
          }
      }
  
      public static Session getSession() throws PersistenceException
      {
          try
          {
  
              return ServiceLocator.currentSession();
          }
          catch (Exception e)
          {
              throw new PersistenceException("Could not find current Hibernate session.", e);
          }
  
      }
  }
  
  ]]>
        </source>
      </section>
  
  
  
  
  
  
      <section name="Implementing Manager classes">
          <p>
        To centralize access to the database, we create Manager classes.  Often Manager
        classes are called DAO (Data Access Classes).  Here is an example:
          </p>
          <source>
  <![CDATA[
  /*
   * Created on Apr 28, 2003
   *
   */
  package com.upstate.cellculture.om.persist;
  import java.util.List;
  
  import net.sf.hibernate.Query;
  import net.sf.hibernate.Session;
  
  import com.upstate.cellculture.om.Technician;
  /**
   * @author tmckinney
   *
   * Centralizes all access to the Technicians table
   */
  public class TechnicianManager
  {
      private static List allTechnicians;
      public static void save(Technician technician) throws PersistenceException
      {
          try
          {
  
              ServiceLocator.currentSession().save(technician);
  
          }
          catch (Exception e)
          {
              throw new PersistenceException("Could not save.", e);
          }
      }
  
      public static Technician retrieveByPK(long technicianId) throws PersistenceException
         {
             try
               {
                   Technician technician= (Technician) ServiceLocator.currentSession().load(Technician.class, new Long(technicianId));
                   return technician;
               }
               catch (Exception e)
               {
                   throw new PersistenceException("Could not retrieve.", e);
               }
         }
  
  
      public static List retrieveAllTechnicians() throws PersistenceException
      {
          if (allTechnicians == null)
          {
              try
              {
                  Query q = ServiceLocator.currentSession().createQuery("from technician in class " + Technician.class +" order by upper(technician.name)");
                  allTechnicians = q.list();
                  session.flush();
              }
              catch (Exception e)
              {
                  e.printStackTrace();
                  throw new PersistenceException(e);
              }
          }
          return allTechnicians;
  
      }
  
  
  }
  
  ]]>
        </source>
        <p>
            By using this TechnicianManager class, we could feasibly swap out the Hibernate code
            for another OM strategy.  Even better would be to have a TechnicianManager interface
            and a TechicianManagerImpl class.  Possibly loaded via Avalon as a component!
        </p>
      </section>
  
      <section name="Setup PersitenceException handling">
          <p>
        By catching a generice Persistence Exception that wraps all the
        thrown exceptions we can just catch a single exception and then 
        retrieve what type of exception it is.
          </p>
          <source>
  <![CDATA[
  package com.upstate.cellculture.om.persist;
  
  import org.apache.commons.lang.exception.NestableException;
  /**
   * A general PersistenceException that is thrown by all Manager classes.
   *
   */
  public class PersistenceException extends NestableException
  {
      //~ Constructors ===========================================================
  
      /**
       * Constructor for PersistenceException.
       */
      public PersistenceException()
      {
          super();
      }
  
      /**
       * Constructor for PersistenceException.
       *
       * @param message
       */
      public PersistenceException(String message)
      {
          super(message);
      }
  
      /**
       * Constructor for PersistenceException.
       *
       * @param message
       * @param cause
       */
      public PersistenceException(String message, Throwable cause)
      {
          super(message, cause);
      }
  
      /**
       * Constructor for PersistenceException.
       *
       * @param cause
       */
      public PersistenceException(Throwable cause)
      {
          super(cause);
      }
  
  }
  
  ]]>
        </source>
  
      </section>
    </body>
  </document>
  
  
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: turbine-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: turbine-dev-help@jakarta.apache.org