IT Berater – Java, J2EE, WebSphere Portal, Lotus Domino
RSS icon Home icon
  • Configuring EMF Teneo with Hibernate, Commons DBCP, Spring Hibernate Transaction Manager, and the OpenSessionInViewFilter

    (5 votes) 1 Star2 Stars3 Stars4 Stars5 Stars
    Loading ... Loading ...
    Posted on 2 October 2010 No comments

    While trying to get an application working with , Hibernate, Commons DBCP, Spring Hibernate Transaction Manager and the OpenSessionInViewFilter I encountered several unexpected issues. Here is an example of a working configuration for this software stack. Read the comments below the XML file for some explanations.

    HbDataStoreWithDataSource.java:

    package de.sebthom.util;
    
    import java.sql.*;
    import java.util.Properties;
    import javax.sql.DataSource;
    import org.eclipse.emf.teneo.PersistenceOptions;
    import org.eclipse.emf.teneo.hibernate.HbSessionDataStore;
    import org.hibernate.HibernateException;
    import org.hibernate.cfg.*;
    import org.hibernate.connection.ConnectionProvider;
    
    /**
     * @author Sebastian Thomschke
     */
    public class HbDataStoreWithDataSource extends HbSessionDataStore {
      public final static class CustomConnectionProvider implements ConnectionProvider {
        private DataSource ds;
    
        public void close() throws HibernateException { }
    
        public void closeConnection(final Connection conn) throws SQLException {
          conn.close();
        }
    
        public void configure(final Properties props) throws HibernateException {
          ds = _CONFIG_TIME_DS_HOLDER.get();
        }
    
        public Connection getConnection() throws SQLException {
          return ds.getConnection();
        }
    
       public boolean supportsAggressiveRelease() {
         return false;
       }
      }
    
      private static final long serialVersionUID = 1L;
      private static HbDataStoreWithDataSource INSTANCE;
    
      /**
       * the data source holder acts as a thread safe bridge between the DSConnectionProvider which is instantiated by Hibernate internally
       * and the data source injected into the session factory during spring configuration time. 
       */
      private static final ThreadLocal _CONFIG_TIME_DS_HOLDER = new ThreadLocal();
    
      public static HbDataStoreWithDataSource getInstance() {
    		return INSTANCE;
      }
    
      private final Properties props = new Properties();
    
      public HbDataStoreWithDataSource() {
        INSTANCE = this;
    
        props.setProperty(PersistenceOptions.ADD_INDEX_FOR_FOREIGN_KEY, "true");
        props.setProperty(PersistenceOptions.FETCH_CONTAINMENT_EAGERLY, "false");
        props.setProperty(PersistenceOptions.FETCH_ASSOCIATION_EXTRA_LAZY, "false");
    
        // tell Teneo to not change the table names, we do this using Hibernate's ImprovedNamingStrategy
        props.setProperty(PersistenceOptions.SQL_CASE_STRATEGY, "none");
        props.setProperty(PersistenceOptions.MAXIMUM_SQL_NAME_LENGTH, "-1");
      }
    
      protected Configuration createConfiguration() {
        Configuration cfg = super.createConfiguration();
        cfg.setNamingStrategy(ImprovedNamingStrategy.INSTANCE);
    
        // only if a DS is injected we register our connection provider
        if (_CONFIG_TIME_DS_HOLDER.get() != null)
          cfg.setProperty(Environment.CONNECTION_PROVIDER, CustomConnectionProvider.class.getName());
        return cfg;
      }
    
      public void setDataSource(final DataSource ds) {
        _CONFIG_TIME_DS_HOLDER.set(ds);
      }
    
      public final void setHibernateProperties(final Properties hibernateProperties) {
        throw new UnsupportedOperationException();
      }
    
      public final void setPersistenceProperties(final Properties persistenceOptions) {
        throw new UnsupportedOperationException();
      }
    
      public void setProperties(final Properties props) {
        this.props.putAll(props);
        super.setProperties(this.props);
      }
    }
    

    web.xml:

    
      openSessionInViewFilter
      org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
        
          singleSession
          true
        
        
          sessionFactoryBeanName
          realSessionFactory
        
    
    
      openSessionInViewFilter
      /*
      REQUEST
      ERROR
    
    

    applicationContext.xml:

    
    
      
    
      
        
          
            ${jdbc.driverClassName}
            ${jdbc.url}
            ${jdbc.username}
            ${jdbc.password}
            false
            ${jdbc.maxConnections}
            ${jdbc.minConnections}
            10000
            ${jdbc.minConnections}
            ${jdbc.validationQuery}
            false
            true
            1200000
            1800000
            5
          
        
      
    
      
        
          hibernate.dialect=${hibernate.dialect}
          hibernate.hbm2ddl.auto=${hibernate.hbm2ddl.auto}
          hibernate.jdbc.batch_size=2000
          hibernate.show_sql=${hibernate.show_sql}
          hibernate.transaction.auto_close_session=false
          hibernate.current_session_context_class=org.springframework.orm.hibernate3.SpringSessionContext
          hibernate.transaction.factory_class=org.springframework.orm.hibernate3.SpringTransactionFactory
        
        com.acme.ShopPackageImpl 
      
    
      
    
      
    
      
    
    
    

    Comments:

    1. VERY IMPORTANT:The HibernateTransactionManager does NOT get the teneoSessionFactory(HbSessionDataStore) object passed in as sessionFactory, but the underlying Hibernate SessionFactory that can be retrieved from the HbSessionDataStore via the getSessionFactory() method. Not doing so will result in duplicate Hibernate Sessions completely breaking the transaction managment. This happens because in that case the TransactionSynchronizationManager will sometimes internally bind the hibernate session to the current thread based on different keys and thus does not find it again. Here are some stacktraces showing the problem:
      TransactionSynchronizationManager.bindResource(Object, Object) line: 170	
      at OpenSessionInViewFilter.doFilterInternal(HttpServletRequest, HttpServletResponse, FilterChain) line: 183
      => results in TransactionSynchronizationManager.bindResource(HbSessionDataStore, SessionHolder) => as key the Teneo SessionFactory is used
      
      TransactionSynchronizationManager.bindResource(Object, Object) line: 170	
      at SessionFactoryUtils.doGetSession(SessionFactory, Interceptor, SQLExceptionTranslator, boolean) line: 339	
      at SessionFactoryUtils.doGetSession(SessionFactory, boolean) line: 256	
      at SpringSessionContext.currentSession() line: 60	
      at SessionFactoryImpl.getCurrentSession() line: 700	
      at HbSessionDataStore(HbBaseSessionDataStore).getCurrentSession() line: 161	
      => results in TransactionSynchronizationManager.bindResource(SessionFactoryImpl, SessionHolder) => as key the Hibernate SessionFactory is used
      
      TransactionSynchronizationManager.bindResource(Object, Object) line: 170	
      at HibernateTransactionManager.doBegin(Object, TransactionDefinition) line: 577	
      at HibernateTransactionManager(AbstractPlatformTransactionManager).getTransaction(TransactionDefinition) line: 371
      at TransactionInterceptor(TransactionAspectSupport).createTransactionIfNecessary(PlatformTransactionManager, TransactionAttribute, String) line: 316	
      at TransactionInterceptor.invoke(MethodInvocation) line: 105	
      at ReflectiveMethodInvocation.proceed() line: 172
      => results in TransactionSynchronizationManager.bindResource(HbSessionDataStore, SessionHolder) => as key the Teneo SessionFactory is used
      
    2. defaultAutoCommit is set to false to ensure that Spring has full control over the transaction and that the connection pool is not committing statements automatically based on it’s own strategies.
    3. The example uses an extended version of the org.eclipse.emf.teneo.hibernate.HbSessionDataStore class that allows the usage of a DataSource object and that configures Teneo to not alter the table, column names but let’s Hibernate handle this via its ImprovedNamingStrategy.
    4. hibernate.transaction.auto_close_session is set to false to avoid “SessionException: Session is closed!” in conjunction with the OpenSessionInViewListener.
    5. hibernate.current_session_context_class and hibernate.transaction.factory_class are set to special Spring implementations to avoid “org.hibernate.HibernateException: No CurrentSessionContext configured!” when not using Spring Hibernate Template via HibernateDAOSupport. It is not recommended to use the Hibernate Template anymore, see http://blog.springsource.com/2007/06/26/so-should-you-still-use-springs-hibernatetemplate-andor-jpatemplate/

    Comments are closed.