HHH-11651 - unwrapping error in AbstractMultiTenantConnectionProvider

This commit is contained in:
Andrea Boriero 2017-04-20 14:24:23 +02:00 committed by Vlad Mihalcea
parent 442f5e60dd
commit 6379a42a58
8 changed files with 569 additions and 265 deletions

View File

@ -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 );
}

View File

@ -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 );
}

View File

@ -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" );
}
}

View File

@ -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;

View File

@ -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() ) );
}
}

View File

@ -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();
}
}

View File

@ -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 );
}

View File

@ -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
*