Hi All,
After many efforts,I have implemented multitenant with hibernate and Spring based using seperate schema approach.Iam providing the code for you.
If you need an introduction to hibernate multi-tenancy please refer to following hibernate documentation.
http://docs.jboss.org/hibernate/orm/4.2/devguide/en-US/html/ch16.html
So starting with Context file to initialize your sessionfactory and datasoure
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
class="in.sridhar.persistance.dao.SessionCurrentTenantIdentifierResolver">
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"
abstract="true"> destroy-method="close">
SessionCurrentTenantIdentifierResolver is used to resolve the current tenent during the establishment of connection to database.
Here Iam using ThreadLocal to identify my tenent id.
public class SessionCurrentTenantIdentifierResolver implements CurrentTenantIdentifierResolver
{
private static final Logger LOG = LoggerFactory
.getLogger(SessionCurrentTenantIdentifierResolver.class);
@Override
public String resolveCurrentTenantIdentifier()
{
String tenantId = ThreadContext.getDataSourceIdentifier();
System.out.println("Found TenantId=" + tenantId);
return tenantId;
}
@Override
public boolean validateExistingCurrentSessions()
{
return false;
}
}
MultiTenantSchemaConnectionProvider provides the implementation of getting and releasing conneciton from data source and setting up the schema specific to tenant identifier
public class MultiTenantSchemaConnectionProvider implements MultiTenantConnectionProvider
{
private static final long serialVersionUID = 4368575201221677384L;
@Autowired
private DataSource dataSource;
@Override
public boolean supportsAggressiveRelease()
{
return false;
}
@Override
public boolean isUnwrappableAs(Class clazz)
{
return false;
}
@Override
public T unwrap(Class clazz)
{
return null;
}
@Override
public Connection getAnyConnection() throws SQLException
{
return dataSource.getConnection();
}
@Override
public Connection getConnection(String tenantIdentifier) throws SQLException
{
final Connection connection = getAnyConnection();
try
{
System.out.println("inside sridharconnection :" + connection + " and tenantIdentifier "
+ tenantIdentifier);
connection.createStatement().execute("SET search_path to '" + tenantIdentifier + "'");
}
catch (SQLException e)
{
throw new HibernateException("Could not alter JDBC connection to specified schema ["
+ tenantIdentifier + "]", e);
}
return connection;
}
@Override
public void releaseAnyConnection(Connection connection) throws SQLException
{
try
{
connection.createStatement().execute("SET search_path to 'public'");
}
catch (SQLException e)
{
throw new HibernateException(
"Could not alter JDBC connection to specified schema [public]", e);
}
connection.close();
}
@Override
public void releaseConnection(String tenantIdentifier, Connection connection)
throws SQLException
{
releaseAnyConnection(connection);
}
}
ThreadContext class has the the logic of implementing thread local
public class ThreadContext
{
private static final ThreadLocal dataSourceIdentifier = new ThreadLocal();
public static String getDataSourceIdentifier()
{
return dataSourceIdentifier.get();
}
public static void setDataSourceIdentifier(String contextStr)
{
dataSourceIdentifier.set(contextStr);
}
public static void resetDataSourceIdentifier()
{
dataSourceIdentifier.remove();
}
}
Few things observed during the implementation of multi tenancy are:
1)hibernate.hbm2ddl.auto does work in multi tenancy approch
2) when you try to use
Session session = sessionFactory.withOptions() .tenantIdentifier( yourTenantIdentifier ) ... .openSession();
you will not able to use @transactional spring annotation.Because when we use this annotation spring internally uses session.getCurrentSession() which will not identify your tenent.
After many efforts,I have implemented multitenant with hibernate and Spring based using seperate schema approach.Iam providing the code for you.
If you need an introduction to hibernate multi-tenancy please refer to following hibernate documentation.
http://docs.jboss.org/hibernate/orm/4.2/devguide/en-US/html/ch16.html
So starting with Context file to initialize your sessionfactory and datasoure
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"
SessionCurrentTenantIdentifierResolver is used to resolve the current tenent during the establishment of connection to database.
Here Iam using ThreadLocal to identify my tenent id.
public class SessionCurrentTenantIdentifierResolver implements CurrentTenantIdentifierResolver
{
private static final Logger LOG = LoggerFactory
.getLogger(SessionCurrentTenantIdentifierResolver.class);
@Override
public String resolveCurrentTenantIdentifier()
{
String tenantId = ThreadContext.getDataSourceIdentifier();
System.out.println("Found TenantId=" + tenantId);
return tenantId;
}
@Override
public boolean validateExistingCurrentSessions()
{
return false;
}
}
MultiTenantSchemaConnectionProvider provides the implementation of getting and releasing conneciton from data source and setting up the schema specific to tenant identifier
public class MultiTenantSchemaConnectionProvider implements MultiTenantConnectionProvider
{
private static final long serialVersionUID = 4368575201221677384L;
@Autowired
private DataSource dataSource;
@Override
public boolean supportsAggressiveRelease()
{
return false;
}
@Override
public boolean isUnwrappableAs(Class clazz)
{
return false;
}
@Override
public
{
return null;
}
@Override
public Connection getAnyConnection() throws SQLException
{
return dataSource.getConnection();
}
@Override
public Connection getConnection(String tenantIdentifier) throws SQLException
{
final Connection connection = getAnyConnection();
try
{
System.out.println("inside sridharconnection :" + connection + " and tenantIdentifier "
+ tenantIdentifier);
connection.createStatement().execute("SET search_path to '" + tenantIdentifier + "'");
}
catch (SQLException e)
{
throw new HibernateException("Could not alter JDBC connection to specified schema ["
+ tenantIdentifier + "]", e);
}
return connection;
}
@Override
public void releaseAnyConnection(Connection connection) throws SQLException
{
try
{
connection.createStatement().execute("SET search_path to 'public'");
}
catch (SQLException e)
{
throw new HibernateException(
"Could not alter JDBC connection to specified schema [public]", e);
}
connection.close();
}
@Override
public void releaseConnection(String tenantIdentifier, Connection connection)
throws SQLException
{
releaseAnyConnection(connection);
}
}
ThreadContext class has the the logic of implementing thread local
public class ThreadContext
{
private static final ThreadLocal
public static String getDataSourceIdentifier()
{
return dataSourceIdentifier.get();
}
public static void setDataSourceIdentifier(String contextStr)
{
dataSourceIdentifier.set(contextStr);
}
public static void resetDataSourceIdentifier()
{
dataSourceIdentifier.remove();
}
}
Few things observed during the implementation of multi tenancy are:
1)hibernate.hbm2ddl.auto does work in multi tenancy approch
2) when you try to use
Session session = sessionFactory.withOptions() .tenantIdentifier( yourTenantIdentifier ) ... .openSession();
you will not able to use @transactional spring annotation.Because when we use this annotation spring internally uses session.getCurrentSession() which will not identify your tenent.
No comments:
Post a Comment