HHH-6336 - Add TenantIdentifierResolver
This commit is contained in:
parent
230cff7d00
commit
89911003e3
|
@ -547,5 +547,7 @@ public interface AvailableSettings {
|
||||||
*/
|
*/
|
||||||
public static final String CUSTOM_ENTITY_DIRTINESS_STRATEGY = "hibernate.entity_dirtiness_strategy";
|
public static final String CUSTOM_ENTITY_DIRTINESS_STRATEGY = "hibernate.entity_dirtiness_strategy";
|
||||||
|
|
||||||
|
public static final String TENANT_IDENTIFIER_RESOLVER = "hibernate.tenant_identifier_resolver";
|
||||||
|
|
||||||
public static final String FORCE_DISCRIMINATOR_IN_SELECTS_BY_DEFAULT = "hibernate.discriminator.force_in_select";
|
public static final String FORCE_DISCRIMINATOR_IN_SELECTS_BY_DEFAULT = "hibernate.discriminator.force_in_select";
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,6 +79,7 @@ import org.hibernate.annotations.common.reflection.ReflectionManager;
|
||||||
import org.hibernate.annotations.common.reflection.XClass;
|
import org.hibernate.annotations.common.reflection.XClass;
|
||||||
import org.hibernate.annotations.common.reflection.java.JavaReflectionManager;
|
import org.hibernate.annotations.common.reflection.java.JavaReflectionManager;
|
||||||
import org.hibernate.cfg.annotations.reflection.JPAMetadataProvider;
|
import org.hibernate.cfg.annotations.reflection.JPAMetadataProvider;
|
||||||
|
import org.hibernate.context.spi.CurrentTenantIdentifierResolver;
|
||||||
import org.hibernate.dialect.Dialect;
|
import org.hibernate.dialect.Dialect;
|
||||||
import org.hibernate.dialect.MySQLDialect;
|
import org.hibernate.dialect.MySQLDialect;
|
||||||
import org.hibernate.dialect.function.SQLFunction;
|
import org.hibernate.dialect.function.SQLFunction;
|
||||||
|
@ -254,6 +255,7 @@ public class Configuration implements Serializable {
|
||||||
private boolean isValidatorNotPresentLogged;
|
private boolean isValidatorNotPresentLogged;
|
||||||
private Map<XClass, Map<String, PropertyData>> propertiesAnnotatedWithMapsId;
|
private Map<XClass, Map<String, PropertyData>> propertiesAnnotatedWithMapsId;
|
||||||
private Map<XClass, Map<String, PropertyData>> propertiesAnnotatedWithIdAndToOne;
|
private Map<XClass, Map<String, PropertyData>> propertiesAnnotatedWithIdAndToOne;
|
||||||
|
private CurrentTenantIdentifierResolver currentTenantIdentifierResolver;
|
||||||
private boolean specjProprietarySyntaxEnabled;
|
private boolean specjProprietarySyntaxEnabled;
|
||||||
|
|
||||||
|
|
||||||
|
@ -2436,6 +2438,14 @@ public class Configuration implements Serializable {
|
||||||
this.sessionFactoryObserver = sessionFactoryObserver;
|
this.sessionFactoryObserver = sessionFactoryObserver;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CurrentTenantIdentifierResolver getCurrentTenantIdentifierResolver() {
|
||||||
|
return currentTenantIdentifierResolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCurrentTenantIdentifierResolver(CurrentTenantIdentifierResolver currentTenantIdentifierResolver) {
|
||||||
|
this.currentTenantIdentifierResolver = currentTenantIdentifierResolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Mappings impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// Mappings impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
|
||||||
|
* indicated by the @author tags or express copyright attribution
|
||||||
|
* statements applied by the authors. All third-party contributions are
|
||||||
|
* distributed under license by Red Hat Inc.
|
||||||
|
*
|
||||||
|
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||||
|
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||||
|
* Lesser General Public License, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this distribution; if not, write to:
|
||||||
|
* Free Software Foundation, Inc.
|
||||||
|
* 51 Franklin Street, Fifth Floor
|
||||||
|
* Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
package org.hibernate.context;
|
||||||
|
|
||||||
|
import org.hibernate.HibernateException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that tenant identifiers did not match in cases where
|
||||||
|
* {@link org.hibernate.context.spi.CurrentTenantIdentifierResolver#validateExistingCurrentSessions()} returns
|
||||||
|
* {@code true} and there is a mismatch found.
|
||||||
|
*
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
public class TenantIdentifierMismatchException extends HibernateException{
|
||||||
|
public TenantIdentifierMismatchException(String message) {
|
||||||
|
super( message );
|
||||||
|
}
|
||||||
|
|
||||||
|
public TenantIdentifierMismatchException(String message, Throwable root) {
|
||||||
|
super( message, root );
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,6 +34,7 @@ import org.jboss.logging.Logger;
|
||||||
import org.hibernate.ConnectionReleaseMode;
|
import org.hibernate.ConnectionReleaseMode;
|
||||||
import org.hibernate.HibernateException;
|
import org.hibernate.HibernateException;
|
||||||
import org.hibernate.Session;
|
import org.hibernate.Session;
|
||||||
|
import org.hibernate.context.spi.AbstractCurrentSessionContext;
|
||||||
import org.hibernate.context.spi.CurrentSessionContext;
|
import org.hibernate.context.spi.CurrentSessionContext;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.engine.transaction.internal.jta.JtaStatusHelper;
|
import org.hibernate.engine.transaction.internal.jta.JtaStatusHelper;
|
||||||
|
@ -63,19 +64,18 @@ import org.hibernate.service.jta.platform.spi.JtaPlatform;
|
||||||
*
|
*
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class JTASessionContext implements CurrentSessionContext {
|
public class JTASessionContext extends AbstractCurrentSessionContext {
|
||||||
private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, JTASessionContext.class.getName());
|
private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, JTASessionContext.class.getName());
|
||||||
|
|
||||||
protected final SessionFactoryImplementor factory;
|
|
||||||
private transient Map currentSessionMap = new Hashtable();
|
private transient Map currentSessionMap = new Hashtable();
|
||||||
|
|
||||||
public JTASessionContext(SessionFactoryImplementor factory) {
|
public JTASessionContext(SessionFactoryImplementor factory) {
|
||||||
this.factory = factory;
|
super( factory );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Session currentSession() throws HibernateException {
|
public Session currentSession() throws HibernateException {
|
||||||
final JtaPlatform jtaPlatform = factory.getServiceRegistry().getService( JtaPlatform.class );
|
final JtaPlatform jtaPlatform = factory().getServiceRegistry().getService( JtaPlatform.class );
|
||||||
final TransactionManager transactionManager = jtaPlatform.retrieveTransactionManager();
|
final TransactionManager transactionManager = jtaPlatform.retrieveTransactionManager();
|
||||||
if ( transactionManager == null ) {
|
if ( transactionManager == null ) {
|
||||||
throw new HibernateException( "No TransactionManagerLookup specified" );
|
throw new HibernateException( "No TransactionManagerLookup specified" );
|
||||||
|
@ -123,6 +123,9 @@ public class JTASessionContext implements CurrentSessionContext {
|
||||||
|
|
||||||
currentSessionMap.put( txnIdentifier, currentSession );
|
currentSessionMap.put( txnIdentifier, currentSession );
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
validateExistingSession( currentSession );
|
||||||
|
}
|
||||||
|
|
||||||
return currentSession;
|
return currentSession;
|
||||||
}
|
}
|
||||||
|
@ -147,7 +150,7 @@ public class JTASessionContext implements CurrentSessionContext {
|
||||||
* @return the built or (re)obtained session.
|
* @return the built or (re)obtained session.
|
||||||
*/
|
*/
|
||||||
protected Session buildOrObtainSession() {
|
protected Session buildOrObtainSession() {
|
||||||
return factory.withOptions()
|
return baseSessionBuilder()
|
||||||
.autoClose( isAutoCloseEnabled() )
|
.autoClose( isAutoCloseEnabled() )
|
||||||
.connectionReleaseMode( getConnectionReleaseMode() )
|
.connectionReleaseMode( getConnectionReleaseMode() )
|
||||||
.flushBeforeCompletion( isAutoFlushEnabled() )
|
.flushBeforeCompletion( isAutoFlushEnabled() )
|
||||||
|
|
|
@ -29,7 +29,7 @@ import java.util.Map;
|
||||||
import org.hibernate.HibernateException;
|
import org.hibernate.HibernateException;
|
||||||
import org.hibernate.Session;
|
import org.hibernate.Session;
|
||||||
import org.hibernate.SessionFactory;
|
import org.hibernate.SessionFactory;
|
||||||
import org.hibernate.context.spi.CurrentSessionContext;
|
import org.hibernate.context.spi.AbstractCurrentSessionContext;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -56,21 +56,23 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
*
|
*
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class ManagedSessionContext implements CurrentSessionContext {
|
public class ManagedSessionContext extends AbstractCurrentSessionContext {
|
||||||
|
|
||||||
private static final ThreadLocal<Map<SessionFactory,Session>> context = new ThreadLocal<Map<SessionFactory,Session>>();
|
private static final ThreadLocal<Map<SessionFactory,Session>> context = new ThreadLocal<Map<SessionFactory,Session>>();
|
||||||
private final SessionFactoryImplementor factory;
|
|
||||||
|
|
||||||
public ManagedSessionContext(SessionFactoryImplementor factory) {
|
public ManagedSessionContext(SessionFactoryImplementor factory) {
|
||||||
this.factory = factory;
|
super( factory );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Session currentSession() {
|
public Session currentSession() {
|
||||||
Session current = existingSession( factory );
|
Session current = existingSession( factory() );
|
||||||
if ( current == null ) {
|
if ( current == null ) {
|
||||||
throw new HibernateException( "No session currently bound to execution context" );
|
throw new HibernateException( "No session currently bound to execution context" );
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
validateExistingSession( current );
|
||||||
|
}
|
||||||
return current;
|
return current;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,7 @@ import org.hibernate.ConnectionReleaseMode;
|
||||||
import org.hibernate.HibernateException;
|
import org.hibernate.HibernateException;
|
||||||
import org.hibernate.Session;
|
import org.hibernate.Session;
|
||||||
import org.hibernate.SessionFactory;
|
import org.hibernate.SessionFactory;
|
||||||
|
import org.hibernate.context.spi.AbstractCurrentSessionContext;
|
||||||
import org.hibernate.context.spi.CurrentSessionContext;
|
import org.hibernate.context.spi.CurrentSessionContext;
|
||||||
import org.hibernate.engine.jdbc.LobCreationContext;
|
import org.hibernate.engine.jdbc.LobCreationContext;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
@ -75,7 +76,7 @@ import org.hibernate.internal.CoreMessageLogger;
|
||||||
*
|
*
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class ThreadLocalSessionContext implements CurrentSessionContext {
|
public class ThreadLocalSessionContext extends AbstractCurrentSessionContext {
|
||||||
|
|
||||||
private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class,
|
private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class,
|
||||||
ThreadLocalSessionContext.class.getName());
|
ThreadLocalSessionContext.class.getName());
|
||||||
|
@ -95,18 +96,14 @@ public class ThreadLocalSessionContext implements CurrentSessionContext {
|
||||||
*/
|
*/
|
||||||
private static final ThreadLocal<Map> context = new ThreadLocal<Map>();
|
private static final ThreadLocal<Map> context = new ThreadLocal<Map>();
|
||||||
|
|
||||||
protected final SessionFactoryImplementor factory;
|
|
||||||
|
|
||||||
public ThreadLocalSessionContext(SessionFactoryImplementor factory) {
|
public ThreadLocalSessionContext(SessionFactoryImplementor factory) {
|
||||||
this.factory = factory;
|
super( factory );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public final Session currentSession() throws HibernateException {
|
public final Session currentSession() throws HibernateException {
|
||||||
Session current = existingSession( factory );
|
Session current = existingSession( factory() );
|
||||||
if (current == null) {
|
if ( current == null ) {
|
||||||
current = buildOrObtainSession();
|
current = buildOrObtainSession();
|
||||||
// register a cleanup sync
|
// register a cleanup sync
|
||||||
current.getTransaction().registerSynchronization( buildCleanupSynch() );
|
current.getTransaction().registerSynchronization( buildCleanupSynch() );
|
||||||
|
@ -115,7 +112,10 @@ public class ThreadLocalSessionContext implements CurrentSessionContext {
|
||||||
current = wrap( current );
|
current = wrap( current );
|
||||||
}
|
}
|
||||||
// then bind it
|
// then bind it
|
||||||
doBind( current, factory );
|
doBind( current, factory() );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
validateExistingSession( current );
|
||||||
}
|
}
|
||||||
return current;
|
return current;
|
||||||
}
|
}
|
||||||
|
@ -134,7 +134,7 @@ public class ThreadLocalSessionContext implements CurrentSessionContext {
|
||||||
* @return Value for property 'factory'.
|
* @return Value for property 'factory'.
|
||||||
*/
|
*/
|
||||||
protected SessionFactoryImplementor getFactory() {
|
protected SessionFactoryImplementor getFactory() {
|
||||||
return factory;
|
return factory();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -146,7 +146,7 @@ public class ThreadLocalSessionContext implements CurrentSessionContext {
|
||||||
* @return the built or (re)obtained session.
|
* @return the built or (re)obtained session.
|
||||||
*/
|
*/
|
||||||
protected Session buildOrObtainSession() {
|
protected Session buildOrObtainSession() {
|
||||||
return factory.withOptions()
|
return baseSessionBuilder()
|
||||||
.autoClose( isAutoCloseEnabled() )
|
.autoClose( isAutoCloseEnabled() )
|
||||||
.connectionReleaseMode( getConnectionReleaseMode() )
|
.connectionReleaseMode( getConnectionReleaseMode() )
|
||||||
.flushBeforeCompletion( isAutoFlushEnabled() )
|
.flushBeforeCompletion( isAutoFlushEnabled() )
|
||||||
|
@ -154,7 +154,7 @@ public class ThreadLocalSessionContext implements CurrentSessionContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected CleanupSynch buildCleanupSynch() {
|
protected CleanupSynch buildCleanupSynch() {
|
||||||
return new CleanupSynch( factory );
|
return new CleanupSynch( factory() );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -181,7 +181,7 @@ public class ThreadLocalSessionContext implements CurrentSessionContext {
|
||||||
* @return The connection release mode for any built sessions.
|
* @return The connection release mode for any built sessions.
|
||||||
*/
|
*/
|
||||||
protected ConnectionReleaseMode getConnectionReleaseMode() {
|
protected ConnectionReleaseMode getConnectionReleaseMode() {
|
||||||
return factory.getSettings().getConnectionReleaseMode();
|
return factory().getSettings().getConnectionReleaseMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Session wrap(Session session) {
|
protected Session wrap(Session session) {
|
||||||
|
@ -240,7 +240,9 @@ public class ThreadLocalSessionContext implements CurrentSessionContext {
|
||||||
|
|
||||||
private static Session existingSession(SessionFactory factory) {
|
private static Session existingSession(SessionFactory factory) {
|
||||||
Map sessionMap = sessionMap();
|
Map sessionMap = sessionMap();
|
||||||
if ( sessionMap == null ) return null;
|
if ( sessionMap == null ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return (Session) sessionMap.get( factory );
|
return (Session) sessionMap.get( factory );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -372,8 +374,8 @@ public class ThreadLocalSessionContext implements CurrentSessionContext {
|
||||||
// serialized, to be completely correct, we need to make sure
|
// serialized, to be completely correct, we need to make sure
|
||||||
// that unbinding of that session occurs.
|
// that unbinding of that session occurs.
|
||||||
oos.defaultWriteObject();
|
oos.defaultWriteObject();
|
||||||
if ( existingSession( factory ) == wrappedSession ) {
|
if ( existingSession( factory() ) == wrappedSession ) {
|
||||||
unbind( factory );
|
unbind( factory() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -383,7 +385,7 @@ public class ThreadLocalSessionContext implements CurrentSessionContext {
|
||||||
// the ThreadLocalSessionContext session map.
|
// the ThreadLocalSessionContext session map.
|
||||||
ois.defaultReadObject();
|
ois.defaultReadObject();
|
||||||
realSession.getTransaction().registerSynchronization( buildCleanupSynch() );
|
realSession.getTransaction().registerSynchronization( buildCleanupSynch() );
|
||||||
doBind( wrappedSession, factory );
|
doBind( wrappedSession, factory() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
|
||||||
|
* indicated by the @author tags or express copyright attribution
|
||||||
|
* statements applied by the authors. All third-party contributions are
|
||||||
|
* distributed under license by Red Hat Inc.
|
||||||
|
*
|
||||||
|
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||||
|
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||||
|
* Lesser General Public License, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this distribution; if not, write to:
|
||||||
|
* Free Software Foundation, Inc.
|
||||||
|
* 51 Franklin Street, Fifth Floor
|
||||||
|
* Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
package org.hibernate.context.spi;
|
||||||
|
|
||||||
|
import org.hibernate.Session;
|
||||||
|
import org.hibernate.SessionBuilder;
|
||||||
|
import org.hibernate.context.TenantIdentifierMismatchException;
|
||||||
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
import org.hibernate.internal.util.compare.EqualsHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base support for {@link CurrentSessionContext} implementors.
|
||||||
|
*
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
public abstract class AbstractCurrentSessionContext implements CurrentSessionContext {
|
||||||
|
private final SessionFactoryImplementor factory;
|
||||||
|
|
||||||
|
protected AbstractCurrentSessionContext(SessionFactoryImplementor factory) {
|
||||||
|
this.factory = factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionFactoryImplementor factory() {
|
||||||
|
return factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected SessionBuilder baseSessionBuilder() {
|
||||||
|
final SessionBuilder builder = factory.withOptions();
|
||||||
|
final CurrentTenantIdentifierResolver resolver = factory.getCurrentTenantIdentifierResolver();
|
||||||
|
if ( resolver != null ) {
|
||||||
|
builder.tenantIdentifier( resolver.resolveCurrentTenantIdentifier() );
|
||||||
|
}
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void validateExistingSession(Session existingSession) {
|
||||||
|
final CurrentTenantIdentifierResolver resolver = factory.getCurrentTenantIdentifierResolver();
|
||||||
|
if ( resolver != null && resolver.validateExistingCurrentSessions() ) {
|
||||||
|
final String current = resolver.resolveCurrentTenantIdentifier();
|
||||||
|
if ( ! EqualsHelper.equals( existingSession.getTenantIdentifier(), current ) ) {
|
||||||
|
throw new TenantIdentifierMismatchException(
|
||||||
|
String.format(
|
||||||
|
"Reported current tenant identifier [%s] did not match tenant identifier from " +
|
||||||
|
"existing session [%s]",
|
||||||
|
current,
|
||||||
|
existingSession.getTenantIdentifier()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
|
||||||
|
* indicated by the @author tags or express copyright attribution
|
||||||
|
* statements applied by the authors. All third-party contributions are
|
||||||
|
* distributed under license by Red Hat Inc.
|
||||||
|
*
|
||||||
|
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||||
|
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||||
|
* Lesser General Public License, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this distribution; if not, write to:
|
||||||
|
* Free Software Foundation, Inc.
|
||||||
|
* 51 Franklin Street, Fifth Floor
|
||||||
|
* Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
package org.hibernate.context.spi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A callback registered with the {@link org.hibernate.SessionFactory} that is responsible for resolving the
|
||||||
|
* current tenant identifier for use with {@link CurrentSessionContext} and
|
||||||
|
* {@link org.hibernate.SessionFactory#getCurrentSession()}
|
||||||
|
*
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
public interface CurrentTenantIdentifierResolver {
|
||||||
|
/**
|
||||||
|
* Resolve the current tenant identifier.
|
||||||
|
*
|
||||||
|
* @return The current tenant identifier
|
||||||
|
*/
|
||||||
|
public String resolveCurrentTenantIdentifier();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should we validate that the tenant identifier on "current sessions" that already exist when
|
||||||
|
* {@link CurrentSessionContext#currentSession()} is called matches the value returned here from
|
||||||
|
* {@link #resolveCurrentTenantIdentifier()}?
|
||||||
|
*
|
||||||
|
* @return {@code true} indicates that the extra validation will be performed; {@code false} indicates it will not.
|
||||||
|
*
|
||||||
|
* @see org.hibernate.context.TenantIdentifierMismatchException
|
||||||
|
*/
|
||||||
|
public boolean validateExistingCurrentSessions();
|
||||||
|
}
|
|
@ -38,6 +38,7 @@ import org.hibernate.cache.spi.QueryCache;
|
||||||
import org.hibernate.cache.spi.Region;
|
import org.hibernate.cache.spi.Region;
|
||||||
import org.hibernate.cache.spi.UpdateTimestampsCache;
|
import org.hibernate.cache.spi.UpdateTimestampsCache;
|
||||||
import org.hibernate.cfg.Settings;
|
import org.hibernate.cfg.Settings;
|
||||||
|
import org.hibernate.context.spi.CurrentTenantIdentifierResolver;
|
||||||
import org.hibernate.dialect.Dialect;
|
import org.hibernate.dialect.Dialect;
|
||||||
import org.hibernate.dialect.function.SQLFunctionRegistry;
|
import org.hibernate.dialect.function.SQLFunctionRegistry;
|
||||||
import org.hibernate.engine.ResultSetMappingDefinition;
|
import org.hibernate.engine.ResultSetMappingDefinition;
|
||||||
|
@ -241,4 +242,6 @@ public interface SessionFactoryImplementor extends Mapping, SessionFactory {
|
||||||
public void addObserver(SessionFactoryObserver observer);
|
public void addObserver(SessionFactoryObserver observer);
|
||||||
|
|
||||||
public CustomEntityDirtinessStrategy getCustomEntityDirtinessStrategy();
|
public CustomEntityDirtinessStrategy getCustomEntityDirtinessStrategy();
|
||||||
|
|
||||||
|
public CurrentTenantIdentifierResolver getCurrentTenantIdentifierResolver();
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,6 +82,7 @@ import org.hibernate.context.internal.JTASessionContext;
|
||||||
import org.hibernate.context.internal.ManagedSessionContext;
|
import org.hibernate.context.internal.ManagedSessionContext;
|
||||||
import org.hibernate.context.internal.ThreadLocalSessionContext;
|
import org.hibernate.context.internal.ThreadLocalSessionContext;
|
||||||
import org.hibernate.context.spi.CurrentSessionContext;
|
import org.hibernate.context.spi.CurrentSessionContext;
|
||||||
|
import org.hibernate.context.spi.CurrentTenantIdentifierResolver;
|
||||||
import org.hibernate.dialect.Dialect;
|
import org.hibernate.dialect.Dialect;
|
||||||
import org.hibernate.dialect.function.SQLFunction;
|
import org.hibernate.dialect.function.SQLFunction;
|
||||||
import org.hibernate.dialect.function.SQLFunctionRegistry;
|
import org.hibernate.dialect.function.SQLFunctionRegistry;
|
||||||
|
@ -210,6 +211,7 @@ public final class SessionFactoryImpl
|
||||||
private final transient TransactionEnvironment transactionEnvironment;
|
private final transient TransactionEnvironment transactionEnvironment;
|
||||||
private final transient SessionFactoryOptions sessionFactoryOptions;
|
private final transient SessionFactoryOptions sessionFactoryOptions;
|
||||||
private final transient CustomEntityDirtinessStrategy customEntityDirtinessStrategy;
|
private final transient CustomEntityDirtinessStrategy customEntityDirtinessStrategy;
|
||||||
|
private final transient CurrentTenantIdentifierResolver currentTenantIdentifierResolver;
|
||||||
|
|
||||||
@SuppressWarnings( {"unchecked", "ThrowableResultOfMethodCallIgnored"})
|
@SuppressWarnings( {"unchecked", "ThrowableResultOfMethodCallIgnored"})
|
||||||
public SessionFactoryImpl(
|
public SessionFactoryImpl(
|
||||||
|
@ -536,6 +538,10 @@ public final class SessionFactoryImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
this.customEntityDirtinessStrategy = determineCustomEntityDirtinessStrategy( properties );
|
this.customEntityDirtinessStrategy = determineCustomEntityDirtinessStrategy( properties );
|
||||||
|
this.currentTenantIdentifierResolver = determineCurrentTenantIdentifierResolver(
|
||||||
|
cfg.getCurrentTenantIdentifierResolver(),
|
||||||
|
properties
|
||||||
|
);
|
||||||
this.transactionEnvironment = new TransactionEnvironmentImpl( this );
|
this.transactionEnvironment = new TransactionEnvironmentImpl( this );
|
||||||
this.observer.sessionFactoryCreated( this );
|
this.observer.sessionFactoryCreated( this );
|
||||||
}
|
}
|
||||||
|
@ -564,14 +570,16 @@ public final class SessionFactoryImpl
|
||||||
customEntityDirtinessStrategyClass = null;
|
customEntityDirtinessStrategyClass = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
if ( customEntityDirtinessStrategyClass != null ) {
|
||||||
return customEntityDirtinessStrategyClass.newInstance();
|
try {
|
||||||
}
|
return customEntityDirtinessStrategyClass.newInstance();
|
||||||
catch (Exception e) {
|
}
|
||||||
LOG.debugf(
|
catch (Exception e) {
|
||||||
"Unable to instantiate CustomEntityDirtinessStrategy class %s",
|
LOG.debugf(
|
||||||
customEntityDirtinessStrategyClass.getName()
|
"Unable to instantiate CustomEntityDirtinessStrategy class %s",
|
||||||
);
|
customEntityDirtinessStrategyClass.getName()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -602,6 +610,53 @@ public final class SessionFactoryImpl
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings( {"unchecked"})
|
||||||
|
private CurrentTenantIdentifierResolver determineCurrentTenantIdentifierResolver(
|
||||||
|
CurrentTenantIdentifierResolver explicitResolver,
|
||||||
|
Properties properties) {
|
||||||
|
if ( explicitResolver != null ) {
|
||||||
|
return explicitResolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Object value = properties.get( AvailableSettings.TENANT_IDENTIFIER_RESOLVER );
|
||||||
|
if ( value == null ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( CurrentTenantIdentifierResolver.class.isInstance( value ) ) {
|
||||||
|
return CurrentTenantIdentifierResolver.class.cast( value );
|
||||||
|
}
|
||||||
|
|
||||||
|
Class<CurrentTenantIdentifierResolver> implClass;
|
||||||
|
if ( Class.class.isInstance( value ) ) {
|
||||||
|
implClass = Class.class.cast( customEntityDirtinessStrategy );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
try {
|
||||||
|
implClass = serviceRegistry.getService( ClassLoaderService.class ).classForName( value.toString() );
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
LOG.debugf(
|
||||||
|
"Unable to locate CurrentTenantIdentifierResolver implementation class %s",
|
||||||
|
value.toString()
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return implClass.newInstance();
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
LOG.debugf(
|
||||||
|
"Unable to instantiate CurrentTenantIdentifierResolver class %s",
|
||||||
|
implClass.getName()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings( {"ThrowableResultOfMethodCallIgnored"})
|
@SuppressWarnings( {"ThrowableResultOfMethodCallIgnored"})
|
||||||
public SessionFactoryImpl(
|
public SessionFactoryImpl(
|
||||||
MetadataImplementor metadata,
|
MetadataImplementor metadata,
|
||||||
|
@ -921,6 +976,7 @@ public final class SessionFactoryImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
this.customEntityDirtinessStrategy = determineCustomEntityDirtinessStrategy( properties );
|
this.customEntityDirtinessStrategy = determineCustomEntityDirtinessStrategy( properties );
|
||||||
|
this.currentTenantIdentifierResolver = determineCurrentTenantIdentifierResolver( null, properties );
|
||||||
this.transactionEnvironment = new TransactionEnvironmentImpl( this );
|
this.transactionEnvironment = new TransactionEnvironmentImpl( this );
|
||||||
this.observer.sessionFactoryCreated( this );
|
this.observer.sessionFactoryCreated( this );
|
||||||
}
|
}
|
||||||
|
@ -1791,10 +1847,16 @@ public final class SessionFactoryImpl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public CustomEntityDirtinessStrategy getCustomEntityDirtinessStrategy() {
|
public CustomEntityDirtinessStrategy getCustomEntityDirtinessStrategy() {
|
||||||
return customEntityDirtinessStrategy;
|
return customEntityDirtinessStrategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CurrentTenantIdentifierResolver getCurrentTenantIdentifierResolver() {
|
||||||
|
return currentTenantIdentifierResolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Serialization handling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// Serialization handling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -120,12 +120,12 @@ public class ThreadLocalCurrentSessionTest extends ConnectionManagementTestCase
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isSessionBound(Session session) {
|
public static boolean isSessionBound(Session session) {
|
||||||
return sessionMap() != null && sessionMap().containsKey( me.factory )
|
return sessionMap() != null && sessionMap().containsKey( me.factory() )
|
||||||
&& sessionMap().get( me.factory ) == session;
|
&& sessionMap().get( me.factory() ) == session;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean hasBind() {
|
public static boolean hasBind() {
|
||||||
return sessionMap() != null && sessionMap().containsKey( me.factory );
|
return sessionMap() != null && sessionMap().containsKey( me.factory() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue