I am currently trying to set up Hibernate for multi tenancy using the seperate Schema aproach.
After working on it for about 2 days now and browsing nearly every source
Using these guys' responses and this link, I put this together without Spring or anything else but C3P0.
I had to add these 2 properties to my hibernate config
properties.setProperty("hibernate.multiTenancy", "SCHEMA");
properties.setProperty("hibernate.multi_tenant_connection_provider", MultiTenantConnectionProviderImpl.class.getName());
HibernateUtils.java
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author Alex
*/
public class HibernateUtils {
private static final Logger logger = LoggerFactory.getLogger(HibernateUtils.class);
private static SessionFactory sessionFactory;
static{
init();
}
public static void init(){
try {
Configuration configuration = new Configuration()
.setProperties(ConnectionPropertiesUtils.getProperties());
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()).buildServiceRegistry();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
} catch (Exception e) {
logger.error(e.getMessage());
}
}
public static Session getTenantSession(String tenant){
return getSession(tenant);
}
public static Session getAuthSession(){
return getSession("AUTH");
}
public static Session getLogSession(){
return getSession("LOG");
}
public static Session getConfigSession(){
return getSession("CONFIG");
}
public static Session getSession(String tenant)
throws HibernateException {
if(sessionFactory == null){
init();
}
return sessionFactory.withOptions().tenantIdentifier(tenant).openSession();
}
@Deprecated
public static Session getSession()
throws HibernateException {
if(sessionFactory == null){
init();
}
return sessionFactory.openSession();
}
}
And MultiTenantConnectionProviderImpl.java
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.SQLException;
import org.hibernate.HibernateException;
import org.hibernate.service.UnknownUnwrapTypeException;
import org.hibernate.service.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.service.jdbc.connections.spi.MultiTenantConnectionProvider;
import org.hibernate.service.spi.Stoppable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Simplistic implementation for illustration purposes showing a single
* connection pool used to serve multiple schemas using "connection altering".
* Here we use the T-SQL specific USE command; Oracle users might use the ALTER
* SESSION SET SCHEMA command; etc.
*/
public class MultiTenantConnectionProviderImpl implements MultiTenantConnectionProvider, Stoppable {
private static Logger log = LoggerFactory.getLogger(MultiTenantConnectionProviderImpl.class);
private ComboPooledDataSource cpds;
public MultiTenantConnectionProviderImpl() throws PropertyVetoException {
log.info("Initializing Connection Pool!");
cpds = new ComboPooledDataSource("Example");
cpds.setDriverClass(ConnectionPropertiesUtils.getProperty("hibernate.connection.driver_class"));
cpds.setJdbcUrl(ConnectionPropertiesUtils.getProperty("hibernate.connection.url"));
cpds.setUser(ConnectionPropertiesUtils.getProperty("hibernate.connection.username"));
cpds.setPassword(ConnectionPropertiesUtils.getProperty("hibernate.connection.password"));
log.info("Connection Pool initialised!");
}
@Override
public Connection getAnyConnection() throws SQLException {
log.debug("Get Default Connection:::Number of connections (max: busy - idle): {} : {} - {}", new int[]{cpds.getMaxPoolSize(), cpds.getNumBusyConnectionsAllUsers(), cpds.getNumIdleConnectionsAllUsers()});
if (cpds.getNumConnectionsAllUsers() == cpds.getMaxPoolSize()) {
log.warn("Maximum number of connections opened");
}
if (cpds.getNumConnectionsAllUsers() == cpds.getMaxPoolSize() && cpds.getNumIdleConnectionsAllUsers() == 0) {
log.error("Connection pool empty!");
}
return cpds.getConnection();
}
@Override
public Connection getConnection(String tenantIdentifier) throws SQLException {
final Connection connection = getAnyConnection();
try {
//This is DB specific syntax. This work for MSSQL and MySQL
//Oracle uses the ALTER SESSION SET SCHEMA command
connection.createStatement().execute("USE " + 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 {
connection.close();
}
@Override
public void releaseConnection(String tenantIdentifier, Connection connection) {
try {
this.releaseAnyConnection(connection);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public boolean supportsAggressiveRelease() {
return false;
}
@SuppressWarnings("rawtypes")
@Override
public boolean isUnwrappableAs(Class unwrapType) {
return ConnectionProvider.class.equals(unwrapType) || MultiTenantConnectionProvider.class.equals(unwrapType) || MultiTenantConnectionProviderImpl.class.isAssignableFrom(unwrapType);
}
@SuppressWarnings("unchecked")
@Override
public T unwrap(Class unwrapType) {
if (isUnwrappableAs(unwrapType)) {
return (T) this;
} else {
throw new UnknownUnwrapTypeException(unwrapType);
}
}
public void stop() {
cpds.close();
}
}