HHH-11980 MultiTenantConnectionProvider is required for MultiTenancyStrategy.DISCRIMINATOR

Instead of checking that multiTenancyStrategy is NONE start using MultiTenancyStrategy.requiresMultiTenantConnectionProvider to distinguish whether MultiTenantConnectionProvider is to be used.
This commit is contained in:
msvticket 2017-09-25 13:26:25 +02:00 committed by Vlad Mihalcea
parent 5be294506d
commit e391d8577e
5 changed files with 202 additions and 6 deletions

View File

@ -42,7 +42,7 @@ public class MultiTenantConnectionProviderInitiator implements StandardServiceIn
@SuppressWarnings( {"unchecked"})
public MultiTenantConnectionProvider initiateService(Map configurationValues, ServiceRegistryImplementor registry) {
final MultiTenancyStrategy strategy = MultiTenancyStrategy.determineMultiTenancyStrategy( configurationValues );
if ( strategy == MultiTenancyStrategy.NONE || strategy == MultiTenancyStrategy.DISCRIMINATOR ) {
if ( !strategy.requiresMultiTenantConnectionProvider() ) {
// nothing to do, but given the separate hierarchies have to handle this here.
return null;
}

View File

@ -141,7 +141,7 @@ public class JdbcEnvironmentInitiator implements StandardServiceInitiator<JdbcEn
final MultiTenancyStrategy multiTenancyStrategy = MultiTenancyStrategy.determineMultiTenancyStrategy(
configValues
);
if ( MultiTenancyStrategy.NONE == multiTenancyStrategy ) {
if ( !multiTenancyStrategy.requiresMultiTenantConnectionProvider() ) {
ConnectionProvider connectionProvider = registry.getService( ConnectionProvider.class );
return new ConnectionProviderJdbcConnectionAccess( connectionProvider );
}
@ -154,7 +154,7 @@ public class JdbcEnvironmentInitiator implements StandardServiceInitiator<JdbcEn
public static JdbcConnectionAccess buildBootstrapJdbcConnectionAccess(
MultiTenancyStrategy multiTenancyStrategy,
ServiceRegistryImplementor registry) {
if ( MultiTenancyStrategy.NONE == multiTenancyStrategy ) {
if ( !multiTenancyStrategy.requiresMultiTenantConnectionProvider() ) {
ConnectionProvider connectionProvider = registry.getService( ConnectionProvider.class );
return new ConnectionProviderJdbcConnectionAccess( connectionProvider );
}

View File

@ -445,7 +445,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
public JdbcConnectionAccess getJdbcConnectionAccess() {
// See class-level JavaDocs for a discussion of the concurrent-access safety of this method
if ( jdbcConnectionAccess == null ) {
if ( MultiTenancyStrategy.NONE == factory.getSettings().getMultiTenancyStrategy() ) {
if ( !factory.getSettings().getMultiTenancyStrategy().requiresMultiTenantConnectionProvider() ) {
jdbcConnectionAccess = new NonContextualJdbcConnectionAccess(
getEventListenerManager(),
factory.getServiceRegistry().getService( ConnectionProvider.class )

View File

@ -424,14 +424,14 @@ public final class SessionFactoryImpl implements SessionFactoryImplementor {
return new JdbcConnectionAccess() {
@Override
public Connection obtainConnection() throws SQLException {
return settings.getMultiTenancyStrategy() == MultiTenancyStrategy.NONE
return !settings.getMultiTenancyStrategy().requiresMultiTenantConnectionProvider()
? serviceRegistry.getService( ConnectionProvider.class ).getConnection()
: serviceRegistry.getService( MultiTenantConnectionProvider.class ).getAnyConnection();
}
@Override
public void releaseConnection(Connection connection) throws SQLException {
if ( settings.getMultiTenancyStrategy() == MultiTenancyStrategy.NONE ) {
if ( !settings.getMultiTenancyStrategy().requiresMultiTenantConnectionProvider() ) {
serviceRegistry.getService( ConnectionProvider.class ).closeConnection( connection );
}
else {

View File

@ -0,0 +1,196 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.multitenancy.discriminator;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import org.hibernate.MultiTenancyStrategy;
import org.hibernate.Session;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.SessionFactoryBuilder;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Environment;
import org.hibernate.context.spi.CurrentTenantIdentifierResolver;
import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.mapping.RootClass;
import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.hibernate.tool.schema.internal.HibernateSchemaManagementTool;
import org.hibernate.tool.schema.internal.SchemaCreatorImpl;
import org.hibernate.tool.schema.internal.SchemaDropperImpl;
import org.hibernate.tool.schema.internal.exec.GenerationTargetToDatabase;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.cache.CachingRegionFactory;
import org.hibernate.testing.env.ConnectionProviderBuilder;
import org.hibernate.testing.junit4.BaseUnitTestCase;
import org.hibernate.testing.transaction.TransactionUtil;
import org.hibernate.test.multitenancy.schema.Customer;
import org.hibernate.test.util.DdlTransactionIsolatorTestingImpl;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* @author Mårten Svantesson
*/
@TestForIssue(jiraKey = "HHH-11980")
public class DiscriminatorMultiTenancyTest extends BaseUnitTestCase {
private SessionFactoryImplementor sessionFactory;
private DriverManagerConnectionProviderImpl connectionProvider;
private final TestCurrentTenantIdentifierResolver currentTenantResolver = new TestCurrentTenantIdentifierResolver();
@Before
public void setUp() {
Map settings = new HashMap();
settings.put(Environment.MULTI_TENANT, MultiTenancyStrategy.DISCRIMINATOR);
settings.put(Environment.MULTI_TENANT_IDENTIFIER_RESOLVER, currentTenantResolver);
settings.put(Environment.CACHE_REGION_FACTORY, CachingRegionFactory.class.getName());
settings.put(Environment.GENERATE_STATISTICS, "true");
ServiceRegistryImplementor serviceRegistry = (ServiceRegistryImplementor) new StandardServiceRegistryBuilder()
.applySettings(settings)
.build();
MetadataSources ms = new MetadataSources(serviceRegistry);
ms.addAnnotatedClass(Customer.class);
Metadata metadata = ms.buildMetadata();
((RootClass) metadata.getEntityBinding(Customer.class.getName())).setCacheConcurrencyStrategy("read-write");
HibernateSchemaManagementTool tool = new HibernateSchemaManagementTool();
tool.injectServices(serviceRegistry);
connectionProvider = ConnectionProviderBuilder.buildConnectionProvider();
final GenerationTargetToDatabase target = new GenerationTargetToDatabase(
new DdlTransactionIsolatorTestingImpl(
serviceRegistry,
connectionProvider
)
);
new SchemaDropperImpl(serviceRegistry).doDrop(
metadata,
serviceRegistry,
settings,
true,
target
);
new SchemaCreatorImpl(serviceRegistry).doCreation(
metadata,
serviceRegistry,
settings,
true,
target
);
target.release();
final SessionFactoryBuilder sfb = metadata.getSessionFactoryBuilder();
sessionFactory = (SessionFactoryImplementor) sfb.build();
}
@After
public void destroy() {
sessionFactory.close();
connectionProvider.stop();
}
public SessionFactoryImplementor sessionFactory() {
return sessionFactory;
}
/**
* This tests for current tenant being used for second level cache, but not selecting connection provider.
* Discrimination on connection level will for now need to be implemented in the supplied connection provider.
*/
@Test
public void testDiscriminator() {
doInHibernate( "jboss", session -> {
Customer steve = new Customer( 1L, "steve" );
session.save( steve );
} );
sessionFactory.getStatistics().clear();
// make sure we get the steve back, from cache if same tenant (jboss)
doInHibernate( "jboss", session -> {
Customer customer = session.load( Customer.class, 1L );
Assert.assertEquals( "steve", customer.getName() );
// also, make sure this came from second level
Assert.assertEquals( 1, sessionFactory.getStatistics().getSecondLevelCacheHitCount() );
} );
sessionFactory.getStatistics().clear();
// then make sure we get the steve back, from db if other tenant (acme)
doInHibernate( "acme", session -> {
Customer customer = session.load( Customer.class, 1L );
Assert.assertEquals( "steve", customer.getName() );
// also, make sure this doesn't came from second level
Assert.assertEquals( 0, sessionFactory.getStatistics().getSecondLevelCacheHitCount() );
} );
// make sure the same works from data store too
sessionFactory.getStatistics().clear();
sessionFactory.getCache().evictEntityRegions();
// first jboss
doInHibernate( "jboss", session -> {
Customer customer = session.load( Customer.class, 1L );
Assert.assertEquals( "steve", customer.getName() );
// also, make sure this doesn't came from second level
Assert.assertEquals( 0, sessionFactory.getStatistics().getSecondLevelCacheHitCount() );
} );
sessionFactory.getStatistics().clear();
// then, acme
doInHibernate( "acme", session -> {
Customer customer = session.load( Customer.class, 1L );
Assert.assertEquals( "steve", customer.getName() );
// also, make sure this doesn't came from second level
Assert.assertEquals( 0, sessionFactory.getStatistics().getSecondLevelCacheHitCount() );
} );
doInHibernate( "jboss", session -> {
Customer customer = session.load( Customer.class, 1L );
session.delete( customer );
} );
}
private static class TestCurrentTenantIdentifierResolver implements CurrentTenantIdentifierResolver {
private String currentTenantIdentifier;
@Override
public String resolveCurrentTenantIdentifier() {
return currentTenantIdentifier;
}
@Override
public boolean validateExistingCurrentSessions() {
return false;
}
}
public void doInHibernate(String tenant,
Consumer<Session> function) {
currentTenantResolver.currentTenantIdentifier = tenant;
TransactionUtil.doInHibernate( this::sessionFactory, tenant, function);
}
}