/*
* 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.userguide.multitenancy;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.function.Consumer;
import javax.persistence.Entity;
import javax.persistence.Id;
import org.hibernate.MultiTenancyStrategy;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
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.AvailableSettings;
import org.hibernate.cfg.Environment;
import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
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.AfterClassOnce;
import org.hibernate.testing.junit4.BaseUnitTestCase;
import org.hibernate.test.util.DdlTransactionIsolatorTestingImpl;
import org.junit.Test;
/**
* @author Vlad Mihalcea
*/
public abstract class AbstractMultiTenancyTest extends BaseUnitTestCase {
protected static final String FRONT_END_TENANT = "front_end";
protected static final String BACK_END_TENANT = "back_end";
private Map<String, ConnectionProvider> connectionProviderMap = new HashMap<>( );
private SessionFactory sessionFactory;
public AbstractMultiTenancyTest() {
init();
}
//tag::multitenacy-hibernate-MultiTenantConnectionProvider-example[]
private void init() {
registerConnectionProvider( FRONT_END_TENANT );
registerConnectionProvider( BACK_END_TENANT );
Map<String, Object> settings = new HashMap<>( );
settings.put( AvailableSettings.MULTI_TENANT, multiTenancyStrategy() );
settings.put( AvailableSettings.MULTI_TENANT_CONNECTION_PROVIDER,
new ConfigurableMultiTenantConnectionProvider( connectionProviderMap ) );
sessionFactory = sessionFactory(settings);
}
//end::multitenacy-hibernate-MultiTenantConnectionProvider-example[]
@AfterClassOnce
public void destroy() {
sessionFactory.close();
for ( ConnectionProvider connectionProvider : connectionProviderMap.values() ) {
if ( connectionProvider instanceof Stoppable ) {
( (Stoppable) connectionProvider ).stop();
}
}
}
//tag::multitenacy-hibernate-MultiTenantConnectionProvider-example[]
protected void registerConnectionProvider(String tenantIdentifier) {
Properties properties = properties();
properties.put( Environment.URL,
tenantUrl(properties.getProperty( Environment.URL ), tenantIdentifier) );
DriverManagerConnectionProviderImpl connectionProvider =
new DriverManagerConnectionProviderImpl();
connectionProvider.configure( properties );
connectionProviderMap.put( tenantIdentifier, connectionProvider );
}
//end::multitenacy-hibernate-MultiTenantConnectionProvider-example[]
@Test
public void testBasicExpectedBehavior() {
//tag::multitenacy-multitenacy-hibernate-same-entity-example[]
doInSession( FRONT_END_TENANT, session -> {
Person person = new Person( );
person.setId( 1L );
person.setName( "John Doe" );
session.persist( person );
} );
doInSession( BACK_END_TENANT, session -> {
Person person = new Person( );
person.setId( 1L );
person.setName( "John Doe" );
session.persist( person );
} );
//end::multitenacy-multitenacy-hibernate-same-entity-example[]
}
protected abstract MultiTenancyStrategy multiTenancyStrategy();
protected Properties properties() {
Properties properties = new Properties( );
URL propertiesURL = Thread.currentThread().getContextClassLoader().getResource( "hibernate.properties" );
try(FileInputStream inputStream = new FileInputStream( propertiesURL.getFile() )) {
properties.load( inputStream );
}
catch (IOException e) {
throw new IllegalArgumentException( e );
}
return properties;
}
protected abstract String tenantUrl(String originalUrl, String tenantIdentifier);
protected SessionFactory sessionFactory(Map<String, Object> settings) {
ServiceRegistryImplementor serviceRegistry = (ServiceRegistryImplementor) new StandardServiceRegistryBuilder()
.applySettings( settings )
.build();
MetadataSources metadataSources = new MetadataSources( serviceRegistry );
for(Class annotatedClasses : getAnnotatedClasses()) {
metadataSources.addAnnotatedClass( annotatedClasses );
}
Metadata metadata = metadataSources.buildMetadata();
HibernateSchemaManagementTool tool = new HibernateSchemaManagementTool();
tool.injectServices( serviceRegistry );
final GenerationTargetToDatabase frontEndSchemaGenerator = new GenerationTargetToDatabase(
new DdlTransactionIsolatorTestingImpl(
serviceRegistry,
connectionProviderMap.get( FRONT_END_TENANT )
)
);
final GenerationTargetToDatabase backEndSchemaGenerator = new GenerationTargetToDatabase(
new DdlTransactionIsolatorTestingImpl(
serviceRegistry,
connectionProviderMap.get( BACK_END_TENANT )
)
);
new SchemaDropperImpl( serviceRegistry ).doDrop(
metadata,
serviceRegistry,
settings,
true,
frontEndSchemaGenerator,
backEndSchemaGenerator
);
new SchemaCreatorImpl( serviceRegistry ).doCreation(
metadata,
serviceRegistry,
settings,
true,
frontEndSchemaGenerator,
backEndSchemaGenerator
);
final SessionFactoryBuilder sessionFactoryBuilder = metadata.getSessionFactoryBuilder();
return sessionFactoryBuilder.build();
}
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Person.class
};
}
//tag::multitenacy-hibernate-session-example[]
private void doInSession(String tenant, Consumer<Session> function) {
Session session = null;
Transaction txn = null;
try {
session = sessionFactory
.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();
}
}
}
//end::multitenacy-hibernate-session-example[]
@Entity(name = "Person")
public static class Person {
@Id
private Long id;
private String name;
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;
}
}
}