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:
parent
5be294506d
commit
e391d8577e
|
@ -42,7 +42,7 @@ public class MultiTenantConnectionProviderInitiator implements StandardServiceIn
|
||||||
@SuppressWarnings( {"unchecked"})
|
@SuppressWarnings( {"unchecked"})
|
||||||
public MultiTenantConnectionProvider initiateService(Map configurationValues, ServiceRegistryImplementor registry) {
|
public MultiTenantConnectionProvider initiateService(Map configurationValues, ServiceRegistryImplementor registry) {
|
||||||
final MultiTenancyStrategy strategy = MultiTenancyStrategy.determineMultiTenancyStrategy( configurationValues );
|
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.
|
// nothing to do, but given the separate hierarchies have to handle this here.
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,7 +141,7 @@ public class JdbcEnvironmentInitiator implements StandardServiceInitiator<JdbcEn
|
||||||
final MultiTenancyStrategy multiTenancyStrategy = MultiTenancyStrategy.determineMultiTenancyStrategy(
|
final MultiTenancyStrategy multiTenancyStrategy = MultiTenancyStrategy.determineMultiTenancyStrategy(
|
||||||
configValues
|
configValues
|
||||||
);
|
);
|
||||||
if ( MultiTenancyStrategy.NONE == multiTenancyStrategy ) {
|
if ( !multiTenancyStrategy.requiresMultiTenantConnectionProvider() ) {
|
||||||
ConnectionProvider connectionProvider = registry.getService( ConnectionProvider.class );
|
ConnectionProvider connectionProvider = registry.getService( ConnectionProvider.class );
|
||||||
return new ConnectionProviderJdbcConnectionAccess( connectionProvider );
|
return new ConnectionProviderJdbcConnectionAccess( connectionProvider );
|
||||||
}
|
}
|
||||||
|
@ -154,7 +154,7 @@ public class JdbcEnvironmentInitiator implements StandardServiceInitiator<JdbcEn
|
||||||
public static JdbcConnectionAccess buildBootstrapJdbcConnectionAccess(
|
public static JdbcConnectionAccess buildBootstrapJdbcConnectionAccess(
|
||||||
MultiTenancyStrategy multiTenancyStrategy,
|
MultiTenancyStrategy multiTenancyStrategy,
|
||||||
ServiceRegistryImplementor registry) {
|
ServiceRegistryImplementor registry) {
|
||||||
if ( MultiTenancyStrategy.NONE == multiTenancyStrategy ) {
|
if ( !multiTenancyStrategy.requiresMultiTenantConnectionProvider() ) {
|
||||||
ConnectionProvider connectionProvider = registry.getService( ConnectionProvider.class );
|
ConnectionProvider connectionProvider = registry.getService( ConnectionProvider.class );
|
||||||
return new ConnectionProviderJdbcConnectionAccess( connectionProvider );
|
return new ConnectionProviderJdbcConnectionAccess( connectionProvider );
|
||||||
}
|
}
|
||||||
|
|
|
@ -445,7 +445,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
|
||||||
public JdbcConnectionAccess getJdbcConnectionAccess() {
|
public JdbcConnectionAccess getJdbcConnectionAccess() {
|
||||||
// See class-level JavaDocs for a discussion of the concurrent-access safety of this method
|
// See class-level JavaDocs for a discussion of the concurrent-access safety of this method
|
||||||
if ( jdbcConnectionAccess == null ) {
|
if ( jdbcConnectionAccess == null ) {
|
||||||
if ( MultiTenancyStrategy.NONE == factory.getSettings().getMultiTenancyStrategy() ) {
|
if ( !factory.getSettings().getMultiTenancyStrategy().requiresMultiTenantConnectionProvider() ) {
|
||||||
jdbcConnectionAccess = new NonContextualJdbcConnectionAccess(
|
jdbcConnectionAccess = new NonContextualJdbcConnectionAccess(
|
||||||
getEventListenerManager(),
|
getEventListenerManager(),
|
||||||
factory.getServiceRegistry().getService( ConnectionProvider.class )
|
factory.getServiceRegistry().getService( ConnectionProvider.class )
|
||||||
|
|
|
@ -424,14 +424,14 @@ public final class SessionFactoryImpl implements SessionFactoryImplementor {
|
||||||
return new JdbcConnectionAccess() {
|
return new JdbcConnectionAccess() {
|
||||||
@Override
|
@Override
|
||||||
public Connection obtainConnection() throws SQLException {
|
public Connection obtainConnection() throws SQLException {
|
||||||
return settings.getMultiTenancyStrategy() == MultiTenancyStrategy.NONE
|
return !settings.getMultiTenancyStrategy().requiresMultiTenantConnectionProvider()
|
||||||
? serviceRegistry.getService( ConnectionProvider.class ).getConnection()
|
? serviceRegistry.getService( ConnectionProvider.class ).getConnection()
|
||||||
: serviceRegistry.getService( MultiTenantConnectionProvider.class ).getAnyConnection();
|
: serviceRegistry.getService( MultiTenantConnectionProvider.class ).getAnyConnection();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void releaseConnection(Connection connection) throws SQLException {
|
public void releaseConnection(Connection connection) throws SQLException {
|
||||||
if ( settings.getMultiTenancyStrategy() == MultiTenancyStrategy.NONE ) {
|
if ( !settings.getMultiTenancyStrategy().requiresMultiTenantConnectionProvider() ) {
|
||||||
serviceRegistry.getService( ConnectionProvider.class ).closeConnection( connection );
|
serviceRegistry.getService( ConnectionProvider.class ).closeConnection( connection );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue