Hibernate Books

All times are UTC - 5 hours [ DST ]



Post new topic Reply to topic  [ 1 post ] 
Author Message
 Post subject: Multi-Tenancy and session pollution
PostPosted: Thu May 23, 2013 12:15 pm 
Newbie

Joined: Thu May 23, 2013 10:26 am
Posts: 1
Hey all. Having a lot of trouble getting multitenancy working with Hibernate 4, and after much board searching and googling I'm stuck.

We have an application where each client has their own schema- during the login, we determine which tenant they belong to and from then on the application needs to operate in that context. What we have works great… with just one user logged in. When we have multiple simultaneoususers, we get session pollution- one user sees data from another user’s tenant's schema, or is trying to load an object which only exists in another tenant. For example, a user in company1 tries to view an address of id#3, but instead is seeing the address of id#3 ... from a different database.

The way we have this structured:
  • A user logs in with a default tenant and based on their credentials we create a session scoped bean which stores their correct tenant.
  • The SchemaCurrentTenantIdentifierResolver uses that bean to pick the tenant (hibernate.tenant_identifier_resolver) for the sessionFactory.
  • The sessionFactory retrieves connections through the SchemaMultiTenantConnectionProvider (hibernate.multi_tenant_connection_provider)
  • The connection provider uses a single datasource, but prior to returning a connection it sets the catalog on that connection to match the tenant
  • We're using the OpenSessionInViewFilter to open and close the master session and @Transactional to join existing transactions in services which manipulate DAOs

    The normal flow is thus:
    • Request comes in
    • OpenSessionInView creates new session
    • Service calls DAO, which requests session
    • Response written. OpenSessionInView closes session

We really could not find an example of multitenancy implementation using the session per request pattern with OpenSessionInView. If anyone has an example that I could look at, or can spot some issue in the below, I would be eternally grateful, and happily sing your praises to the board :-p

application-context.xml
Code:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:...>
  <context:component-scan base-package="com.eb"/>
  <tx:annotation-driven transaction-manager="transactionManager"/>

  <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory"/>
    <property name="dataSource" ref="dataSource"/>
  </bean>

  <bean id="dataSource" class="com.jolbox.bonecp.BoneCPDataSource" destroy-method="close">
    <property name="driverClass" value="${jdbc.driverClassName}"/>
    <property name="jdbcUrl" value="${jdbc.databaseurl}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
  </bean>

  <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <property name="packagesToScan" value="com.opensys.data"/>
    <property name="hibernateProperties">
      <map>
        <entry key="hibernate.dialect" value="${hibernate.dialect}"/>
        <entry key="hibernate.multiTenancy" value="${hibernate.multiTenancy}"/>
        <entry key="hibernate.tenant_identifier_resolver" value-ref="multiTenantIdentifierResolver"/>
        <entry key="hibernate.multi_tenant_connection_provider" value-ref="multiTenantConnectionProvider"/>
      </map>
    </property>
  </bean>

  <bean id="multiTenantConnectionProvider" class="com.opensys.data.datasource.SchemaMultiTenantConnectionProvider">
    <property name="dataSource" ref="dataSource"/>
  </bean>
  <bean id="multiTenantIdentifierResolver" class="com.opensys.data.datasource.SchemaCurrentTenantIdentifierResolver" />
  <bean class="com.opensys.app.filter.DefaultTenantResolver" />


</beans>


BaseDAO (extended by all other DAOs)
Code:

@Transactional(propagation=Propagation.REQUIRED)
@Repository
public class BaseDAO <T extends Serializable> extends AbstractBaseDAO<T> {

   @Autowired
   private SessionFactory sessionFactory;


   public Session getSession() throws HibernateException {
      return sessionFactory.getCurrentSession();
   }
   
   
   public void save(T transientInstance) {
        try {
            getSession().save(transientInstance);
            getSession().flush();
        } catch (RuntimeException re) {
            throw re;
        }
    }
   
    //similar methods for merge/delete/reload/etc
}


SchemaMultiTenantConnectionProvider
Code:

/**
*
* This connection provider reuses a single connection pool by changing the schema of the connection it picks up.
*
*/
@SuppressWarnings("serial")
public class SchemaMultiTenantConnectionProvider extends AbstractDataSourceBasedMultiTenantConnectionProviderImpl {

   private DataSource dataSource;


   public void setDataSource(DataSource dataSource) {
      this.dataSource = dataSource;
   }

   /**
    * Returns the single dataSource
    */
   @Override
   protected DataSource selectAnyDataSource() {
      return dataSource;
   }

   /**
    * Returns the single dataSource
    */
   @Override
   protected DataSource selectDataSource(String tenantIdentifier) {
      return dataSource;
   }

   /**
    * Picks a connection and resets its schema based on the tenant provided.
    */
   @Override
   public Connection getConnection(String tenantIdentifier) throws SQLException {

        Connection connection = dataSource.getConnection();
       
        String connectionSchema = TenantContextManager.getTenant();
       
        try {
            connection.setCatalog(connectionSchema);
        } catch (Exception e) {
            e.printStackTrace();
            connection.setCatalog(TenantContextManager.getDefaultTenant());           
        }
      return connection;
   }

   /**
    * Picks a connection and resets its schema to <code>notenant</code>.
    */
   @Override
   public Connection getAnyConnection() throws SQLException {
      return getConnection(TenantContextManager.getDefaultTenant());
   }
   
   @Override
    public void releaseConnection(String tenantIdentifier, Connection connection) throws SQLException {
        connection.close();
    }
   
}


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 1 post ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.