HHH-11651 - unwrapping error in AbstractMultiTenantConnectionProvider
This commit is contained in:
parent
442f5e60dd
commit
6379a42a58
|
@ -47,16 +47,20 @@ public abstract class AbstractDataSourceBasedMultiTenantConnectionProviderImpl i
|
|||
|
||||
@Override
|
||||
public boolean isUnwrappableAs(Class unwrapType) {
|
||||
return MultiTenantConnectionProvider.class.equals( unwrapType ) ||
|
||||
AbstractMultiTenantConnectionProvider.class.isAssignableFrom( unwrapType );
|
||||
return
|
||||
DataSource.class.isAssignableFrom( unwrapType ) ||
|
||||
MultiTenantConnectionProvider.class.isAssignableFrom( unwrapType );
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings( {"unchecked"})
|
||||
public <T> T unwrap(Class<T> unwrapType) {
|
||||
if ( isUnwrappableAs( unwrapType ) ) {
|
||||
if ( MultiTenantConnectionProvider.class.isAssignableFrom( unwrapType ) ) {
|
||||
return (T) this;
|
||||
}
|
||||
else if ( DataSource.class.isAssignableFrom( unwrapType ) ) {
|
||||
return (T) selectAnyDataSource();
|
||||
}
|
||||
else {
|
||||
throw new UnknownUnwrapTypeException( unwrapType );
|
||||
}
|
||||
|
|
|
@ -48,17 +48,20 @@ public abstract class AbstractMultiTenantConnectionProvider implements MultiTena
|
|||
|
||||
@Override
|
||||
public boolean isUnwrappableAs(Class unwrapType) {
|
||||
return ConnectionProvider.class.equals( unwrapType ) ||
|
||||
MultiTenantConnectionProvider.class.equals( unwrapType ) ||
|
||||
AbstractMultiTenantConnectionProvider.class.isAssignableFrom( unwrapType );
|
||||
return
|
||||
ConnectionProvider.class.isAssignableFrom( unwrapType ) ||
|
||||
MultiTenantConnectionProvider.class.isAssignableFrom( unwrapType );
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public <T> T unwrap(Class<T> unwrapType) {
|
||||
if ( isUnwrappableAs( unwrapType ) ) {
|
||||
if ( MultiTenantConnectionProvider.class.isAssignableFrom( unwrapType ) ) {
|
||||
return (T) this;
|
||||
}
|
||||
else if ( ConnectionProvider.class.isAssignableFrom( unwrapType ) ) {
|
||||
return (T) getAnyConnectionProvider();
|
||||
}
|
||||
else {
|
||||
throw new UnknownUnwrapTypeException( unwrapType );
|
||||
}
|
||||
|
|
|
@ -0,0 +1,263 @@
|
|||
/*
|
||||
* 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.schema;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.MultiTenancyStrategy;
|
||||
import org.hibernate.SessionBuilder;
|
||||
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.engine.jdbc.connections.spi.ConnectionProvider;
|
||||
import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.mapping.RootClass;
|
||||
import org.hibernate.service.spi.ServiceRegistryImplementor;
|
||||
import org.hibernate.service.spi.Stoppable;
|
||||
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.cache.CachingRegionFactory;
|
||||
import org.hibernate.testing.junit4.BaseUnitTestCase;
|
||||
import org.hibernate.test.util.DdlTransactionIsolatorTestingImpl;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.core.Is.is;
|
||||
import static org.hamcrest.core.IsNull.notNullValue;
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernateSessionBuilder;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public abstract class AbstractSchemaBasedMultiTenancyTest<T extends MultiTenantConnectionProvider, C extends ConnectionProvider & Stoppable> extends BaseUnitTestCase {
|
||||
protected C acmeProvider;
|
||||
protected C jbossProvider;
|
||||
|
||||
protected ServiceRegistryImplementor serviceRegistry;
|
||||
|
||||
protected SessionFactoryImplementor sessionFactory;
|
||||
|
||||
protected SessionFactoryImplementor getSessionFactory() {
|
||||
return sessionFactory;
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
T multiTenantConnectionProvider = buildMultiTenantConnectionProvider();
|
||||
|
||||
Map settings = new HashMap();
|
||||
settings.put( Environment.MULTI_TENANT, MultiTenancyStrategy.SCHEMA );
|
||||
settings.put( Environment.CACHE_REGION_FACTORY, CachingRegionFactory.class.getName() );
|
||||
settings.put( Environment.GENERATE_STATISTICS, "true" );
|
||||
|
||||
serviceRegistry = (ServiceRegistryImplementor) new StandardServiceRegistryBuilder()
|
||||
.applySettings( settings )
|
||||
.addService( MultiTenantConnectionProvider.class, multiTenantConnectionProvider )
|
||||
.build();
|
||||
|
||||
MetadataSources ms = new MetadataSources( serviceRegistry );
|
||||
ms.addAnnotatedClass( Customer.class );
|
||||
ms.addAnnotatedClass( Invoice.class );
|
||||
|
||||
Metadata metadata = ms.buildMetadata();
|
||||
( (RootClass) metadata.getEntityBinding( Customer.class.getName() ) ).setCacheConcurrencyStrategy( "read-write" );
|
||||
|
||||
HibernateSchemaManagementTool tool = new HibernateSchemaManagementTool();
|
||||
tool.injectServices( serviceRegistry );
|
||||
|
||||
final GenerationTargetToDatabase acmeTarget = new GenerationTargetToDatabase(
|
||||
new DdlTransactionIsolatorTestingImpl(
|
||||
serviceRegistry,
|
||||
acmeProvider
|
||||
)
|
||||
);
|
||||
final GenerationTargetToDatabase jbossTarget = new GenerationTargetToDatabase(
|
||||
new DdlTransactionIsolatorTestingImpl(
|
||||
serviceRegistry,
|
||||
jbossProvider
|
||||
)
|
||||
);
|
||||
|
||||
new SchemaDropperImpl( serviceRegistry ).doDrop(
|
||||
metadata,
|
||||
serviceRegistry,
|
||||
settings,
|
||||
true,
|
||||
acmeTarget,
|
||||
jbossTarget
|
||||
);
|
||||
|
||||
new SchemaCreatorImpl( serviceRegistry ).doCreation(
|
||||
metadata,
|
||||
serviceRegistry,
|
||||
settings,
|
||||
true,
|
||||
acmeTarget,
|
||||
jbossTarget
|
||||
);
|
||||
|
||||
final SessionFactoryBuilder sfb = metadata.getSessionFactoryBuilder();
|
||||
configure( sfb );
|
||||
sessionFactory = (SessionFactoryImplementor) sfb.build();
|
||||
}
|
||||
|
||||
protected void configure(SessionFactoryBuilder sfb) {
|
||||
}
|
||||
|
||||
protected abstract T buildMultiTenantConnectionProvider();
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
if ( sessionFactory != null ) {
|
||||
sessionFactory.close();
|
||||
}
|
||||
if ( serviceRegistry != null ) {
|
||||
serviceRegistry.destroy();
|
||||
}
|
||||
if ( jbossProvider != null ) {
|
||||
jbossProvider.stop();
|
||||
}
|
||||
if ( acmeProvider != null ) {
|
||||
acmeProvider.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasicExpectedBehavior() {
|
||||
Customer steve = doInHibernateSessionBuilder( this::jboss, session -> {
|
||||
Customer _steve = new Customer( 1L, "steve" );
|
||||
session.save( _steve );
|
||||
return _steve;
|
||||
} );
|
||||
|
||||
doInHibernateSessionBuilder( this::acme, session -> {
|
||||
Customer check = session.get( Customer.class, steve.getId() );
|
||||
Assert.assertNull( "tenancy not properly isolated", check );
|
||||
} );
|
||||
|
||||
doInHibernateSessionBuilder( this::jboss, session -> {
|
||||
session.delete( steve );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSameIdentifiers() {
|
||||
// create a customer 'steve' in jboss
|
||||
Customer steve = doInHibernateSessionBuilder( this::jboss, session -> {
|
||||
Customer _steve = new Customer( 1L, "steve" );
|
||||
session.save( _steve );
|
||||
return _steve;
|
||||
} );
|
||||
|
||||
// now, create a customer 'john' in acme
|
||||
Customer john = doInHibernateSessionBuilder( this::acme, session -> {
|
||||
Customer _john = new Customer( 1L, "john" );
|
||||
session.save( _john );
|
||||
return _john;
|
||||
} );
|
||||
|
||||
sessionFactory.getStatistics().clear();
|
||||
|
||||
// make sure we get the correct people back, from cache
|
||||
// first, jboss
|
||||
doInHibernateSessionBuilder( this::jboss, session -> {
|
||||
Customer 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, acme
|
||||
doInHibernateSessionBuilder( this::acme, session -> {
|
||||
Customer customer = (Customer) session.load( Customer.class, 1L );
|
||||
Assert.assertEquals( "john", customer.getName() );
|
||||
// also, make sure this came from second level
|
||||
Assert.assertEquals( 1, sessionFactory.getStatistics().getSecondLevelCacheHitCount() );
|
||||
} );
|
||||
|
||||
// make sure the same works from datastore too
|
||||
sessionFactory.getStatistics().clear();
|
||||
sessionFactory.getCache().evictEntityRegions();
|
||||
// first jboss
|
||||
doInHibernateSessionBuilder( this::jboss, session -> {
|
||||
Customer customer = (Customer) session.load( Customer.class, 1L );
|
||||
Assert.assertEquals( "steve", customer.getName() );
|
||||
// also, make sure this came from second level
|
||||
Assert.assertEquals( 0, sessionFactory.getStatistics().getSecondLevelCacheHitCount() );
|
||||
} );
|
||||
|
||||
sessionFactory.getStatistics().clear();
|
||||
// then, acme
|
||||
doInHibernateSessionBuilder( this::acme, session -> {
|
||||
Customer customer = (Customer) session.load( Customer.class, 1L );
|
||||
Assert.assertEquals( "john", customer.getName() );
|
||||
// also, make sure this came from second level
|
||||
Assert.assertEquals( 0, sessionFactory.getStatistics().getSecondLevelCacheHitCount() );
|
||||
} );
|
||||
|
||||
doInHibernateSessionBuilder( this::jboss, session -> {
|
||||
session.delete( steve );
|
||||
} );
|
||||
|
||||
doInHibernateSessionBuilder( this::acme, session -> {
|
||||
session.delete( john );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTableIdentifiers() {
|
||||
Invoice orderJboss = doInHibernateSessionBuilder( this::jboss, session -> {
|
||||
Invoice _orderJboss = new Invoice();
|
||||
session.save( _orderJboss );
|
||||
Assert.assertEquals( Long.valueOf( 1 ), _orderJboss.getId() );
|
||||
return _orderJboss;
|
||||
} );
|
||||
|
||||
Invoice orderAcme = doInHibernateSessionBuilder( this::acme, session -> {
|
||||
Invoice _orderAcme = new Invoice();
|
||||
session.save( _orderAcme );
|
||||
Assert.assertEquals( Long.valueOf( 1 ), _orderAcme.getId() );
|
||||
return _orderAcme;
|
||||
} );
|
||||
|
||||
doInHibernateSessionBuilder( this::jboss, session -> {
|
||||
session.delete( orderJboss );
|
||||
} );
|
||||
|
||||
doInHibernateSessionBuilder( this::acme, session -> {
|
||||
session.delete( orderAcme );
|
||||
} );
|
||||
|
||||
sessionFactory.getStatistics().clear();
|
||||
}
|
||||
|
||||
protected SessionBuilder newSession(String tenant) {
|
||||
return sessionFactory
|
||||
.withOptions()
|
||||
.tenantIdentifier( tenant );
|
||||
}
|
||||
|
||||
private SessionBuilder jboss() {
|
||||
return newSession( "jboss" );
|
||||
}
|
||||
|
||||
private SessionBuilder acme() {
|
||||
return newSession( "acme" );
|
||||
}
|
||||
|
||||
}
|
|
@ -7,6 +7,7 @@
|
|||
package org.hibernate.test.multitenancy.schema;
|
||||
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.SessionBuilder;
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.hibernate.boot.SessionFactoryBuilder;
|
||||
import org.hibernate.context.spi.CurrentTenantIdentifierResolver;
|
||||
|
@ -35,13 +36,14 @@ public class CurrentTenantResolverMultiTenancyTest extends SchemaBasedMultiTenan
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Session getNewSession(String tenant) {
|
||||
protected SessionBuilder newSession(String tenant) {
|
||||
currentTenantResolver.currentTenantIdentifier = tenant;
|
||||
Session session = sessionFactory.openSession();
|
||||
SessionBuilder sessionBuilder = sessionFactory.withOptions();
|
||||
try(Session session = sessionBuilder.openSession()) {
|
||||
Assert.assertEquals( tenant, session.getTenantIdentifier() );
|
||||
return session;
|
||||
}
|
||||
|
||||
return sessionBuilder;
|
||||
}
|
||||
|
||||
private static class TestCurrentTenantIdentifierResolver implements CurrentTenantIdentifierResolver {
|
||||
private String currentTenantIdentifier;
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* 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.schema;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl;
|
||||
import org.hibernate.engine.jdbc.connections.spi.AbstractDataSourceBasedMultiTenantConnectionProviderImpl;
|
||||
import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider;
|
||||
|
||||
import org.hibernate.testing.RequiresDialectFeature;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.env.ConnectionProviderBuilder;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.core.Is.is;
|
||||
import static org.hamcrest.core.IsNull.notNullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@RequiresDialectFeature( value = ConnectionProviderBuilder.class )
|
||||
public class SchemaBasedDataSourceMultiTenancyTest extends AbstractSchemaBasedMultiTenancyTest<
|
||||
AbstractDataSourceBasedMultiTenantConnectionProviderImpl, DatasourceConnectionProviderImpl> {
|
||||
|
||||
protected AbstractDataSourceBasedMultiTenantConnectionProviderImpl buildMultiTenantConnectionProvider() {
|
||||
acmeProvider = ConnectionProviderBuilder.buildDataSourceConnectionProvider( "acme" );
|
||||
jbossProvider = ConnectionProviderBuilder.buildDataSourceConnectionProvider( "jboss" );
|
||||
return new AbstractDataSourceBasedMultiTenantConnectionProviderImpl() {
|
||||
@Override
|
||||
protected DataSource selectAnyDataSource() {
|
||||
return acmeProvider.unwrap( DataSource.class );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataSource selectDataSource(String tenantIdentifier) {
|
||||
if ( "acme".equals( tenantIdentifier ) ) {
|
||||
return acmeProvider.unwrap( DataSource.class );
|
||||
}
|
||||
else if ( "jboss".equals( tenantIdentifier ) ) {
|
||||
return jbossProvider.unwrap( DataSource.class );
|
||||
}
|
||||
throw new HibernateException( "Unknown tenant identifier" );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-11651")
|
||||
public void testUnwrappingConnectionProvider() {
|
||||
final MultiTenantConnectionProvider multiTenantConnectionProvider = serviceRegistry.getService(
|
||||
MultiTenantConnectionProvider.class );
|
||||
final DataSource dataSource = multiTenantConnectionProvider.unwrap( DataSource.class );
|
||||
assertThat( dataSource, is( notNullValue() ) );
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-11651")
|
||||
public void testUnwrappingAbstractMultiTenantConnectionProvider() {
|
||||
final MultiTenantConnectionProvider multiTenantConnectionProvider = serviceRegistry.getService(
|
||||
MultiTenantConnectionProvider.class );
|
||||
final AbstractDataSourceBasedMultiTenantConnectionProviderImpl dataSourceBasedMultiTenantConnectionProvider = multiTenantConnectionProvider.unwrap(
|
||||
AbstractDataSourceBasedMultiTenantConnectionProviderImpl.class );
|
||||
assertThat( dataSourceBasedMultiTenantConnectionProvider, is( notNullValue() ) );
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-11651")
|
||||
public void testUnwrappingMultiTenantConnectionProvider() {
|
||||
final MultiTenantConnectionProvider multiTenantConnectionProvider = serviceRegistry.getService(
|
||||
MultiTenantConnectionProvider.class );
|
||||
final MultiTenantConnectionProvider connectionProvider = multiTenantConnectionProvider.unwrap(
|
||||
MultiTenantConnectionProvider.class );
|
||||
assertThat( connectionProvider, is( notNullValue() ) );
|
||||
}
|
||||
}
|
|
@ -6,115 +6,29 @@
|
|||
*/
|
||||
package org.hibernate.test.multitenancy.schema;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
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.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl;
|
||||
import org.hibernate.engine.jdbc.connections.spi.AbstractMultiTenantConnectionProvider;
|
||||
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
|
||||
import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider;
|
||||
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.RequiresDialectFeature;
|
||||
import org.hibernate.testing.cache.CachingRegionFactory;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.env.ConnectionProviderBuilder;
|
||||
import org.hibernate.testing.junit4.BaseUnitTestCase;
|
||||
import org.hibernate.test.util.DdlTransactionIsolatorTestingImpl;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.core.Is.is;
|
||||
import static org.hamcrest.core.IsNull.notNullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@RequiresDialectFeature( value = ConnectionProviderBuilder.class )
|
||||
public class SchemaBasedMultiTenancyTest extends BaseUnitTestCase {
|
||||
private DriverManagerConnectionProviderImpl acmeProvider;
|
||||
private DriverManagerConnectionProviderImpl jbossProvider;
|
||||
public class SchemaBasedMultiTenancyTest extends AbstractSchemaBasedMultiTenancyTest<
|
||||
AbstractMultiTenantConnectionProvider, DriverManagerConnectionProviderImpl> {
|
||||
|
||||
private ServiceRegistryImplementor serviceRegistry;
|
||||
|
||||
protected SessionFactoryImplementor sessionFactory;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
AbstractMultiTenantConnectionProvider multiTenantConnectionProvider = buildMultiTenantConnectionProvider();
|
||||
|
||||
Map settings = new HashMap();
|
||||
settings.put( Environment.MULTI_TENANT, MultiTenancyStrategy.SCHEMA );
|
||||
settings.put( Environment.CACHE_REGION_FACTORY, CachingRegionFactory.class.getName() );
|
||||
settings.put( Environment.GENERATE_STATISTICS, "true" );
|
||||
|
||||
serviceRegistry = (ServiceRegistryImplementor) new StandardServiceRegistryBuilder()
|
||||
.applySettings( settings )
|
||||
.addService( MultiTenantConnectionProvider.class, multiTenantConnectionProvider )
|
||||
.build();
|
||||
|
||||
MetadataSources ms = new MetadataSources( serviceRegistry );
|
||||
ms.addAnnotatedClass( Customer.class );
|
||||
ms.addAnnotatedClass( Invoice.class );
|
||||
|
||||
Metadata metadata = ms.buildMetadata();
|
||||
( (RootClass) metadata.getEntityBinding( Customer.class.getName() ) ).setCacheConcurrencyStrategy( "read-write" );
|
||||
|
||||
HibernateSchemaManagementTool tool = new HibernateSchemaManagementTool();
|
||||
tool.injectServices( serviceRegistry );
|
||||
|
||||
final GenerationTargetToDatabase acmeTarget = new GenerationTargetToDatabase(
|
||||
new DdlTransactionIsolatorTestingImpl(
|
||||
serviceRegistry,
|
||||
acmeProvider
|
||||
)
|
||||
);
|
||||
final GenerationTargetToDatabase jbossTarget = new GenerationTargetToDatabase(
|
||||
new DdlTransactionIsolatorTestingImpl(
|
||||
serviceRegistry,
|
||||
jbossProvider
|
||||
)
|
||||
);
|
||||
|
||||
new SchemaDropperImpl( serviceRegistry ).doDrop(
|
||||
metadata,
|
||||
serviceRegistry,
|
||||
settings,
|
||||
true,
|
||||
acmeTarget,
|
||||
jbossTarget
|
||||
);
|
||||
|
||||
new SchemaCreatorImpl( serviceRegistry ).doCreation(
|
||||
metadata,
|
||||
serviceRegistry,
|
||||
settings,
|
||||
true,
|
||||
acmeTarget,
|
||||
jbossTarget
|
||||
);
|
||||
|
||||
final SessionFactoryBuilder sfb = metadata.getSessionFactoryBuilder();
|
||||
configure( sfb );
|
||||
sessionFactory = (SessionFactoryImplementor) sfb.build();
|
||||
}
|
||||
|
||||
protected void configure(SessionFactoryBuilder sfb) {
|
||||
}
|
||||
|
||||
private AbstractMultiTenantConnectionProvider buildMultiTenantConnectionProvider() {
|
||||
protected AbstractMultiTenantConnectionProvider buildMultiTenantConnectionProvider() {
|
||||
acmeProvider = ConnectionProviderBuilder.buildConnectionProvider( "acme" );
|
||||
jbossProvider = ConnectionProviderBuilder.buildConnectionProvider( "jboss" );
|
||||
return new AbstractMultiTenantConnectionProvider() {
|
||||
|
@ -136,169 +50,32 @@ public class SchemaBasedMultiTenancyTest extends BaseUnitTestCase {
|
|||
};
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
if ( sessionFactory != null ) {
|
||||
sessionFactory.close();
|
||||
}
|
||||
if ( serviceRegistry != null ) {
|
||||
serviceRegistry.destroy();
|
||||
}
|
||||
if ( jbossProvider != null ) {
|
||||
jbossProvider.stop();
|
||||
}
|
||||
if ( acmeProvider != null ) {
|
||||
acmeProvider.stop();
|
||||
}
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-11651")
|
||||
public void testUnwrappingConnectionProvider() {
|
||||
final MultiTenantConnectionProvider multiTenantConnectionProvider = serviceRegistry.getService(
|
||||
MultiTenantConnectionProvider.class );
|
||||
final ConnectionProvider connectionProvider = multiTenantConnectionProvider.unwrap( ConnectionProvider.class );
|
||||
assertThat( connectionProvider, is( notNullValue() ) );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasicExpectedBehavior() {
|
||||
Session session = getNewSession("jboss");
|
||||
session.beginTransaction();
|
||||
Customer steve = new Customer( 1L, "steve" );
|
||||
session.save( steve );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
|
||||
session = getNewSession("acme");
|
||||
try {
|
||||
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 = getNewSession("jboss");
|
||||
session.beginTransaction();
|
||||
session.delete( steve );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
@TestForIssue(jiraKey = "HHH-11651")
|
||||
public void testUnwrappingAbstractMultiTenantConnectionProvider() {
|
||||
final MultiTenantConnectionProvider multiTenantConnectionProvider = serviceRegistry.getService(
|
||||
MultiTenantConnectionProvider.class );
|
||||
final AbstractMultiTenantConnectionProvider connectionProvider = multiTenantConnectionProvider.unwrap(
|
||||
AbstractMultiTenantConnectionProvider.class );
|
||||
assertThat( connectionProvider, is( notNullValue() ) );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSameIdentifiers() {
|
||||
// create a customer 'steve' in jboss
|
||||
Session session = getNewSession("jboss");
|
||||
session.beginTransaction();
|
||||
Customer steve = new Customer( 1L, "steve" );
|
||||
session.save( steve );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
|
||||
// now, create a customer 'john' in acme
|
||||
session = getNewSession("acme");
|
||||
session.beginTransaction();
|
||||
Customer john = new Customer( 1L, "john" );
|
||||
session.save( john );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
|
||||
sessionFactory.getStatisticsImplementor().clear();
|
||||
|
||||
// make sure we get the correct people back, from cache
|
||||
// first, jboss
|
||||
{
|
||||
session = getNewSession("jboss");
|
||||
session.beginTransaction();
|
||||
Customer customer = (Customer) session.load( Customer.class, 1L );
|
||||
Assert.assertEquals( "steve", customer.getName() );
|
||||
// also, make sure this came from second level
|
||||
Assert.assertEquals( 1, sessionFactory.getStatisticsImplementor().getSecondLevelCacheHitCount() );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
@TestForIssue(jiraKey = "HHH-11651")
|
||||
public void testUnwrappingMultiTenantConnectionProvider() {
|
||||
final MultiTenantConnectionProvider multiTenantConnectionProvider = serviceRegistry.getService(
|
||||
MultiTenantConnectionProvider.class );
|
||||
final MultiTenantConnectionProvider connectionProvider = multiTenantConnectionProvider.unwrap(
|
||||
MultiTenantConnectionProvider.class );
|
||||
assertThat( connectionProvider, is( notNullValue() ) );
|
||||
}
|
||||
sessionFactory.getStatisticsImplementor().clear();
|
||||
// then, acme
|
||||
{
|
||||
session = getNewSession("acme");
|
||||
session.beginTransaction();
|
||||
Customer customer = (Customer) session.load( Customer.class, 1L );
|
||||
Assert.assertEquals( "john", customer.getName() );
|
||||
// also, make sure this came from second level
|
||||
Assert.assertEquals( 1, sessionFactory.getStatisticsImplementor().getSecondLevelCacheHitCount() );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
|
||||
// make sure the same works from datastore too
|
||||
sessionFactory.getStatisticsImplementor().clear();
|
||||
sessionFactory.getCache().evictEntityRegions();
|
||||
// first jboss
|
||||
{
|
||||
session = getNewSession("jboss");
|
||||
session.beginTransaction();
|
||||
Customer customer = (Customer) session.load( Customer.class, 1L );
|
||||
Assert.assertEquals( "steve", customer.getName() );
|
||||
// also, make sure this came from second level
|
||||
Assert.assertEquals( 0, sessionFactory.getStatisticsImplementor().getSecondLevelCacheHitCount() );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
sessionFactory.getStatisticsImplementor().clear();
|
||||
// then, acme
|
||||
{
|
||||
session = getNewSession("acme");
|
||||
session.beginTransaction();
|
||||
Customer customer = (Customer) session.load( Customer.class, 1L );
|
||||
Assert.assertEquals( "john", customer.getName() );
|
||||
// also, make sure this came from second level
|
||||
Assert.assertEquals( 0, sessionFactory.getStatisticsImplementor().getSecondLevelCacheHitCount() );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
|
||||
session = getNewSession("jboss");
|
||||
session.beginTransaction();
|
||||
session.delete( steve );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
|
||||
session = getNewSession("acme");
|
||||
session.beginTransaction();
|
||||
session.delete( john );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTableIdentifiers() {
|
||||
Session session = getNewSession( "jboss" );
|
||||
session.beginTransaction();
|
||||
Invoice orderJboss = new Invoice();
|
||||
session.save( orderJboss );
|
||||
Assert.assertEquals( Long.valueOf( 1 ), orderJboss.getId() );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
|
||||
session = getNewSession( "acme" );
|
||||
session.beginTransaction();
|
||||
Invoice orderAcme = new Invoice();
|
||||
session.save( orderAcme );
|
||||
Assert.assertEquals( Long.valueOf( 1 ), orderAcme.getId() );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
|
||||
session = getNewSession( "jboss" );
|
||||
session.beginTransaction();
|
||||
session.delete( orderJboss );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
|
||||
session = getNewSession( "acme" );
|
||||
session.beginTransaction();
|
||||
session.delete( orderAcme );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
|
||||
sessionFactory.getStatisticsImplementor().clear();
|
||||
}
|
||||
|
||||
protected Session getNewSession(String tenant) {
|
||||
return sessionFactory.withOptions().tenantIdentifier( tenant ).openSession();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,11 +6,30 @@
|
|||
*/
|
||||
package org.hibernate.testing.env;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.SQLFeatureNotSupportedException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import javassist.scopedpool.SoftValueHashMap;
|
||||
|
||||
import org.hibernate.annotations.common.reflection.ReflectionUtil;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl;
|
||||
import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl;
|
||||
import org.hibernate.internal.util.ReflectHelper;
|
||||
|
||||
import org.hibernate.testing.DialectCheck;
|
||||
|
||||
|
@ -21,6 +40,7 @@ import org.hibernate.testing.DialectCheck;
|
|||
*/
|
||||
public class ConnectionProviderBuilder implements DialectCheck {
|
||||
public static final String DRIVER = "org.h2.Driver";
|
||||
public static final String DATA_SOURCE = "org.h2.jdbcx.JdbcDataSource";
|
||||
// public static final String URL = "jdbc:h2:mem:%s;DB_CLOSE_DELAY=-1;MVCC=TRUE";
|
||||
public static final String URL = "jdbc:h2:mem:%s;DB_CLOSE_DELAY=-1";
|
||||
public static final String USER = "sa";
|
||||
|
@ -47,6 +67,92 @@ public class ConnectionProviderBuilder implements DialectCheck {
|
|||
return buildConnectionProvider( getConnectionProviderProperties( dbName ), false );
|
||||
}
|
||||
|
||||
public static DatasourceConnectionProviderImpl buildDataSourceConnectionProvider(String dbName) {
|
||||
try {
|
||||
Class dataSourceClass = ReflectHelper.classForName( DATA_SOURCE, ConnectionProviderBuilder.class );
|
||||
DataSource actualDataSource = (DataSource) dataSourceClass.newInstance();
|
||||
ReflectHelper.findSetterMethod( dataSourceClass, "URL", String.class ).invoke( actualDataSource, String.format( URL, dbName ) );
|
||||
ReflectHelper.findSetterMethod( dataSourceClass, "user", String.class ).invoke( actualDataSource, USER );
|
||||
ReflectHelper.findSetterMethod( dataSourceClass, "password", String.class ).invoke( actualDataSource, PASS );
|
||||
|
||||
final DataSourceInvocationHandler dataSourceInvocationHandler = new DataSourceInvocationHandler( actualDataSource );
|
||||
|
||||
DatasourceConnectionProviderImpl connectionProvider = new DatasourceConnectionProviderImpl() {
|
||||
@Override
|
||||
public void stop() {
|
||||
dataSourceInvocationHandler.stop();
|
||||
}
|
||||
};
|
||||
|
||||
connectionProvider.configure(
|
||||
Collections.singletonMap( Environment.DATASOURCE,
|
||||
Proxy.newProxyInstance(
|
||||
Thread.currentThread().getContextClassLoader(),
|
||||
new Class[] { DataSource.class },
|
||||
dataSourceInvocationHandler
|
||||
)
|
||||
)
|
||||
);
|
||||
return connectionProvider;
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalArgumentException( e );
|
||||
}
|
||||
}
|
||||
|
||||
private static class DataSourceInvocationHandler implements InvocationHandler {
|
||||
|
||||
private final DataSource target;
|
||||
|
||||
private Connection actualConnection;
|
||||
|
||||
private Connection connectionProxy;
|
||||
|
||||
public DataSourceInvocationHandler(DataSource target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
if ("getConnection".equals(method.getName())) {
|
||||
if(actualConnection == null) {
|
||||
actualConnection = (Connection) method.invoke( target, args);
|
||||
connectionProxy = (Connection) Proxy.newProxyInstance(
|
||||
this.getClass().getClassLoader(),
|
||||
new Class[] { Connection.class },
|
||||
new ConnectionInvocationHandler( actualConnection )
|
||||
);
|
||||
}
|
||||
}
|
||||
return connectionProxy;
|
||||
}
|
||||
|
||||
private class ConnectionInvocationHandler implements InvocationHandler {
|
||||
|
||||
private final Connection target;
|
||||
|
||||
public ConnectionInvocationHandler(Connection target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
if ("close".equals(method.getName())) {
|
||||
//Do nothing
|
||||
return null;
|
||||
}
|
||||
return method.invoke(target, args);
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
try {
|
||||
actualConnection.close();
|
||||
}
|
||||
catch (SQLException ignore) {}
|
||||
}
|
||||
}
|
||||
|
||||
public static DriverManagerConnectionProviderImpl buildConnectionProvider(final boolean allowAggressiveRelease) {
|
||||
return buildConnectionProvider( getConnectionProviderProperties( "db1" ), allowAggressiveRelease );
|
||||
}
|
||||
|
|
|
@ -348,6 +348,73 @@ public class TransactionUtil {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute function in a Hibernate transaction without return value and for a given tenant
|
||||
*
|
||||
* @param factorySupplier SessionFactory supplier
|
||||
* @param tenant tenant
|
||||
* @param function function
|
||||
*/
|
||||
public static void doInHibernate(
|
||||
Supplier<SessionFactory> factorySupplier,
|
||||
String tenant,
|
||||
Consumer<Session> function) {
|
||||
Session session = null;
|
||||
Transaction txn = null;
|
||||
try {
|
||||
session = factorySupplier.get()
|
||||
.withOptions()
|
||||
.tenantIdentifier( tenant )
|
||||
.openSession();
|
||||
txn = session.getTransaction();
|
||||
txn.begin();
|
||||
function.accept(session);
|
||||
txn.commit();
|
||||
} catch (Throwable e) {
|
||||
if ( txn != null ) txn.rollback();
|
||||
throw e;
|
||||
} finally {
|
||||
if (session != null) {
|
||||
session.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute function in a Hibernate transaction for a given tenant and return a value
|
||||
*
|
||||
* @param factorySupplier SessionFactory supplier
|
||||
* @param tenant tenant
|
||||
* @param function function
|
||||
*
|
||||
* @return result
|
||||
*/
|
||||
public static <R> R doInHibernate(
|
||||
Supplier<SessionFactory> factorySupplier,
|
||||
String tenant,
|
||||
Function<Session, R> function) {
|
||||
Session session = null;
|
||||
Transaction txn = null;
|
||||
try {
|
||||
session = factorySupplier.get()
|
||||
.withOptions()
|
||||
.tenantIdentifier( tenant )
|
||||
.openSession();
|
||||
txn = session.getTransaction();
|
||||
txn.begin();
|
||||
R returnValue = function.apply(session);
|
||||
txn.commit();
|
||||
return returnValue;
|
||||
} catch (Throwable e) {
|
||||
if ( txn != null ) txn.rollback();
|
||||
throw e;
|
||||
} finally {
|
||||
if (session != null) {
|
||||
session.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute function in a Hibernate transaction
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue