diff --git a/hibernate-core/src/main/java/org/hibernate/MultiTenancyStrategy.java b/hibernate-core/src/main/java/org/hibernate/MultiTenancyStrategy.java
new file mode 100644
index 0000000000..999b584057
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/MultiTenancyStrategy.java
@@ -0,0 +1,81 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2011, Red Hat Inc. or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Inc.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate;
+
+import java.util.Map;
+
+import org.hibernate.cfg.Environment;
+
+/**
+ * Describes the methods for multi-tenancy understood by Hibernate.
+ *
+ * @author Steve Ebersole
+ */
+public enum MultiTenancyStrategy {
+ /**
+ * Multi-tenancy implemented by use of discriminator columns.
+ */
+ DISCRIMINATOR,
+ /**
+ * Multi-tenancy implemented as separate schemas.
+ */
+ SCHEMA,
+ /**
+ * Multi-tenancy implemented as separate databases.
+ */
+ DATABASE,
+ /**
+ * No multi-tenancy
+ */
+ NONE;
+
+ public static MultiTenancyStrategy determineMultiTenancyStrategy(Map properties) {
+ final Object strategy = properties.get( Environment.MULTI_TENANT );
+ if ( strategy == null ) {
+ return MultiTenancyStrategy.NONE;
+ }
+
+ if ( MultiTenancyStrategy.class.isInstance( strategy ) ) {
+ return (MultiTenancyStrategy) strategy;
+ }
+
+ final String strategyName = strategy.toString();
+ if ( MultiTenancyStrategy.DISCRIMINATOR.name().equals( strategyName ) ) {
+ return MultiTenancyStrategy.DISCRIMINATOR;
+ }
+ else if ( MultiTenancyStrategy.SCHEMA.name().equals( strategyName ) ) {
+ return MultiTenancyStrategy.SCHEMA;
+ }
+ else if ( MultiTenancyStrategy.DATABASE.name().equals( strategyName ) ) {
+ return MultiTenancyStrategy.DATABASE;
+ }
+ else if ( MultiTenancyStrategy.NONE.name().equals( strategyName ) ) {
+ return MultiTenancyStrategy.NONE;
+ }
+ else {
+ // todo log?
+ return MultiTenancyStrategy.NONE;
+ }
+ }
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/SharedSessionContract.java b/hibernate-core/src/main/java/org/hibernate/SharedSessionContract.java
index e25ecb1d33..e318f3a219 100644
--- a/hibernate-core/src/main/java/org/hibernate/SharedSessionContract.java
+++ b/hibernate-core/src/main/java/org/hibernate/SharedSessionContract.java
@@ -31,6 +31,24 @@ import java.io.Serializable;
* @author Steve Ebersole
*/
public interface SharedSessionContract extends Serializable {
+ /**
+ * Obtain the tenant identifier associated with this session.
+ *
+ * @return The tenant identifier associated with this session, or {@code null}
+ */
+ public String getTenantIdentifier();
+
+ /**
+ * Should be set only once for the session. Would rather this be supplied to opening the session, as
+ * being discussed for HHH-2860
+ *
+ * @param identifier The tenant identifier.
+ *
+ * @deprecated HHH-2860
+ */
+ @Deprecated
+ public void setTenantIdentifier(String identifier);
+
/**
* Begin a unit of work and return the associated {@link Transaction} object. If a new underlying transaction is
* required, begin the transaction. Otherwise continue the new work in the context of the existing underlying
diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/Environment.java b/hibernate-core/src/main/java/org/hibernate/cfg/Environment.java
index cc3c8d36bc..a1b03f455b 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/Environment.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/Environment.java
@@ -551,6 +551,11 @@ public final class Environment {
*/
public static final String NON_CONTEXTUAL_LOB_CREATION = "hibernate.jdbc.lob.non_contextual_creation";
+ /**
+ * Strategy for multi-tenancy.
+ * @see org.hibernate.MultiTenancyStrategy
+ */
+ public static final String MULTI_TENANT = "hibernate.multiTenancy";
private static final BytecodeProvider BYTECODE_PROVIDER_INSTANCE;
private static final boolean ENABLE_BINARY_STREAMS;
diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/Settings.java b/hibernate-core/src/main/java/org/hibernate/cfg/Settings.java
index 5b9c416c25..3628b4d4ae 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/Settings.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/Settings.java
@@ -26,6 +26,7 @@ package org.hibernate.cfg;
import java.util.Map;
import org.hibernate.ConnectionReleaseMode;
import org.hibernate.EntityMode;
+import org.hibernate.MultiTenancyStrategy;
import org.hibernate.cache.QueryCacheFactory;
import org.hibernate.cache.RegionFactory;
import org.hibernate.hql.QueryTranslatorFactory;
@@ -81,6 +82,7 @@ public final class Settings {
// private ComponentTuplizerFactory componentTuplizerFactory; todo : HHH-3517 and HHH-1907
// private BytecodeProvider bytecodeProvider;
private String importFiles;
+ private MultiTenancyStrategy multiTenancyStrategy;
private JtaPlatform jtaPlatform;
@@ -438,4 +440,12 @@ public final class Settings {
void setJtaPlatform(JtaPlatform jtaPlatform) {
this.jtaPlatform = jtaPlatform;
}
+
+ public MultiTenancyStrategy getMultiTenancyStrategy() {
+ return multiTenancyStrategy;
+ }
+
+ void setMultiTenancyStrategy(MultiTenancyStrategy multiTenancyStrategy) {
+ this.multiTenancyStrategy = multiTenancyStrategy;
+ }
}
diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/SettingsFactory.java b/hibernate-core/src/main/java/org/hibernate/cfg/SettingsFactory.java
index fe355cc039..efcfec4ccf 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/SettingsFactory.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/SettingsFactory.java
@@ -30,6 +30,7 @@ import org.hibernate.ConnectionReleaseMode;
import org.hibernate.EntityMode;
import org.hibernate.HibernateException;
import org.hibernate.HibernateLogger;
+import org.hibernate.MultiTenancyStrategy;
import org.hibernate.cache.QueryCacheFactory;
import org.hibernate.cache.RegionFactory;
import org.hibernate.cache.impl.NoCachingRegionFactory;
@@ -241,6 +242,9 @@ public class SettingsFactory implements Serializable {
LOG.checkNullability(enabledDisabled(checkNullability));
settings.setCheckNullability(checkNullability);
+ MultiTenancyStrategy multiTenancyStrategy = MultiTenancyStrategy.determineMultiTenancyStrategy( properties );
+ LOG.debug( "multi-tenancy strategy : " + multiTenancyStrategy );
+ settings.setMultiTenancyStrategy( multiTenancyStrategy );
// String provider = properties.getProperty( Environment.BYTECODE_PROVIDER );
// log.info( "Bytecode provider name : " + provider );
@@ -292,7 +296,7 @@ public class SettingsFactory implements Serializable {
if ( regionFactoryClassName == null ) {
regionFactoryClassName = DEF_CACHE_REG_FACTORY;
}
- LOG.cacheRegionFactory(regionFactoryClassName);
+ LOG.cacheRegionFactory( regionFactoryClassName );
try {
try {
return (RegionFactory) ReflectHelper.classForName( regionFactoryClassName )
@@ -314,7 +318,7 @@ public class SettingsFactory implements Serializable {
String className = ConfigurationHelper.getString(
Environment.QUERY_TRANSLATOR, properties, "org.hibernate.hql.ast.ASTQueryTranslatorFactory"
);
- LOG.queryTranslator(className);
+ LOG.queryTranslator( className );
try {
return (QueryTranslatorFactory) ReflectHelper.classForName(className).newInstance();
}
diff --git a/hibernate-core/src/main/java/org/hibernate/engine/SessionFactoryImplementor.java b/hibernate-core/src/main/java/org/hibernate/engine/SessionFactoryImplementor.java
index d06b800ec4..fa751170e9 100644
--- a/hibernate-core/src/main/java/org/hibernate/engine/SessionFactoryImplementor.java
+++ b/hibernate-core/src/main/java/org/hibernate/engine/SessionFactoryImplementor.java
@@ -32,6 +32,7 @@ import org.hibernate.ConnectionReleaseMode;
import org.hibernate.HibernateException;
import org.hibernate.Interceptor;
import org.hibernate.MappingException;
+import org.hibernate.MultiTenancyStrategy;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.SessionFactoryObserver;
@@ -52,6 +53,7 @@ import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.proxy.EntityNotFoundDelegate;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.jdbc.connections.spi.ConnectionProvider;
+import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.hibernate.stat.spi.StatisticsImplementor;
import org.hibernate.type.Type;
import org.hibernate.type.TypeResolver;
@@ -255,7 +257,7 @@ public interface SessionFactoryImplementor extends Mapping, SessionFactory {
*/
public FetchProfile getFetchProfile(String name);
- public ServiceRegistry getServiceRegistry();
+ public ServiceRegistryImplementor getServiceRegistry();
public void addObserver(SessionFactoryObserver observer);
}
diff --git a/hibernate-core/src/main/java/org/hibernate/engine/SessionImplementor.java b/hibernate-core/src/main/java/org/hibernate/engine/SessionImplementor.java
index abcf983b75..19758ee96d 100644
--- a/hibernate-core/src/main/java/org/hibernate/engine/SessionImplementor.java
+++ b/hibernate-core/src/main/java/org/hibernate/engine/SessionImplementor.java
@@ -1,10 +1,10 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
- * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * Copyright (c) 2008-2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
- * distributed under license by Red Hat Middleware LLC.
+ * distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
@@ -20,7 +20,6 @@
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
- *
*/
package org.hibernate.engine;
@@ -39,6 +38,7 @@ import org.hibernate.ScrollMode;
import org.hibernate.ScrollableResults;
import org.hibernate.collection.PersistentCollection;
import org.hibernate.engine.jdbc.LobCreationContext;
+import org.hibernate.engine.jdbc.spi.JdbcConnectionAccess;
import org.hibernate.engine.query.sql.NativeSQLQuerySpecification;
import org.hibernate.engine.transaction.spi.TransactionCoordinator;
import org.hibernate.event.EventListeners;
@@ -47,16 +47,21 @@ import org.hibernate.loader.custom.CustomQuery;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.type.Type;
-
/**
- * Defines the internal contract between the Session and other parts of
- * Hibernate such as implementors of Type or EntityPersister.
+ * Defines the internal contract between {@link org.hibernate.Session} / {@link org.hibernate.StatelessSession} and
+ * other parts of Hibernate such as {@link Type}, {@link EntityPersister} and
+ * {@link org.hibernate.persister.collection.CollectionPersister} implementors
*
- * @see org.hibernate.Session the interface to the application
- * @see org.hibernate.impl.SessionImpl the actual implementation
* @author Gavin King
+ * @author Steve Ebersole
*/
public interface SessionImplementor extends Serializable, LobCreationContext {
+ /**
+ * Provides access to JDBC connections
+ *
+ * @return The contract for accessing JDBC connections.
+ */
+ public JdbcConnectionAccess getJdbcConnectionAccess();
/**
* Retrieves the interceptor currently in use by this event source.
diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java
index cc5125ac17..347ccb5875 100644
--- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java
+++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java
@@ -71,7 +71,8 @@ public class JdbcCoordinatorImpl implements JdbcCoordinator {
this.logicalConnection = new LogicalConnectionImpl(
userSuppliedConnection,
transactionCoordinator.getTransactionContext().getConnectionReleaseMode(),
- transactionCoordinator.getTransactionContext().getTransactionEnvironment().getJdbcServices()
+ transactionCoordinator.getTransactionContext().getTransactionEnvironment().getJdbcServices(),
+ transactionCoordinator.getTransactionContext().getJdbcConnectionAccess()
);
}
diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcServicesImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcServicesImpl.java
index 183293c6f9..400f575011 100644
--- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcServicesImpl.java
+++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcServicesImpl.java
@@ -22,21 +22,28 @@
* Boston, MA 02110-1301 USA
*/
package org.hibernate.engine.jdbc.internal;
+
import java.lang.reflect.InvocationTargetException;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
+import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
+
+import org.jboss.logging.Logger;
+
import org.hibernate.HibernateLogger;
+import org.hibernate.MultiTenancyStrategy;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.LobCreationContext;
import org.hibernate.engine.jdbc.LobCreator;
import org.hibernate.engine.jdbc.spi.ExtractedDatabaseMetaData;
+import org.hibernate.engine.jdbc.spi.JdbcConnectionAccess;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.jdbc.spi.ResultSetWrapper;
import org.hibernate.engine.jdbc.spi.SchemaNameResolver;
@@ -45,41 +52,39 @@ import org.hibernate.engine.jdbc.spi.SqlStatementLogger;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.service.jdbc.connections.spi.ConnectionProvider;
+import org.hibernate.service.jdbc.connections.spi.MultiTenantConnectionProvider;
import org.hibernate.service.jdbc.dialect.spi.DialectFactory;
import org.hibernate.service.spi.Configurable;
-import org.hibernate.service.spi.InjectService;
-import org.jboss.logging.Logger;
+import org.hibernate.service.spi.ServiceRegistryAwareService;
+import org.hibernate.service.spi.ServiceRegistryImplementor;
/**
* Standard implementation of the {@link JdbcServices} contract
*
* @author Steve Ebersole
*/
-public class JdbcServicesImpl implements JdbcServices, Configurable {
-
+public class JdbcServicesImpl implements JdbcServices, ServiceRegistryAwareService, Configurable {
private static final HibernateLogger LOG = Logger.getMessageLogger(HibernateLogger.class, JdbcServicesImpl.class.getName());
- private ConnectionProvider connectionProvider;
-
- @InjectService
- public void setConnectionProvider(ConnectionProvider connectionProvider) {
- this.connectionProvider = connectionProvider;
- }
-
- private DialectFactory dialectFactory;
-
- @InjectService
- public void setDialectFactory(DialectFactory dialectFactory) {
- this.dialectFactory = dialectFactory;
- }
+ private ServiceRegistryImplementor serviceRegistry;
private Dialect dialect;
+ private ConnectionProvider connectionProvider;
private SqlStatementLogger sqlStatementLogger;
private SqlExceptionHelper sqlExceptionHelper;
private ExtractedDatabaseMetaData extractedMetaDataSupport;
private LobCreatorBuilder lobCreatorBuilder;
+ @Override
+ public void injectServices(ServiceRegistryImplementor serviceRegistry) {
+ this.serviceRegistry = serviceRegistry;
+ }
+
+ @Override
public void configure(Map configValues) {
+ final JdbcConnectionAccess jdbcConnectionAccess = buildJdbcConnectionAccess( configValues );
+ final DialectFactory dialectFactory = serviceRegistry.getService( DialectFactory.class );
+
Dialect dialect = null;
LobCreatorBuilder lobCreatorBuilder = null;
@@ -105,9 +110,9 @@ public class JdbcServicesImpl implements JdbcServices, Configurable {
boolean useJdbcMetadata = ConfigurationHelper.getBoolean( "hibernate.temp.use_jdbc_metadata_defaults", configValues, true );
if ( useJdbcMetadata ) {
try {
- Connection conn = connectionProvider.getConnection();
+ Connection connection = jdbcConnectionAccess.obtainConnection();
try {
- DatabaseMetaData meta = conn.getMetaData();
+ DatabaseMetaData meta = connection.getMetaData();
LOG.database(meta.getDatabaseProductName(),
meta.getDatabaseProductVersion(),
meta.getDatabaseMajorVersion(),
@@ -128,25 +133,25 @@ public class JdbcServicesImpl implements JdbcServices, Configurable {
lobLocatorUpdateCopy = meta.locatorsUpdateCopy();
typeInfoSet.addAll( TypeInfoExtracter.extractTypeInfo( meta ) );
- dialect = dialectFactory.buildDialect( configValues, conn );
+ dialect = dialectFactory.buildDialect( configValues, connection );
- catalogName = conn.getCatalog();
+ catalogName = connection.getCatalog();
SchemaNameResolver schemaNameResolver = determineExplicitSchemaNameResolver( configValues );
if ( schemaNameResolver == null ) {
// todo : add dialect method
// schemaNameResolver = dialect.getSchemaNameResolver();
}
if ( schemaNameResolver != null ) {
- schemaName = schemaNameResolver.resolveSchemaName( conn );
+ schemaName = schemaNameResolver.resolveSchemaName( connection );
}
- lobCreatorBuilder = new LobCreatorBuilder( configValues, conn );
+ lobCreatorBuilder = new LobCreatorBuilder( configValues, connection );
}
catch ( SQLException sqle ) {
LOG.unableToObtainConnectionMetadata(sqle.getMessage());
}
finally {
- if ( conn != null ) {
- connectionProvider.closeConnection( conn );
+ if ( connection != null ) {
+ jdbcConnectionAccess.releaseConnection( connection );
}
}
}
@@ -190,6 +195,57 @@ public class JdbcServicesImpl implements JdbcServices, Configurable {
);
}
+ private JdbcConnectionAccess buildJdbcConnectionAccess(Map configValues) {
+ final MultiTenancyStrategy multiTenancyStrategy = MultiTenancyStrategy.determineMultiTenancyStrategy( configValues );
+
+ if ( MultiTenancyStrategy.NONE == multiTenancyStrategy ) {
+ connectionProvider = serviceRegistry.getService( ConnectionProvider.class );
+ return new ConnectionProviderJdbcConnectionAccess( connectionProvider );
+ }
+ else {
+ connectionProvider = null;
+ final MultiTenantConnectionProvider multiTenantConnectionProvider = serviceRegistry.getService( MultiTenantConnectionProvider.class );
+ return new MultiTenantConnectionProviderJdbcConnectionAccess( multiTenantConnectionProvider );
+ }
+ }
+
+ private static class ConnectionProviderJdbcConnectionAccess implements JdbcConnectionAccess {
+ private final ConnectionProvider connectionProvider;
+
+ public ConnectionProviderJdbcConnectionAccess(ConnectionProvider connectionProvider) {
+ this.connectionProvider = connectionProvider;
+ }
+
+ @Override
+ public Connection obtainConnection() throws SQLException {
+ return connectionProvider.getConnection();
+ }
+
+ @Override
+ public void releaseConnection(Connection connection) throws SQLException {
+ connection.close();
+ }
+ }
+
+ private static class MultiTenantConnectionProviderJdbcConnectionAccess implements JdbcConnectionAccess {
+ private final MultiTenantConnectionProvider connectionProvider;
+
+ public MultiTenantConnectionProviderJdbcConnectionAccess(MultiTenantConnectionProvider connectionProvider) {
+ this.connectionProvider = connectionProvider;
+ }
+
+ @Override
+ public Connection obtainConnection() throws SQLException {
+ return connectionProvider.getAnyConnection();
+ }
+
+ @Override
+ public void releaseConnection(Connection connection) throws SQLException {
+ connection.close();
+ }
+ }
+
+
// todo : add to Environment
public static final String SCHEMA_NAME_RESOLVER = "hibernate.schema_name_resolver";
@@ -220,9 +276,7 @@ public class JdbcServicesImpl implements JdbcServices, Configurable {
private Set parseKeywords(String extraKeywordsString) {
Set keywordSet = new HashSet();
- for ( String keyword : extraKeywordsString.split( "," ) ) {
- keywordSet.add( keyword );
- }
+ keywordSet.addAll( Arrays.asList( extraKeywordsString.split( "," ) ) );
return keywordSet;
}
@@ -278,81 +332,93 @@ public class JdbcServicesImpl implements JdbcServices, Configurable {
this.typeInfoSet = typeInfoSet;
}
+ @Override
public boolean supportsScrollableResults() {
return supportsScrollableResults;
}
+ @Override
public boolean supportsGetGeneratedKeys() {
return supportsGetGeneratedKeys;
}
+ @Override
public boolean supportsBatchUpdates() {
return supportsBatchUpdates;
}
+ @Override
public boolean supportsDataDefinitionInTransaction() {
return supportsDataDefinitionInTransaction;
}
+ @Override
public boolean doesDataDefinitionCauseTransactionCommit() {
return doesDataDefinitionCauseTransactionCommit;
}
+ @Override
public Set getExtraKeywords() {
return extraKeywords;
}
+ @Override
public SQLStateType getSqlStateType() {
return sqlStateType;
}
+ @Override
public boolean doesLobLocatorUpdateCopy() {
return lobLocatorUpdateCopy;
}
+ @Override
public String getConnectionSchemaName() {
return connectionSchemaName;
}
+ @Override
public String getConnectionCatalogName() {
return connectionCatalogName;
}
+ @Override
public LinkedHashSet getTypeInfoSet() {
return typeInfoSet;
}
}
+ @Override
public ConnectionProvider getConnectionProvider() {
return connectionProvider;
}
+ @Override
public SqlStatementLogger getSqlStatementLogger() {
return sqlStatementLogger;
}
+ @Override
public SqlExceptionHelper getSqlExceptionHelper() {
return sqlExceptionHelper;
}
+ @Override
public Dialect getDialect() {
return dialect;
}
+ @Override
public ExtractedDatabaseMetaData getExtractedMetaDataSupport() {
return extractedMetaDataSupport;
}
- /**
- * {@inheritDoc}
- */
+ @Override
public LobCreator getLobCreator(LobCreationContext lobCreationContext) {
return lobCreatorBuilder.buildLobCreator( lobCreationContext );
}
- /**
- * {@inheritDoc}
- */
+ @Override
public ResultSetWrapper getResultSetWrapper() {
return ResultSetWrapperImpl.INSTANCE;
}
diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/LogicalConnectionImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/LogicalConnectionImpl.java
index c6ed725dcf..8220ef8d09 100644
--- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/LogicalConnectionImpl.java
+++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/LogicalConnectionImpl.java
@@ -37,6 +37,7 @@ import org.hibernate.HibernateLogger;
import org.hibernate.JDBCException;
import org.hibernate.engine.jdbc.internal.proxy.ProxyBuilder;
import org.hibernate.engine.jdbc.spi.ConnectionObserver;
+import org.hibernate.engine.jdbc.spi.JdbcConnectionAccess;
import org.hibernate.engine.jdbc.spi.JdbcResourceRegistry;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.jdbc.spi.LogicalConnectionImplementor;
@@ -61,6 +62,7 @@ public class LogicalConnectionImpl implements LogicalConnectionImplementor {
private final transient ConnectionReleaseMode connectionReleaseMode;
private final transient JdbcServices jdbcServices;
+ private final transient JdbcConnectionAccess jdbcConnectionAccess;
private final transient JdbcResourceRegistry jdbcResourceRegistry;
private final transient List observers;
@@ -73,10 +75,12 @@ public class LogicalConnectionImpl implements LogicalConnectionImplementor {
public LogicalConnectionImpl(
Connection userSuppliedConnection,
ConnectionReleaseMode connectionReleaseMode,
- JdbcServices jdbcServices) {
+ JdbcServices jdbcServices,
+ JdbcConnectionAccess jdbcConnectionAccess) {
this(
connectionReleaseMode,
jdbcServices,
+ jdbcConnectionAccess,
(userSuppliedConnection != null),
false,
new ArrayList()
@@ -87,6 +91,7 @@ public class LogicalConnectionImpl implements LogicalConnectionImplementor {
private LogicalConnectionImpl(
ConnectionReleaseMode connectionReleaseMode,
JdbcServices jdbcServices,
+ JdbcConnectionAccess jdbcConnectionAccess,
boolean isUserSuppliedConnection,
boolean isClosed,
List observers) {
@@ -94,6 +99,7 @@ public class LogicalConnectionImpl implements LogicalConnectionImplementor {
jdbcServices, isUserSuppliedConnection, connectionReleaseMode
);
this.jdbcServices = jdbcServices;
+ this.jdbcConnectionAccess = jdbcConnectionAccess;
this.jdbcResourceRegistry = new JdbcResourceRegistryImpl( getJdbcServices().getSqlExceptionHelper() );
this.observers = observers;
@@ -286,7 +292,7 @@ public class LogicalConnectionImpl implements LogicalConnectionImplementor {
private void obtainConnection() throws JDBCException {
LOG.debugf("Obtaining JDBC connection");
try {
- physicalConnection = getJdbcServices().getConnectionProvider().getConnection();
+ physicalConnection = jdbcConnectionAccess.obtainConnection();
for ( ConnectionObserver observer : observers ) {
observer.physicalConnectionObtained( physicalConnection );
}
@@ -304,13 +310,19 @@ public class LogicalConnectionImpl implements LogicalConnectionImplementor {
*/
private void releaseConnection() throws JDBCException {
LOG.debugf("Releasing JDBC connection");
- if ( physicalConnection == null ) return;
- try {
- if (!physicalConnection.isClosed()) getJdbcServices().getSqlExceptionHelper().logAndClearWarnings(physicalConnection);
- if (!isUserSuppliedConnection) getJdbcServices().getConnectionProvider().closeConnection(physicalConnection);
+ if ( physicalConnection == null ) {
+ return;
}
- catch (SQLException sqle) {
- throw getJdbcServices().getSqlExceptionHelper().convert( sqle, "Could not close connection" );
+ try {
+ if ( ! physicalConnection.isClosed() ) {
+ getJdbcServices().getSqlExceptionHelper().logAndClearWarnings( physicalConnection );
+ }
+ if ( ! isUserSuppliedConnection ) {
+ jdbcConnectionAccess.releaseConnection( physicalConnection );
+ }
+ }
+ catch (SQLException e) {
+ throw getJdbcServices().getSqlExceptionHelper().convert( e, "Could not close connection" );
}
finally {
physicalConnection = null;
@@ -424,6 +436,7 @@ public class LogicalConnectionImpl implements LogicalConnectionImplementor {
return new LogicalConnectionImpl(
transactionContext.getConnectionReleaseMode(),
transactionContext.getTransactionEnvironment().getJdbcServices(),
+ transactionContext.getJdbcConnectionAccess(),
isUserSuppliedConnection,
isClosed,
observers
diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/ConnectionManager.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/ConnectionManager.java
deleted file mode 100644
index b76fcf467c..0000000000
--- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/ConnectionManager.java
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * Hibernate, Relational Persistence for Idiomatic Java
- *
- * Copyright (c) 2010, Red Hat Inc. or third-party contributors as
- * indicated by the @author tags or express copyright attribution
- * statements applied by the authors. All third-party contributions are
- * distributed under license by Red Hat Inc.
- *
- * This copyrighted material is made available to anyone wishing to use, modify,
- * copy, or redistribute it subject to the terms and conditions of the GNU
- * Lesser General Public License, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this distribution; if not, write to:
- * Free Software Foundation, Inc.
- * 51 Franklin Street, Fifth Floor
- * Boston, MA 02110-1301 USA
- */
-package org.hibernate.engine.jdbc.spi;
-import java.io.Serializable;
-import java.sql.CallableStatement;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import org.hibernate.HibernateException;
-import org.hibernate.ScrollMode;
-import org.hibernate.jdbc.Expectation;
-
-/**
- * Encapsulates JDBC Connection management SPI.
- *
- * The lifecycle is intended to span a logical series of interactions with the
- * database. Internally, this means the the lifecycle of the Session.
- *
- * @author Gail Badner
- */
-public interface ConnectionManager extends Serializable {
-
- /**
- * Retrieves the connection currently managed by this ConnectionManager.
- *
- * Note, that we may need to obtain a connection to return here if a
- * connection has either not yet been obtained (non-UserSuppliedConnectionProvider)
- * or has previously been aggressively released (if supported in this environment).
- *
- * @return The current Connection.
- *
- * @throws HibernateException Indicates a connection is currently not
- * available (we are currently manually disconnected).
- */
- Connection getConnection();
-
- // TODO: should this be removd from the SPI?
- boolean hasBorrowedConnection();
-
- // TODO: should this be removd from the SPI?
- void releaseBorrowedConnection();
-
- /**
- * Is this ConnectionManager instance "logically" connected. Meaning
- * do we either have a cached connection available or do we have the
- * ability to obtain a connection on demand.
- *
- * @return True if logically connected; false otherwise.
- */
- boolean isCurrentlyConnected();
-
- /**
- * To be called after execution of each JDBC statement. Used to
- * conditionally release the JDBC connection aggressively if
- * the configured release mode indicates.
- */
- void afterStatement();
-
- /**
- * Sets the transaction timeout.
- * @param seconds - number of seconds until the the transaction times out.
- */
- void setTransactionTimeout(int seconds);
-
- /**
- * To be called after Session completion. Used to release the JDBC
- * connection.
- *
- * @return The connection mantained here at time of close. Null if
- * there was no connection cached internally.
- */
- Connection close();
-
- /**
- * Manually disconnect the underlying JDBC Connection. The assumption here
- * is that the manager will be reconnected at a later point in time.
- *
- * @return The connection mantained here at time of disconnect. Null if
- * there was no connection cached internally.
- */
- Connection manualDisconnect();
-
- /**
- * Manually reconnect the underlying JDBC Connection. Should be called at
- * some point after manualDisconnect().
- *
- * This form is used for ConnectionProvider-supplied connections.
- */
- void manualReconnect();
-
- /**
- * Manually reconnect the underlying JDBC Connection. Should be called at
- * some point after manualDisconnect().
- *
- * This form is used for user-supplied connections.
- */
- void manualReconnect(Connection suppliedConnection);
-
- /**
- * Callback to let us know that a flush is beginning. We use this fact
- * to temporarily circumvent aggressive connection releasing until after
- * the flush cycle is complete {@link #flushEnding()}
- */
- void flushBeginning();
-
- /**
- * Callback to let us know that a flush is ending. We use this fact to
- * stop circumventing aggressive releasing connections.
- */
- void flushEnding();
-
- /**
- * Get a non-batchable prepared statement to use for inserting / deleting / updating,
- * using JDBC3 getGeneratedKeys ({@link java.sql.Connection#prepareStatement(String, int)}).
- *
- * If not explicitly closed via {@link java.sql.PreparedStatement#close()}, it will be
- * released when the session is closed or disconnected.
- */
- public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys);
-
- /**
- * Get a non-batchable prepared statement to use for inserting / deleting / updating.
- * using JDBC3 getGeneratedKeys ({@link java.sql.Connection#prepareStatement(String, String[])}).
- *
- * If not explicitly closed via {@link java.sql.PreparedStatement#close()}, it will be
- * released when the session is closed or disconnected.
- */
- public PreparedStatement prepareStatement(String sql, String[] columnNames);
-
- /**
- * Get a non-batchable prepared statement to use for selecting. Does not
- * result in execution of the current batch.
- *
- * If not explicitly closed via {@link java.sql.PreparedStatement#close()},
- * it will be released when the session is closed or disconnected.
- */
- public PreparedStatement prepareSelectStatement(String sql);
-
- /**
- * Get a non-batchable prepared statement to use for inserting / deleting / updating.
- *
- * If not explicitly closed via {@link java.sql.PreparedStatement#close()}, it will be
- * released when the session is closed or disconnected.
- */
- public PreparedStatement prepareStatement(String sql, boolean isCallable);
-
- /**
- * Get a non-batchable callable statement to use for inserting / deleting / updating.
- *
- * If not explicitly closed via {@link java.sql.PreparedStatement#close()}, it will be
- * released when the session is closed or disconnected.
- */
- public CallableStatement prepareCallableStatement(String sql);
-
- /**
- * Get a batchable prepared statement to use for inserting / deleting / updating
- * (might be called many times before a single call to executeBatch()).
- * After setting parameters, call addToBatch - do not execute the
- * statement explicitly.
- * @see org.hibernate.engine.jdbc.batch.spi.Batch#addToBatch
- *
- * If not explicitly closed via {@link java.sql.PreparedStatement#close()}, it will be
- * released when the session is closed or disconnected.
- */
- public PreparedStatement prepareBatchStatement(Object key, String sql, boolean isCallable);
-
- /**
- * Get a prepared statement for use in loading / querying. Does not
- * result in execution of the current batch.
- *
- * If not explicitly closed via {@link java.sql.PreparedStatement#close()},
- * it will be released when the session is closed or disconnected.
- */
- public PreparedStatement prepareQueryStatement(
- String sql,
- boolean isScrollable,
- ScrollMode scrollMode,
- boolean isCallable);
-
- /**
- * Cancel the current query statement
- */
- public void cancelLastQuery();
-
- public void abortBatch();
-
- public void addToBatch(Object batchKey, String sql, Expectation expectation);
-
- public void executeBatch();
-}
diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/JdbcConnectionAccess.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/JdbcConnectionAccess.java
new file mode 100644
index 0000000000..03b8438610
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/JdbcConnectionAccess.java
@@ -0,0 +1,39 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2011, Red Hat Inc. or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Inc.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.engine.jdbc.spi;
+
+import java.io.Serializable;
+import java.sql.Connection;
+import java.sql.SQLException;
+
+/**
+ * Provides centralized access to JDBC connections. Centralized to hide the complexity of accounting for contextual
+ * (multi-tenant) versus non-contextual access.
+ *
+ * @author Steve Ebersole
+ */
+public interface JdbcConnectionAccess extends Serializable {
+ public Connection obtainConnection() throws SQLException;
+ public void releaseConnection(Connection connection) throws SQLException;
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/engine/transaction/spi/TransactionContext.java b/hibernate-core/src/main/java/org/hibernate/engine/transaction/spi/TransactionContext.java
index 85be89fd6e..a172afd071 100644
--- a/hibernate-core/src/main/java/org/hibernate/engine/transaction/spi/TransactionContext.java
+++ b/hibernate-core/src/main/java/org/hibernate/engine/transaction/spi/TransactionContext.java
@@ -24,6 +24,7 @@
package org.hibernate.engine.transaction.spi;
import org.hibernate.ConnectionReleaseMode;
+import org.hibernate.engine.jdbc.spi.JdbcConnectionAccess;
import java.io.Serializable;
@@ -110,4 +111,6 @@ public interface TransactionContext extends Serializable {
public void beforeTransactionCompletion(TransactionImplementor hibernateTransaction);
public void afterTransactionCompletion(TransactionImplementor hibernateTransaction, boolean successful);
+
+ public JdbcConnectionAccess getJdbcConnectionAccess();
}
diff --git a/hibernate-core/src/main/java/org/hibernate/impl/AbstractSessionImpl.java b/hibernate-core/src/main/java/org/hibernate/impl/AbstractSessionImpl.java
index ad2ff42fbb..6375181110 100755
--- a/hibernate-core/src/main/java/org/hibernate/impl/AbstractSessionImpl.java
+++ b/hibernate-core/src/main/java/org/hibernate/impl/AbstractSessionImpl.java
@@ -30,11 +30,13 @@ import java.sql.SQLException;
import java.util.List;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
+import org.hibernate.MultiTenancyStrategy;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.ScrollableResults;
import org.hibernate.SessionException;
import org.hibernate.SharedSessionContract;
+import org.hibernate.engine.jdbc.spi.JdbcConnectionAccess;
import org.hibernate.engine.NamedQueryDefinition;
import org.hibernate.engine.NamedSQLQueryDefinition;
import org.hibernate.engine.QueryParameters;
@@ -48,6 +50,8 @@ import org.hibernate.engine.transaction.spi.TransactionContext;
import org.hibernate.engine.transaction.spi.TransactionEnvironment;
import org.hibernate.jdbc.WorkExecutor;
import org.hibernate.jdbc.WorkExecutorVisitable;
+import org.hibernate.service.jdbc.connections.spi.ConnectionProvider;
+import org.hibernate.service.jdbc.connections.spi.MultiTenantConnectionProvider;
/**
* Functionality common to stateless and stateful sessions
@@ -57,6 +61,7 @@ import org.hibernate.jdbc.WorkExecutorVisitable;
public abstract class AbstractSessionImpl implements Serializable, SharedSessionContract, SessionImplementor, TransactionContext {
protected transient SessionFactoryImpl factory;
+ private String tenantIdentifier;
private boolean closed = false;
protected AbstractSessionImpl(SessionFactoryImpl factory) {
@@ -203,4 +208,77 @@ public abstract class AbstractSessionImpl implements Serializable, SharedSession
return scrollCustomQuery( getNativeSQLQueryPlan( spec ).getCustomQuery(), queryParameters );
}
+ @Override
+ public String getTenantIdentifier() {
+ return tenantIdentifier;
+ }
+
+ @Override
+ public void setTenantIdentifier(String identifier) {
+ if ( MultiTenancyStrategy.NONE == factory.getSettings().getMultiTenancyStrategy() ) {
+ throw new HibernateException( "SessionFactory was not configured for multi-tenancy" );
+ }
+ this.tenantIdentifier = identifier;
+ }
+
+ private transient JdbcConnectionAccess jdbcConnectionAccess;
+
+ @Override
+ public JdbcConnectionAccess getJdbcConnectionAccess() {
+ if ( jdbcConnectionAccess == null ) {
+ if ( MultiTenancyStrategy.NONE == factory.getSettings().getMultiTenancyStrategy() ) {
+ jdbcConnectionAccess = new NonContextualJdbcConnectionAccess(
+ factory.getServiceRegistry().getService( ConnectionProvider.class )
+ );
+ }
+ else {
+ jdbcConnectionAccess = new ContextualJdbcConnectionAccess(
+ factory.getServiceRegistry().getService( MultiTenantConnectionProvider.class )
+ );
+ }
+ }
+ return jdbcConnectionAccess;
+ }
+
+ private static class NonContextualJdbcConnectionAccess implements JdbcConnectionAccess, Serializable {
+ private final ConnectionProvider connectionProvider;
+
+ private NonContextualJdbcConnectionAccess(ConnectionProvider connectionProvider) {
+ this.connectionProvider = connectionProvider;
+ }
+
+ @Override
+ public Connection obtainConnection() throws SQLException {
+ return connectionProvider.getConnection();
+ }
+
+ @Override
+ public void releaseConnection(Connection connection) throws SQLException {
+ connectionProvider.closeConnection( connection );
+ }
+ }
+
+ private class ContextualJdbcConnectionAccess implements JdbcConnectionAccess, Serializable {
+ private final MultiTenantConnectionProvider connectionProvider;
+
+ private ContextualJdbcConnectionAccess(MultiTenantConnectionProvider connectionProvider) {
+ this.connectionProvider = connectionProvider;
+ }
+
+ @Override
+ public Connection obtainConnection() throws SQLException {
+ if ( tenantIdentifier == null ) {
+ throw new HibernateException( "Tenant identifier required!" );
+ }
+ return connectionProvider.getConnection( tenantIdentifier );
+ }
+
+ @Override
+ public void releaseConnection(Connection connection) throws SQLException {
+ if ( tenantIdentifier == null ) {
+ throw new HibernateException( "Tenant identifier required!" );
+ }
+ connectionProvider.releaseConnection( tenantIdentifier, connection );
+ }
+ }
}
diff --git a/hibernate-core/src/main/java/org/hibernate/impl/SessionFactoryImpl.java b/hibernate-core/src/main/java/org/hibernate/impl/SessionFactoryImpl.java
index 478b376a55..5a360bd3af 100644
--- a/hibernate-core/src/main/java/org/hibernate/impl/SessionFactoryImpl.java
+++ b/hibernate-core/src/main/java/org/hibernate/impl/SessionFactoryImpl.java
@@ -53,6 +53,7 @@ import org.hibernate.HibernateException;
import org.hibernate.HibernateLogger;
import org.hibernate.Interceptor;
import org.hibernate.MappingException;
+import org.hibernate.MultiTenancyStrategy;
import org.hibernate.ObjectNotFoundException;
import org.hibernate.QueryException;
import org.hibernate.Session;
@@ -212,6 +213,7 @@ public final class SessionFactoryImpl
this.properties = new Properties();
this.properties.putAll( cfg.getProperties() );
this.interceptor = cfg.getInterceptor();
+
this.serviceRegistry = serviceRegistry.getService( SessionFactoryServiceRegistryFactory.class ).buildServiceRegistry(
this,
cfg
@@ -1260,10 +1262,11 @@ public final class SessionFactoryImpl
}
@Override
- public ServiceRegistry getServiceRegistry() {
+ public ServiceRegistryImplementor getServiceRegistry() {
return serviceRegistry;
}
+ @Override
public EntityNotFoundDelegate getEntityNotFoundDelegate() {
return entityNotFoundDelegate;
}
diff --git a/hibernate-core/src/main/java/org/hibernate/persister/internal/PersisterFactoryImpl.java b/hibernate-core/src/main/java/org/hibernate/persister/internal/PersisterFactoryImpl.java
index 4f34548b2c..7bf9741bad 100644
--- a/hibernate-core/src/main/java/org/hibernate/persister/internal/PersisterFactoryImpl.java
+++ b/hibernate-core/src/main/java/org/hibernate/persister/internal/PersisterFactoryImpl.java
@@ -38,6 +38,7 @@ import org.hibernate.persister.spi.PersisterClassResolver;
import org.hibernate.persister.spi.PersisterFactory;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.spi.ServiceRegistryAwareService;
+import org.hibernate.service.spi.ServiceRegistryImplementor;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
@@ -75,10 +76,10 @@ public final class PersisterFactoryImpl implements PersisterFactory, ServiceRegi
SessionFactoryImplementor.class
};
- private ServiceRegistry serviceRegistry;
+ private ServiceRegistryImplementor serviceRegistry;
@Override
- public void injectServices(ServiceRegistry serviceRegistry) {
+ public void injectServices(ServiceRegistryImplementor serviceRegistry) {
this.serviceRegistry = serviceRegistry;
}
diff --git a/hibernate-core/src/main/java/org/hibernate/service/StandardServiceInitiators.java b/hibernate-core/src/main/java/org/hibernate/service/StandardServiceInitiators.java
index 247e5199ea..44b2f91f3b 100644
--- a/hibernate-core/src/main/java/org/hibernate/service/StandardServiceInitiators.java
+++ b/hibernate-core/src/main/java/org/hibernate/service/StandardServiceInitiators.java
@@ -31,6 +31,7 @@ import org.hibernate.persister.internal.PersisterFactoryInitiator;
import org.hibernate.service.classloading.internal.ClassLoaderServiceInitiator;
import org.hibernate.service.internal.SessionFactoryServiceRegistryFactoryInitiator;
import org.hibernate.service.jdbc.connections.internal.ConnectionProviderInitiator;
+import org.hibernate.service.jdbc.connections.internal.MultiTenantConnectionProviderInitiator;
import org.hibernate.service.jdbc.dialect.internal.DialectFactoryInitiator;
import org.hibernate.service.jdbc.dialect.internal.DialectResolverInitiator;
import org.hibernate.service.jmx.internal.JmxServiceInitiator;
@@ -58,6 +59,7 @@ public class StandardServiceInitiators {
serviceInitiators.add( PersisterFactoryInitiator.INSTANCE );
serviceInitiators.add( ConnectionProviderInitiator.INSTANCE );
+ serviceInitiators.add( MultiTenantConnectionProviderInitiator.INSTANCE );
serviceInitiators.add( DialectResolverInitiator.INSTANCE );
serviceInitiators.add( DialectFactoryInitiator.INSTANCE );
serviceInitiators.add( BatchBuilderInitiator.INSTANCE );
diff --git a/hibernate-core/src/main/java/org/hibernate/service/internal/BasicServiceRegistryImpl.java b/hibernate-core/src/main/java/org/hibernate/service/internal/BasicServiceRegistryImpl.java
index bb02d014e0..f3b7b03f2e 100644
--- a/hibernate-core/src/main/java/org/hibernate/service/internal/BasicServiceRegistryImpl.java
+++ b/hibernate-core/src/main/java/org/hibernate/service/internal/BasicServiceRegistryImpl.java
@@ -1,7 +1,7 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
- * Copyright (c) 2010, Red Hat Inc. or third-party contributors as
+ * Copyright (c) 2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
@@ -125,12 +125,12 @@ public class BasicServiceRegistryImpl extends AbstractServiceRegistryImpl implem
protected void configureService(T service) {
applyInjections( service );
- if ( Configurable.class.isInstance( service ) ) {
- ( (Configurable) service ).configure( configurationValues );
- }
-
if ( ServiceRegistryAwareService.class.isInstance( service ) ) {
( (ServiceRegistryAwareService) service ).injectServices( this );
}
+
+ if ( Configurable.class.isInstance( service ) ) {
+ ( (Configurable) service ).configure( configurationValues );
+ }
}
}
diff --git a/hibernate-core/src/main/java/org/hibernate/service/jdbc/connections/internal/ConnectionProviderInitiator.java b/hibernate-core/src/main/java/org/hibernate/service/jdbc/connections/internal/ConnectionProviderInitiator.java
index a7cb84e174..dc4b21010b 100644
--- a/hibernate-core/src/main/java/org/hibernate/service/jdbc/connections/internal/ConnectionProviderInitiator.java
+++ b/hibernate-core/src/main/java/org/hibernate/service/jdbc/connections/internal/ConnectionProviderInitiator.java
@@ -36,6 +36,7 @@ import org.jboss.logging.Logger;
import org.hibernate.HibernateException;
import org.hibernate.HibernateLogger;
+import org.hibernate.MultiTenancyStrategy;
import org.hibernate.cfg.Environment;
import org.hibernate.internal.util.beans.BeanInfoHelper;
import org.hibernate.service.classloading.spi.ClassLoaderService;
@@ -100,6 +101,10 @@ public class ConnectionProviderInitiator implements BasicServiceInitiator {
+ public static final MultiTenantConnectionProviderInitiator INSTANCE = new MultiTenantConnectionProviderInitiator();
+
+ @Override
+ public Class getServiceInitiated() {
+ return MultiTenantConnectionProvider.class;
+ }
+
+ @Override
+ public MultiTenantConnectionProvider initiateService(Map configurationValues, ServiceRegistryImplementor registry) {
+ if ( MultiTenancyStrategy.determineMultiTenancyStrategy( configurationValues ) == MultiTenancyStrategy.NONE ) {
+ // nothing to do, but given the separate hierarchies have to handle this here.
+ }
+
+ // for now...
+ return null;
+ }
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/service/jdbc/connections/spi/AbstractMultiTenantConnectionProvider.java b/hibernate-core/src/main/java/org/hibernate/service/jdbc/connections/spi/AbstractMultiTenantConnectionProvider.java
new file mode 100644
index 0000000000..b2f8e614b9
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/service/jdbc/connections/spi/AbstractMultiTenantConnectionProvider.java
@@ -0,0 +1,80 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2011, Red Hat Inc. or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Inc.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.service.jdbc.connections.spi;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import org.hibernate.service.UnknownUnwrapTypeException;
+
+/**
+ * @author Steve Ebersole
+ */
+public abstract class AbstractMultiTenantConnectionProvider implements MultiTenantConnectionProvider {
+ protected abstract ConnectionProvider getAnyConnectionProvider();
+ protected abstract ConnectionProvider selectConnectionProvider(String tenantIdentifier);
+
+ @Override
+ public Connection getAnyConnection() throws SQLException {
+ return getAnyConnectionProvider().getConnection();
+ }
+
+ @Override
+ public void releaseAnyConnection(Connection connection) throws SQLException {
+ getAnyConnectionProvider().closeConnection( connection );
+ }
+
+ @Override
+ public Connection getConnection(String tenantIdentifier) throws SQLException {
+ return selectConnectionProvider( tenantIdentifier ).getConnection();
+ }
+
+ @Override
+ public void releaseConnection(String tenantIdentifier, Connection connection) throws SQLException {
+ selectConnectionProvider( tenantIdentifier ).getConnection();
+ }
+
+ @Override
+ public boolean supportsAggressiveRelease() {
+ return getAnyConnectionProvider().supportsAggressiveRelease();
+ }
+
+ @Override
+ public boolean isUnwrappableAs(Class unwrapType) {
+ return ConnectionProvider.class.equals( unwrapType ) ||
+ MultiTenantConnectionProvider.class.equals( unwrapType ) ||
+ AbstractMultiTenantConnectionProvider.class.isAssignableFrom( unwrapType );
+ }
+
+ @Override
+ @SuppressWarnings( {"unchecked"})
+ public T unwrap(Class unwrapType) {
+ if ( isUnwrappableAs( unwrapType ) ) {
+ return (T) this;
+ }
+ else {
+ throw new UnknownUnwrapTypeException( unwrapType );
+ }
+ }
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/service/jdbc/connections/spi/MultiTenantConnectionProvider.java b/hibernate-core/src/main/java/org/hibernate/service/jdbc/connections/spi/MultiTenantConnectionProvider.java
new file mode 100644
index 0000000000..29ce194997
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/service/jdbc/connections/spi/MultiTenantConnectionProvider.java
@@ -0,0 +1,95 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2011, Red Hat Inc. or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Inc.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.service.jdbc.connections.spi;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import org.hibernate.service.Service;
+import org.hibernate.service.spi.Wrapped;
+
+/**
+ * @author Steve Ebersole
+ */
+public interface MultiTenantConnectionProvider extends Service, Wrapped {
+ /**
+ * Allows access to the database metadata of the underlying database(s) in situations where we do not have a
+ * tenant id (like startup processing, for example).
+ *
+ * @return The database metadata.
+ *
+ * @throws SQLException Indicates a problem opening a connection
+ */
+ public Connection getAnyConnection() throws SQLException;
+
+ /**
+ * Release a connection obtained from {@link #getAnyConnection}
+ *
+ * @param connection The JDBC connection to release
+ *
+ * @throws SQLException Indicates a problem closing the connection
+ */
+ public void releaseAnyConnection(Connection connection) throws SQLException;
+
+ /**
+ * Obtains a connection for Hibernate use according to the underlying strategy of this provider.
+ *
+ * @param tenantIdentifier The identifier of the tenant for which to get a connection
+ *
+ * @return The obtained JDBC connection
+ *
+ * @throws SQLException Indicates a problem opening a connection
+ * @throws org.hibernate.HibernateException Indicates a problem otherwise obtaining a connection.
+ */
+ public Connection getConnection(String tenantIdentifier) throws SQLException;
+
+ /**
+ * Release a connection from Hibernate use.
+ *
+ * @param connection The JDBC connection to release
+ * @param tenantIdentifier The identifier of the tenant.
+ *
+ * @throws SQLException Indicates a problem closing the connection
+ * @throws org.hibernate.HibernateException Indicates a problem otherwise releasing a connection.
+ */
+ public void releaseConnection(String tenantIdentifier, Connection connection) throws SQLException;
+
+ /**
+ * Does this connection provider support aggressive release of JDBC
+ * connections and re-acquisition of those connections (if need be) later?
+ *
+ * This is used in conjunction with {@link org.hibernate.cfg.Environment#RELEASE_CONNECTIONS}
+ * to aggressively release JDBC connections. However, the configured ConnectionProvider
+ * must support re-acquisition of the same underlying connection for that semantic to work.
+ *
+ * Typically, this is only true in managed environments where a container
+ * tracks connections by transaction or thread.
+ *
+ * Note that JTA semantic depends on the fact that the underlying connection provider does
+ * support aggressive release.
+ *
+ * @return {@code true} if aggressive releasing is supported; {@code false} otherwise.
+ */
+ public boolean supportsAggressiveRelease();
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/service/jta/platform/internal/AbstractJtaPlatform.java b/hibernate-core/src/main/java/org/hibernate/service/jta/platform/internal/AbstractJtaPlatform.java
index b3200900ca..97e6e34d0a 100644
--- a/hibernate-core/src/main/java/org/hibernate/service/jta/platform/internal/AbstractJtaPlatform.java
+++ b/hibernate-core/src/main/java/org/hibernate/service/jta/platform/internal/AbstractJtaPlatform.java
@@ -29,6 +29,7 @@ import org.hibernate.service.jta.platform.spi.JtaPlatform;
import org.hibernate.service.spi.Configurable;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.spi.ServiceRegistryAwareService;
+import org.hibernate.service.spi.ServiceRegistryImplementor;
import javax.transaction.Synchronization;
import javax.transaction.SystemException;
@@ -44,10 +45,10 @@ public abstract class AbstractJtaPlatform
implements JtaPlatform, Configurable, ServiceRegistryAwareService, TransactionManagerAccess {
private boolean cacheTransactionManager;
private boolean cacheUserTransaction;
- private ServiceRegistry serviceRegistry;
+ private ServiceRegistryImplementor serviceRegistry;
@Override
- public void injectServices(ServiceRegistry serviceRegistry) {
+ public void injectServices(ServiceRegistryImplementor serviceRegistry) {
this.serviceRegistry = serviceRegistry;
}
diff --git a/hibernate-core/src/main/java/org/hibernate/service/spi/ServiceRegistryAwareService.java b/hibernate-core/src/main/java/org/hibernate/service/spi/ServiceRegistryAwareService.java
index 62941d4a61..633ee4d862 100644
--- a/hibernate-core/src/main/java/org/hibernate/service/spi/ServiceRegistryAwareService.java
+++ b/hibernate-core/src/main/java/org/hibernate/service/spi/ServiceRegistryAwareService.java
@@ -23,8 +23,6 @@
*/
package org.hibernate.service.spi;
-import org.hibernate.service.ServiceRegistry;
-
/**
* Allows services to be injected with the {@link org.hibernate.service.ServiceRegistry} during configuration phase.
*
@@ -36,5 +34,5 @@ public interface ServiceRegistryAwareService {
*
* @param serviceRegistry The registry
*/
- public void injectServices(ServiceRegistry serviceRegistry);
+ public void injectServices(ServiceRegistryImplementor serviceRegistry);
}
diff --git a/hibernate-core/src/main/java/org/hibernate/tool/hbm2ddl/ConnectionHelper.java b/hibernate-core/src/main/java/org/hibernate/tool/hbm2ddl/ConnectionHelper.java
index 141aefffb2..d1b1c6d5e8 100644
--- a/hibernate-core/src/main/java/org/hibernate/tool/hbm2ddl/ConnectionHelper.java
+++ b/hibernate-core/src/main/java/org/hibernate/tool/hbm2ddl/ConnectionHelper.java
@@ -32,7 +32,7 @@ import java.sql.SQLException;
*
* @author Steve Ebersole
*/
-interface ConnectionHelper {
+public interface ConnectionHelper {
/**
* Prepare the helper for use.
*
diff --git a/hibernate-core/src/main/java/org/hibernate/tool/hbm2ddl/SchemaExport.java b/hibernate-core/src/main/java/org/hibernate/tool/hbm2ddl/SchemaExport.java
index eba32ea470..438ce25ea1 100644
--- a/hibernate-core/src/main/java/org/hibernate/tool/hbm2ddl/SchemaExport.java
+++ b/hibernate-core/src/main/java/org/hibernate/tool/hbm2ddl/SchemaExport.java
@@ -157,6 +157,17 @@ public class SchemaExport {
);
}
+ public SchemaExport(
+ ConnectionHelper connectionHelper,
+ String[] dropSql,
+ String[] createSql) {
+ this.connectionHelper = connectionHelper;
+ this.dropSQL = dropSql;
+ this.createSQL = createSql;
+ this.importFiles = "";
+ this.formatter = FormatStyle.DDL.getFormatter();
+ }
+
/**
* For generating a export script file, this is the file which will be written.
*
diff --git a/hibernate-core/src/test/java/org/hibernate/test/common/JdbcConnectionAccessImpl.java b/hibernate-core/src/test/java/org/hibernate/test/common/JdbcConnectionAccessImpl.java
new file mode 100644
index 0000000000..d9042a7631
--- /dev/null
+++ b/hibernate-core/src/test/java/org/hibernate/test/common/JdbcConnectionAccessImpl.java
@@ -0,0 +1,61 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2011, Red Hat Inc. or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Inc.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.test.common;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import org.hibernate.engine.jdbc.spi.JdbcConnectionAccess;
+import org.hibernate.engine.transaction.spi.TransactionEnvironment;
+import org.hibernate.service.ServiceRegistry;
+import org.hibernate.service.jdbc.connections.spi.ConnectionProvider;
+
+/**
+ * @author Steve Ebersole
+ */
+public class JdbcConnectionAccessImpl implements JdbcConnectionAccess {
+ private final ConnectionProvider connectionProvider;
+
+ public JdbcConnectionAccessImpl(TransactionEnvironment transactionEnvironment) {
+ this( transactionEnvironment.getSessionFactory().getServiceRegistry() );
+ }
+
+ public JdbcConnectionAccessImpl(ConnectionProvider connectionProvider) {
+ this.connectionProvider = connectionProvider;
+ }
+
+ public JdbcConnectionAccessImpl(ServiceRegistry serviceRegistry) {
+ this( serviceRegistry.getService( ConnectionProvider.class ) );
+ }
+
+ @Override
+ public Connection obtainConnection() throws SQLException {
+ return connectionProvider.getConnection();
+ }
+
+ @Override
+ public void releaseConnection(Connection connection) throws SQLException {
+ connectionProvider.closeConnection( connection );
+ }
+}
diff --git a/hibernate-core/src/test/java/org/hibernate/test/common/TransactionContextImpl.java b/hibernate-core/src/test/java/org/hibernate/test/common/TransactionContextImpl.java
index 549294c789..16d1a95218 100644
--- a/hibernate-core/src/test/java/org/hibernate/test/common/TransactionContextImpl.java
+++ b/hibernate-core/src/test/java/org/hibernate/test/common/TransactionContextImpl.java
@@ -24,18 +24,30 @@
package org.hibernate.test.common;
import org.hibernate.ConnectionReleaseMode;
+import org.hibernate.engine.jdbc.spi.JdbcConnectionAccess;
import org.hibernate.engine.transaction.spi.TransactionContext;
import org.hibernate.engine.transaction.spi.TransactionEnvironment;
import org.hibernate.engine.transaction.spi.TransactionImplementor;
+import org.hibernate.service.ServiceRegistry;
/**
* @author Steve Ebersole
*/
public class TransactionContextImpl implements TransactionContext {
private final TransactionEnvironment transactionEnvironment;
+ private final JdbcConnectionAccess jdbcConnectionAccess;
+
+ public TransactionContextImpl(TransactionEnvironment transactionEnvironment, JdbcConnectionAccess jdbcConnectionAccess) {
+ this.transactionEnvironment = transactionEnvironment;
+ this.jdbcConnectionAccess = jdbcConnectionAccess;
+ }
+
+ public TransactionContextImpl(TransactionEnvironment transactionEnvironment, ServiceRegistry serviceRegistry) {
+ this( transactionEnvironment, new JdbcConnectionAccessImpl( serviceRegistry ) );
+ }
public TransactionContextImpl(TransactionEnvironment transactionEnvironment) {
- this.transactionEnvironment = transactionEnvironment;
+ this( transactionEnvironment, new JdbcConnectionAccessImpl( transactionEnvironment.getJdbcServices().getConnectionProvider() ) );
}
@Override
@@ -48,6 +60,11 @@ public class TransactionContextImpl implements TransactionContext {
return transactionEnvironment.getTransactionFactory().getDefaultReleaseMode();
}
+ @Override
+ public JdbcConnectionAccess getJdbcConnectionAccess() {
+ return jdbcConnectionAccess;
+ }
+
@Override
public boolean shouldAutoJoinTransaction() {
return true;
diff --git a/hibernate-core/src/test/java/org/hibernate/test/jdbc/proxies/AggressiveReleaseTest.java b/hibernate-core/src/test/java/org/hibernate/test/jdbc/proxies/AggressiveReleaseTest.java
index 91b0b2bad9..0546485242 100644
--- a/hibernate-core/src/test/java/org/hibernate/test/jdbc/proxies/AggressiveReleaseTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/test/jdbc/proxies/AggressiveReleaseTest.java
@@ -38,6 +38,7 @@ import org.junit.Test;
import org.hibernate.testing.junit4.BaseUnitTestCase;
import org.hibernate.test.common.BasicTestingJdbcServiceImpl;
+import org.hibernate.test.common.JdbcConnectionAccessImpl;
import org.hibernate.test.common.JournalingConnectionObserver;
import static org.junit.Assert.assertEquals;
@@ -112,7 +113,12 @@ public class AggressiveReleaseTest extends BaseUnitTestCase {
@Test
public void testBasicRelease() {
- LogicalConnectionImpl logicalConnection = new LogicalConnectionImpl( null, ConnectionReleaseMode.AFTER_STATEMENT, services );
+ LogicalConnectionImpl logicalConnection = new LogicalConnectionImpl(
+ null,
+ ConnectionReleaseMode.AFTER_STATEMENT,
+ services ,
+ new JdbcConnectionAccessImpl( services.getConnectionProvider() )
+ );
Connection proxiedConnection = ProxyBuilder.buildConnection( logicalConnection );
JournalingConnectionObserver observer = new JournalingConnectionObserver();
logicalConnection.addObserver( observer );
@@ -142,7 +148,12 @@ public class AggressiveReleaseTest extends BaseUnitTestCase {
@Test
public void testReleaseCircumventedByHeldResources() {
- LogicalConnectionImpl logicalConnection = new LogicalConnectionImpl( null, ConnectionReleaseMode.AFTER_STATEMENT, services );
+ LogicalConnectionImpl logicalConnection = new LogicalConnectionImpl(
+ null,
+ ConnectionReleaseMode.AFTER_STATEMENT,
+ services,
+ new JdbcConnectionAccessImpl( services.getConnectionProvider() )
+ );
Connection proxiedConnection = ProxyBuilder.buildConnection( logicalConnection );
JournalingConnectionObserver observer = new JournalingConnectionObserver();
logicalConnection.addObserver( observer );
@@ -196,7 +207,12 @@ public class AggressiveReleaseTest extends BaseUnitTestCase {
@Test
public void testReleaseCircumventedManually() {
- LogicalConnectionImpl logicalConnection = new LogicalConnectionImpl( null, ConnectionReleaseMode.AFTER_STATEMENT, services );
+ LogicalConnectionImpl logicalConnection = new LogicalConnectionImpl(
+ null,
+ ConnectionReleaseMode.AFTER_STATEMENT,
+ services,
+ new JdbcConnectionAccessImpl( services.getConnectionProvider() )
+ );
Connection proxiedConnection = ProxyBuilder.buildConnection( logicalConnection );
JournalingConnectionObserver observer = new JournalingConnectionObserver();
logicalConnection.addObserver( observer );
diff --git a/hibernate-core/src/test/java/org/hibernate/test/jdbc/proxies/BasicConnectionProxyTest.java b/hibernate-core/src/test/java/org/hibernate/test/jdbc/proxies/BasicConnectionProxyTest.java
index b41e42c62b..b7edfc9fe5 100644
--- a/hibernate-core/src/test/java/org/hibernate/test/jdbc/proxies/BasicConnectionProxyTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/test/jdbc/proxies/BasicConnectionProxyTest.java
@@ -41,6 +41,7 @@ import org.hibernate.JDBCException;
import org.hibernate.engine.jdbc.internal.LogicalConnectionImpl;
import org.hibernate.engine.jdbc.internal.proxy.ProxyBuilder;
import org.hibernate.test.common.BasicTestingJdbcServiceImpl;
+import org.hibernate.test.common.JdbcConnectionAccessImpl;
import org.hibernate.testing.junit4.BaseUnitTestCase;
/**
@@ -64,7 +65,8 @@ public class BasicConnectionProxyTest extends BaseUnitTestCase {
LogicalConnectionImpl logicalConnection = new LogicalConnectionImpl(
null,
ConnectionReleaseMode.AFTER_TRANSACTION,
- services
+ services,
+ new JdbcConnectionAccessImpl( services.getConnectionProvider() )
);
Connection proxiedConnection = ProxyBuilder.buildConnection( logicalConnection );
try {
@@ -92,7 +94,8 @@ public class BasicConnectionProxyTest extends BaseUnitTestCase {
LogicalConnectionImpl logicalConnection = new LogicalConnectionImpl(
null,
ConnectionReleaseMode.AFTER_TRANSACTION,
- services
+ services,
+ new JdbcConnectionAccessImpl( services.getConnectionProvider() )
);
Connection proxiedConnection = ProxyBuilder.buildConnection( logicalConnection );
try {
@@ -114,7 +117,8 @@ public class BasicConnectionProxyTest extends BaseUnitTestCase {
LogicalConnectionImpl logicalConnection = new LogicalConnectionImpl(
null,
ConnectionReleaseMode.AFTER_TRANSACTION,
- services
+ services,
+ new JdbcConnectionAccessImpl( services.getConnectionProvider() )
);
Connection proxiedConnection = ProxyBuilder.buildConnection( logicalConnection );
diff --git a/hibernate-core/src/test/java/org/hibernate/test/jdbc/proxies/BatchingTest.java b/hibernate-core/src/test/java/org/hibernate/test/jdbc/proxies/BatchingTest.java
index 26cd2739d5..2dad3fb5f1 100644
--- a/hibernate-core/src/test/java/org/hibernate/test/jdbc/proxies/BatchingTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/test/jdbc/proxies/BatchingTest.java
@@ -91,7 +91,9 @@ public class BatchingTest extends BaseUnitTestCase implements BatchKey {
@Test
public void testNonBatchingUsage() throws Exception {
- final TransactionContext transactionContext = new TransactionContextImpl( new TransactionEnvironmentImpl( serviceRegistry ) );
+ final TransactionContext transactionContext = new TransactionContextImpl(
+ new TransactionEnvironmentImpl( serviceRegistry )
+ );
TransactionCoordinatorImpl transactionCoordinator = new TransactionCoordinatorImpl( null, transactionContext );
JournalingTransactionObserver observer = new JournalingTransactionObserver();
diff --git a/hibernate-core/src/test/java/org/hibernate/test/multitenancy/schema/Customer.java b/hibernate-core/src/test/java/org/hibernate/test/multitenancy/schema/Customer.java
new file mode 100644
index 0000000000..cc1e06425e
--- /dev/null
+++ b/hibernate-core/src/test/java/org/hibernate/test/multitenancy/schema/Customer.java
@@ -0,0 +1,62 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2011, Red Hat Inc. or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Inc.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.test.multitenancy.schema;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+
+/**
+ * @author Steve Ebersole
+ */
+@Entity
+public class Customer {
+ private Long id;
+ private String name;
+
+ public Customer() {
+ }
+
+ public Customer(String name) {
+ this.name = name;
+ }
+
+ @Id
+ @GeneratedValue
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git a/hibernate-core/src/test/java/org/hibernate/test/multitenancy/schema/SchemaBasedMultiTenancyTest.java b/hibernate-core/src/test/java/org/hibernate/test/multitenancy/schema/SchemaBasedMultiTenancyTest.java
new file mode 100644
index 0000000000..05b8e38da3
--- /dev/null
+++ b/hibernate-core/src/test/java/org/hibernate/test/multitenancy/schema/SchemaBasedMultiTenancyTest.java
@@ -0,0 +1,206 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2011, Red Hat Inc. or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Inc.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.test.multitenancy.schema;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Map;
+
+import org.hibernate.HibernateException;
+import org.hibernate.MultiTenancyStrategy;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.service.ServiceRegistry;
+import org.hibernate.service.internal.BasicServiceRegistryImpl;
+import org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl;
+import org.hibernate.service.jdbc.connections.spi.AbstractMultiTenantConnectionProvider;
+import org.hibernate.service.jdbc.connections.spi.ConnectionProvider;
+import org.hibernate.service.jdbc.connections.spi.MultiTenantConnectionProvider;
+import org.hibernate.service.spi.ServiceRegistryImplementor;
+import org.hibernate.tool.hbm2ddl.ConnectionHelper;
+import org.hibernate.tool.hbm2ddl.SchemaExport;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import org.hibernate.testing.env.ConnectionProviderBuilder;
+import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
+import org.hibernate.testing.junit4.BaseUnitTestCase;
+
+/**
+ * @author Steve Ebersole
+ */
+public class SchemaBasedMultiTenancyTest extends BaseUnitTestCase {
+ private DriverManagerConnectionProviderImpl acmeProvider;
+ private DriverManagerConnectionProviderImpl jbossProvider;
+
+ private ServiceRegistryImplementor serviceRegistry;
+
+ private SessionFactory sessionFactory;
+
+ @Before
+ public void setUp() {
+ acmeProvider = ConnectionProviderBuilder.buildConnectionProvider( "acme" );
+ jbossProvider = ConnectionProviderBuilder.buildConnectionProvider( "jboss" );
+ AbstractMultiTenantConnectionProvider multiTenantConnectionProvider = new AbstractMultiTenantConnectionProvider() {
+ @Override
+ protected ConnectionProvider getAnyConnectionProvider() {
+ return acmeProvider;
+ }
+
+ @Override
+ protected ConnectionProvider selectConnectionProvider(String tenantIdentifier) {
+ if ( "acme".equals( tenantIdentifier ) ) {
+ return acmeProvider;
+ }
+ else if ( "jboss".equals( tenantIdentifier ) ) {
+ return jbossProvider;
+ }
+ throw new HibernateException( "Unknown tenant identifier" );
+ }
+ };
+
+ Configuration cfg = new Configuration();
+ cfg.getProperties().put( Environment.MULTI_TENANT, MultiTenancyStrategy.DATABASE );
+ cfg.addAnnotatedClass( Customer.class );
+
+ cfg.buildMappings();
+
+ // do the acme export
+ new SchemaExport(
+ new ConnectionHelper() {
+ private Connection connection;
+ @Override
+ public void prepare(boolean needsAutoCommit) throws SQLException {
+ connection = acmeProvider.getConnection();
+ }
+
+ @Override
+ public Connection getConnection() throws SQLException {
+ return connection;
+ }
+
+ @Override
+ public void release() throws SQLException {
+ acmeProvider.closeConnection( connection );
+ }
+ },
+ cfg.generateDropSchemaScript( ConnectionProviderBuilder.getCorrespondingDialect() ),
+ cfg.generateSchemaCreationScript( ConnectionProviderBuilder.getCorrespondingDialect() )
+ ).execute( // so stupid...
+ false, // do not script the export (write it to file)
+ true, // do run it against the database
+ false, // do not *just* perform the drop
+ false // do not *just* perform the create
+ );
+
+ // do the jboss export
+ new SchemaExport(
+ new ConnectionHelper() {
+ private Connection connection;
+ @Override
+ public void prepare(boolean needsAutoCommit) throws SQLException {
+ connection = jbossProvider.getConnection();
+ }
+
+ @Override
+ public Connection getConnection() throws SQLException {
+ return connection;
+ }
+
+ @Override
+ public void release() throws SQLException {
+ jbossProvider.closeConnection( connection );
+ }
+ },
+ cfg.generateDropSchemaScript( ConnectionProviderBuilder.getCorrespondingDialect() ),
+ cfg.generateSchemaCreationScript( ConnectionProviderBuilder.getCorrespondingDialect() )
+ ).execute( // so stupid...
+ false, // do not script the export (write it to file)
+ true, // do run it against the database
+ false, // do not *just* perform the drop
+ false // do not *just* perform the create
+ );
+
+ serviceRegistry = new BasicServiceRegistryImpl( cfg.getProperties() );
+ serviceRegistry.registerService( MultiTenantConnectionProvider.class, multiTenantConnectionProvider );
+
+ sessionFactory = cfg.buildSessionFactory( serviceRegistry );
+ }
+
+ @After
+ public void tearDown() {
+ if ( sessionFactory != null ) {
+ sessionFactory.close();
+ }
+ if ( serviceRegistry != null ) {
+ serviceRegistry.destroy();
+ }
+ if ( jbossProvider != null ) {
+ jbossProvider.stop();
+ }
+ if ( acmeProvider != null ) {
+ acmeProvider.stop();
+ }
+ }
+
+ private Session openSession() {
+ return sessionFactory.openSession();
+ }
+
+ @Test
+ public void testBasicExpectedBehavior() {
+ Session session = openSession();
+ session.setTenantIdentifier( "jboss" );
+ session.beginTransaction();
+ Customer steve = new Customer( "steve" );
+ session.save( steve );
+ session.getTransaction().commit();
+ session.close();
+
+ session = openSession();
+ try {
+ session.setTenantIdentifier( "acme" );
+ session.beginTransaction();
+ Customer check = (Customer) session.get( Customer.class, steve.getId() );
+ Assert.assertNull( "tenancy not properly isolated", check );
+ }
+ finally {
+ session.getTransaction().commit();
+ session.close();
+ }
+
+ session = openSession();
+ session.setTenantIdentifier( "jboss" );
+ session.beginTransaction();
+ session.delete( steve );
+ session.getTransaction().commit();
+ session.close();
+ }
+
+}
diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/env/ConnectionProviderBuilder.java b/hibernate-testing/src/main/java/org/hibernate/testing/env/ConnectionProviderBuilder.java
index 410653273c..443a13a346 100644
--- a/hibernate-testing/src/main/java/org/hibernate/testing/env/ConnectionProviderBuilder.java
+++ b/hibernate-testing/src/main/java/org/hibernate/testing/env/ConnectionProviderBuilder.java
@@ -29,7 +29,6 @@ import org.hibernate.cfg.Environment;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl;
-import org.hibernate.service.jdbc.connections.spi.ConnectionProvider;
/**
* Defines the JDBC connection information (currently H2) used by Hibernate for unit (not functional!) tests
@@ -38,24 +37,36 @@ import org.hibernate.service.jdbc.connections.spi.ConnectionProvider;
*/
public class ConnectionProviderBuilder {
public static final String DRIVER = "org.h2.Driver";
- public static final String URL = "jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE";
+ public static final String URL = "jdbc:h2:mem:%s;DB_CLOSE_DELAY=-1;MVCC=TRUE";
public static final String USER = "sa";
public static final String PASS = "";
- public static Properties getConnectionProviderProperties() {
+ public static Properties getConnectionProviderProperties(String dbName) {
Properties props = new Properties( null );
props.put( Environment.DRIVER, DRIVER );
- props.put( Environment.URL, URL );
+ props.put( Environment.URL, String.format( URL, dbName ) );
props.put( Environment.USER, USER );
+ props.put( Environment.PASS, PASS );
return props;
}
- public static ConnectionProvider buildConnectionProvider() {
+ public static Properties getConnectionProviderProperties() {
+ return getConnectionProviderProperties( "db1" );
+ }
+
+ public static DriverManagerConnectionProviderImpl buildConnectionProvider() {
return buildConnectionProvider( false );
}
- public static ConnectionProvider buildConnectionProvider(final boolean allowAggressiveRelease) {
- final Properties props = getConnectionProviderProperties();
+ public static DriverManagerConnectionProviderImpl buildConnectionProvider(String dbName) {
+ return buildConnectionProvider( getConnectionProviderProperties( dbName ), false );
+ }
+
+ public static DriverManagerConnectionProviderImpl buildConnectionProvider(final boolean allowAggressiveRelease) {
+ return buildConnectionProvider( getConnectionProviderProperties( "db1" ), allowAggressiveRelease );
+ }
+
+ private static DriverManagerConnectionProviderImpl buildConnectionProvider(Properties props, final boolean allowAggressiveRelease) {
DriverManagerConnectionProviderImpl connectionProvider = new DriverManagerConnectionProviderImpl() {
public boolean supportsAggressiveRelease() {
return allowAggressiveRelease;