Announcement Announcement Module
Collapse
No announcement yet.
Dynamic Routing Hibernate SessionFactory Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Dynamic Routing Hibernate SessionFactory

    Hi all,

    I am currently working on a webservice that under the hood needs to provide data from/to several unterlying databases.
    To achieve that I am using a DAOs with a hibernateTemplate. The all inherit from a base class which looks like this
    Code:
    public abstract class BaseCrudDao<T extends Serializable> implements BaseDao<T> {
    
      private static final Logger LOGGER = Logger.getLogger(BaseCrudDao.class);
    
      protected HibernateTemplate hibernateTemplate;
    
      protected Class<T> clazz;
    
      public BaseCrudDao(Class<T> clazz) {
        super();
        this.clazz = clazz;
      }
    
      /**
       * 
       * @param sessionFactory
       */
      @Autowired
      public void initializeDao(SessionFactory sessionFactory) {
        hibernateTemplate = new HibernateTemplate(sessionFactory);
      }
    ....
    }
    Now in order to serve various databases, databse selection based on the incoming request, I was playing around with AbstractRoutingDatasource. MY hibernate configuration to achieve that looks like this.

    Code:
    <beans>
    ...
      <bean id="managingDataSource" class="test.DatabaseRoutingDataSource">
        <property name="targetDataSources">
          <map key-type="java.lang.String">
            <entry key="AX1" value-ref="childDataSource"/>
            <entry key="AX2" value-ref="childDataSource2"/>
          </map>
        </property>
        <property name="defaultTargetDataSource" ref="childDataSource" />
      </bean>
    
      <bean id="parentDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close" abstract="true">
        <property name="driverClass" value="${jdbc.driver.classname}" />
      </bean>
      
      <bean id="childDataSource" parent="parentDataSource"
        destroy-method="close">
        <property name="jdbcUrl" value="${jdbc.ax1.url}" />
        <property name="user" value="${jdbc.ax1.username}" />
        <property name="password" value="${jdbc.ax1.password}" />
      </bean>
    
      <bean id="childDataSource2" parent="parentDataSource"
        destroy-method="close">
        <property name="jdbcUrl" value="${jdbc.ax2.url}" />
        <property name="user" value="${jdbc.ax2.username}" />
        <property name="password" value="${jdbc.ax2.password}" />
      </bean>
      
      <bean id="sessionFactory" 
        class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="dataSource" ref="managingDataSource" />
        <property name="packagesToScan" value="test.ws" />
        <property name="hibernateProperties">
          <props>
            <prop key="hibernate.dialect">${jdbc.hibernate.dialect}</prop>
            <prop key="hibernate.hbm2ddl.auto">${jdbc.hibernate.hbm2ddl.auto}</prop>
            <prop key="hibernate.show_sql">${jdbc.hibernate.showSql}</prop>
          </props>
        </property>
      </bean>
    ...
    </beans>
    Now this seems a bit odd to me, first off all only the default datasource is initialized when the session Factory is instantiated. I get the reason why this is happening, still this is a unwanted side-effect.
    Secondly it feels a bit off when sessions from different datasources are provided by one SessionFactory, as all the hibernate configuration, intended for a single database instance is applied over all datasources, incl. e.g. the session count etc.
    So I wanted to implement a wrapper that implements the SessionFactory interface that more or less provides the AbstractRoutingDataSource functionality and looks as follows:

    Code:
    public class DatabaseRoutingSessionFactory implements SessionFactory {
    
      /**
       * 
       */
      private static final long serialVersionUID = 2257846544223138903L;
    
      private static final ThreadLocal<String> systemNameHolder = new ThreadLocal<String>();
    
      private Map<String, SessionFactory> sessionFactories;
    
      /**
       * 
       * @param systemName
       */
      public synchronized static void setSystemName(String systemName) {
        systemNameHolder.set(systemName);
      }
    
      /**
       * 
       * @return
       */
      public synchronized static String getSystemName() {
        return systemNameHolder.get();
      }
    
      /**
       * 
       */
      public synchronized static void clearSystemName() {
        systemNameHolder.remove();
      }
    
      /**
       * @return the sessionFactories
       */
      public synchronized Map<String, SessionFactory> getSessionFactories() {
        return sessionFactories;
      }
    
      /**
       * @param sessionFactories
       *          the sessionFactories to set
       */
      public synchronized void setSessionFactories(Map<String, SessionFactory> sessionFactories) {
        this.sessionFactories = sessionFactories;
      }
    
      @Override
      public synchronized Reference getReference() throws NamingException {
        return sessionFactories.get(getSystemName()).getReference();
      }
    
      @Override
      public synchronized void close() throws HibernateException {
        sessionFactories.get(getSystemName()).close();
      }
    
    //remaining implementation of SessionFactory interface
    
    .... 
    
    }
    and changes my hibernate-configuration accordingly:

    Code:
    <beans>
      ....
      <bean id="managingSessionFactory" class=test.DatabaseRoutingSessionFactory">
        <property name="sessionFactories">
          <map key-type="java.lang.String" value-type="org.hibernate.SessionFactory">
            <entry key="AX1" value-ref="sessionFactory1" />
            <entry key="AX2" value-ref="sessionFactory2" />
          </map>
        </property>
      </bean>
    
      <bean id="childDataSource1" class="com.mchange.v2.c3p0.ComboPooledDataSource"
        destroy-method="close">
        <property name="driverClass" value="${jdbc.driver.classname}" />
        <property name="jdbcUrl" value="${jdbc.ax1.url}" />
        <property name="user" value="${jdbc.ax1.username}" />
        <property name="password" value="${jdbc.ax1.password}" />
      </bean>
      
      <bean id="childDataSource2" class="com.mchange.v2.c3p0.ComboPooledDataSource"
        destroy-method="close">
        <property name="driverClass" value="${jdbc.driver.classname}" />
        <property name="jdbcUrl" value="${jdbc.ax2.url}" />
        <property name="user" value="${jdbc.ax2.username}" />
        <property name="password" value="${jdbc.ax2.password}" />
      </bean>
      
      <bean id="parentSessionFactory" abstract="true">
        <property name="packagesToScan" value="test" />
        <property name="hibernateProperties">
          <props>
            <prop key="hibernate.dialect">${jdbc.hibernate.dialect}</prop>
            <prop key="hibernate.hbm2ddl.auto">${jdbc.hibernate.hbm2ddl.auto}</prop>
            <prop key="hibernate.show_sql">${jdbc.hibernate.showSql}</prop>
          </props>
        </property>
      </bean>
      
      <bean id="sessionFactory1" 
        class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean" autowire-candidate="false">
        <property name="dataSource" ref="childDataSource1" />
      </bean>
      
      <bean id="sessionFactory2" 
        class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean" autowire-candidate="false">
        <property name="dataSource" ref="childDataSource2" />
      </bean>
      ...
    </beans>
    Do you have any ideas or negative implications I am missing out on?? Maybe even a Standard Spring solution exists for this problem?? Any input and help is more than appreciated!

    Thanks in advance!

    Iulius
Working...
X