/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, 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.internal;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.naming.Reference;
import javax.naming.StringRefAddr;
import org.jboss.logging.Logger;
import org.hibernate.AssertionFailure;
import org.hibernate.Cache;
import org.hibernate.ConnectionReleaseMode;
import org.hibernate.EmptyInterceptor;
import org.hibernate.EntityNameResolver;
import org.hibernate.HibernateException;
import org.hibernate.Interceptor;
import org.hibernate.MappingException;
import org.hibernate.ObjectNotFoundException;
import org.hibernate.QueryException;
import org.hibernate.Session;
import org.hibernate.SessionBuilder;
import org.hibernate.SessionFactory;
import org.hibernate.SessionFactoryObserver;
import org.hibernate.StatelessSession;
import org.hibernate.StatelessSessionBuilder;
import org.hibernate.TypeHelper;
import org.hibernate.cache.internal.CacheDataDescriptionImpl;
import org.hibernate.cache.spi.CacheKey;
import org.hibernate.cache.spi.CollectionRegion;
import org.hibernate.cache.spi.EntityRegion;
import org.hibernate.cache.spi.QueryCache;
import org.hibernate.cache.spi.Region;
import org.hibernate.cache.spi.UpdateTimestampsCache;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy;
import org.hibernate.cache.spi.access.EntityRegionAccessStrategy;
import org.hibernate.cache.spi.access.RegionAccessStrategy;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.cfg.Settings;
import org.hibernate.cfg.SettingsFactory;
import org.hibernate.context.internal.JTASessionContext;
import org.hibernate.context.internal.ManagedSessionContext;
import org.hibernate.context.internal.ThreadLocalSessionContext;
import org.hibernate.context.spi.CurrentSessionContext;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.function.SQLFunction;
import org.hibernate.dialect.function.SQLFunctionRegistry;
import org.hibernate.engine.ResultSetMappingDefinition;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.jdbc.spi.SqlExceptionHelper;
import org.hibernate.engine.profile.Association;
import org.hibernate.engine.profile.Fetch;
import org.hibernate.engine.profile.FetchProfile;
import org.hibernate.engine.query.spi.QueryPlanCache;
import org.hibernate.engine.query.spi.sql.NativeSQLQuerySpecification;
import org.hibernate.engine.spi.FilterDefinition;
import org.hibernate.engine.spi.Mapping;
import org.hibernate.engine.spi.NamedQueryDefinition;
import org.hibernate.engine.spi.NamedSQLQueryDefinition;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.transaction.internal.TransactionCoordinatorImpl;
import org.hibernate.engine.transaction.spi.TransactionEnvironment;
import org.hibernate.exception.spi.SQLExceptionConverter;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.UUIDGenerator;
import org.hibernate.id.factory.IdentifierGeneratorFactory;
import org.hibernate.integrator.spi.Integrator;
import org.hibernate.integrator.spi.IntegratorService;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.RootClass;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.metadata.CollectionMetadata;
import org.hibernate.metamodel.binding.EntityBinding;
import org.hibernate.metamodel.binding.PluralAttributeBinding;
import org.hibernate.metamodel.source.MetadataImplementor;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Loadable;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.persister.spi.PersisterFactory;
import org.hibernate.pretty.MessageHelper;
import org.hibernate.proxy.EntityNotFoundDelegate;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.config.spi.ConfigurationService;
import org.hibernate.service.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.service.jndi.spi.JndiService;
import org.hibernate.service.jta.platform.spi.JtaPlatform;
import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.hibernate.service.spi.SessionFactoryServiceRegistry;
import org.hibernate.service.spi.SessionFactoryServiceRegistryFactory;
import org.hibernate.stat.Statistics;
import org.hibernate.stat.spi.StatisticsImplementor;
import org.hibernate.tool.hbm2ddl.ImportSqlCommandExtractor;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import org.hibernate.tool.hbm2ddl.SchemaUpdate;
import org.hibernate.tool.hbm2ddl.SchemaValidator;
import org.hibernate.tuple.entity.EntityTuplizer;
import org.hibernate.type.AssociationType;
import org.hibernate.type.Type;
import org.hibernate.type.TypeResolver;
/**
* Concrete implementation of the <tt>SessionFactory</tt> interface. Has the following
* responsibilities
* <ul>
* <li>caches configuration settings (immutably)
* <li>caches "compiled" mappings ie. <tt>EntityPersister</tt>s and
* <tt>CollectionPersister</tt>s (immutable)
* <li>caches "compiled" queries (memory sensitive cache)
* <li>manages <tt>PreparedStatement</tt>s
* <li> delegates JDBC <tt>Connection</tt> management to the <tt>ConnectionProvider</tt>
* <li>factory for instances of <tt>SessionImpl</tt>
* </ul>
* This class must appear immutable to clients, even if it does all kinds of caching
* and pooling under the covers. It is crucial that the class is not only thread
* safe, but also highly concurrent. Synchronization must be used extremely sparingly.
*
* @see org.hibernate.service.jdbc.connections.spi.ConnectionProvider
* @see org.hibernate.Session
* @see org.hibernate.hql.spi.QueryTranslator
* @see org.hibernate.persister.entity.EntityPersister
* @see org.hibernate.persister.collection.CollectionPersister
* @author Gavin King
*/
public final class SessionFactoryImpl
implements SessionFactoryImplementor {
private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, SessionFactoryImpl.class.getName());
private static final IdentifierGenerator UUID_GENERATOR = UUIDGenerator.buildSessionFactoryUniqueIdentifierGenerator();
private final String name;
private final String uuid;
private final transient Map entityPersisters;
private final transient Map<String,ClassMetadata> classMetadata;
private final transient Map collectionPersisters;
private final transient Map collectionMetadata;
private final transient Map<String,Set<String>> collectionRolesByEntityParticipant;
private final transient Map<String,IdentifierGenerator> identifierGenerators;
private final transient Map<String, NamedQueryDefinition> namedQueries;
private final transient Map<String, NamedSQLQueryDefinition> namedSqlQueries;
private final transient Map<String, ResultSetMappingDefinition> sqlResultSetMappings;
private final transient Map<String, FilterDefinition> filters;
private final transient Map<String, FetchProfile> fetchProfiles;
private final transient Map<String,String> imports;
private final transient SessionFactoryServiceRegistry serviceRegistry;
private final transient JdbcServices jdbcServices;
private final transient Dialect dialect;
private final transient Settings settings;
private final transient Properties properties;
private transient SchemaExport schemaExport;
private final transient QueryCache queryCache;
private final transient UpdateTimestampsCache updateTimestampsCache;
private final transient Map<String,QueryCache> queryCaches;
private final transient ConcurrentMap<String,Region> allCacheRegions = new ConcurrentHashMap<String, Region>();
private final transient CurrentSessionContext currentSessionContext;
private final transient SQLFunctionRegistry sqlFunctionRegistry;
private final transient SessionFactoryObserverChain observer = new SessionFactoryObserverChain();
private final transient ConcurrentHashMap<EntityNameResolver,Object> entityNameResolvers = new ConcurrentHashMap<EntityNameResolver, Object>();
private final transient QueryPlanCache queryPlanCache;
private final transient Cache cacheAccess = new CacheImpl();
private transient boolean isClosed = false;
private final transient TypeResolver typeResolver;
private final transient TypeHelper typeHelper;
private final transient TransactionEnvironment transactionEnvironment;
private final transient SessionFactoryOptions sessionFactoryOptions;
@SuppressWarnings( {"unchecked"} )
public SessionFactoryImpl(
final Configuration cfg,
Mapping mapping,
ServiceRegistry serviceRegistry,
Settings settings,
SessionFactoryObserver observer) throws HibernateException {
LOG.debug( "Building session factory" );
sessionFactoryOptions = new SessionFactoryOptions() {
private EntityNotFoundDelegate entityNotFoundDelegate;
@Override
public Interceptor getInterceptor() {
return cfg.getInterceptor();
}
@Override
public EntityNotFoundDelegate getEntityNotFoundDelegate() {
if ( entityNotFoundDelegate == null ) {
if ( cfg.getEntityNotFoundDelegate() != null ) {
entityNotFoundDelegate = cfg.getEntityNotFoundDelegate();
}
else {
entityNotFoundDelegate = new EntityNotFoundDelegate() {
public void handleEntityNotFound(String entityName, Serializable id) {
throw new ObjectNotFoundException( id, entityName );
}
};
}
}
return entityNotFoundDelegate;
}
};
this.settings = settings;
this.properties = new Properties();
this.properties.putAll( cfg.getProperties() );
this.serviceRegistry = serviceRegistry.getService( SessionFactoryServiceRegistryFactory.class ).buildServiceRegistry(
this,
cfg
);
this.jdbcServices = this.serviceRegistry.getService( JdbcServices.class );
this.dialect = this.jdbcServices.getDialect();
this.sqlFunctionRegistry = new SQLFunctionRegistry( getDialect(), cfg.getSqlFunctions() );
if ( observer != null ) {
this.observer.addObserver( observer );
}
this.typeResolver = cfg.getTypeResolver().scope( this );
this.typeHelper = new TypeLocatorImpl( typeResolver );
this.filters = new HashMap<String, FilterDefinition>();
this.filters.putAll( cfg.getFilterDefinitions() );
LOG.debugf( "Session factory constructed with filter configurations : %s", filters );
LOG.debugf( "Instantiating session factory with properties: %s", properties );
// Caches
settings.getRegionFactory().start( settings, properties );
this.queryPlanCache = new QueryPlanCache( this );
// todo : everything above here consider implementing as standard SF service. specifically: stats, caches, types, function-reg
class IntegratorObserver implements SessionFactoryObserver {
private ArrayList<Integrator> integrators = new ArrayList<Integrator>();
@Override
public void sessionFactoryCreated(SessionFactory factory) {
}
@Override
public void sessionFactoryClosed(SessionFactory factory) {
for ( Integrator integrator : integrators ) {
integrator.disintegrate( SessionFactoryImpl.this, SessionFactoryImpl.this.serviceRegistry );
}
}
}
final IntegratorObserver integratorObserver = new IntegratorObserver();
this.observer.addObserver( integratorObserver );
for ( Integrator integrator : serviceRegistry.getService( IntegratorService.class ).getIntegrators() ) {
integrator.integrate( cfg, this, this.serviceRegistry );
integratorObserver.integrators.add( integrator );
}
//Generators:
identifierGenerators = new HashMap();
Iterator classes = cfg.getClassMappings();
while ( classes.hasNext() ) {
PersistentClass model = (PersistentClass) classes.next();
if ( !model.isInherited() ) {
IdentifierGenerator generator = model.getIdentifier().createIdentifierGenerator(
cfg.getIdentifierGeneratorFactory(),
getDialect(),
settings.getDefaultCatalogName(),
settings.getDefaultSchemaName(),
(RootClass) model
);
identifierGenerators.put( model.getEntityName(), generator );
}
}
///////////////////////////////////////////////////////////////////////
// Prepare persisters and link them up with their cache
// region/access-strategy
final String cacheRegionPrefix = settings.getCacheRegionPrefix() == null ? "" : settings.getCacheRegionPrefix() + ".";
entityPersisters = new HashMap();
Map entityAccessStrategies = new HashMap();
Map<String,ClassMetadata> classMeta = new HashMap<String,ClassMetadata>();
classes = cfg.getClassMappings();
while ( classes.hasNext() ) {
final PersistentClass model = (PersistentClass) classes.next();
model.prepareTemporaryTables( mapping, getDialect() );
final String cacheRegionName = cacheRegionPrefix + model.getRootClass().getCacheRegionName();
// cache region is defined by the root-class in the hierarchy...
EntityRegionAccessStrategy accessStrategy = ( EntityRegionAccessStrategy ) entityAccessStrategies.get( cacheRegionName );
if ( accessStrategy == null && settings.isSecondLevelCacheEnabled() ) {
final AccessType accessType = AccessType.fromExternalName( model.getCacheConcurrencyStrategy() );
if ( accessType != null ) {
if ( LOG.isTraceEnabled() ) {
LOG.tracev( "Building cache for entity data [{0}]", model.getEntityName() );
}
EntityRegion entityRegion = settings.getRegionFactory().buildEntityRegion( cacheRegionName, properties, CacheDataDescriptionImpl.decode( model ) );
accessStrategy = entityRegion.buildAccessStrategy( accessType );
entityAccessStrategies.put( cacheRegionName, accessStrategy );
allCacheRegions.put( cacheRegionName, entityRegion );
}
}
EntityPersister cp = serviceRegistry.getService( PersisterFactory.class ).createEntityPersister(
model,
accessStrategy,
this,
mapping
);
entityPersisters.put( model.getEntityName(), cp );
classMeta.put( model.getEntityName(), cp.getClassMetadata() );
}
this.classMetadata = Collections.unmodifiableMap(classMeta);
Map<String,Set<String>> tmpEntityToCollectionRoleMap = new HashMap<String,Set<String>>();
collectionPersisters = new HashMap();
Iterator collections = cfg.getCollectionMappings();
while ( collections.hasNext() ) {
Collection model = (Collection) collections.next();
final String cacheRegionName = cacheRegionPrefix + model.getCacheRegionName();
final AccessType accessType = AccessType.fromExternalName( model.getCacheConcurrencyStrategy() );
CollectionRegionAccessStrategy accessStrategy = null;
if ( accessType != null && settings.isSecondLevelCacheEnabled() ) {
if ( LOG.isTraceEnabled() ) {
LOG.tracev("Building cache for collection data [{0}]", model.getRole() );
}
CollectionRegion collectionRegion = settings.getRegionFactory().buildCollectionRegion( cacheRegionName, properties, CacheDataDescriptionImpl
.decode( model ) );
accessStrategy = collectionRegion.buildAccessStrategy( accessType );
entityAccessStrategies.put( cacheRegionName, accessStrategy );
allCacheRegions.put( cacheRegionName, collectionRegion );
}
CollectionPersister persister = serviceRegistry.getService( PersisterFactory.class ).createCollectionPersister(
cfg,
model,
accessStrategy,
this
) ;
collectionPersisters.put( model.getRole(), persister.getCollectionMetadata() );
Type indexType = persister.getIndexType();
if ( indexType != null && indexType.isAssociationType() && !indexType.isAnyType() ) {
String entityName = ( ( AssociationType ) indexType ).getAssociatedEntityName( this );
Set roles = tmpEntityToCollectionRoleMap.get( entityName );
if ( roles == null ) {
roles = new HashSet();
tmpEntityToCollectionRoleMap.put( entityName, roles );
}
roles.add( persister.getRole() );
}
Type elementType = persister.getElementType();
if ( elementType.isAssociationType() && !elementType.isAnyType() ) {
String entityName = ( ( AssociationType ) elementType ).getAssociatedEntityName( this );
Set roles = tmpEntityToCollectionRoleMap.get( entityName );
if ( roles == null ) {
roles = new HashSet();
tmpEntityToCollectionRoleMap.put( entityName, roles );
}
roles.add( persister.getRole() );
}
}
collectionMetadata = Collections.unmodifiableMap(collectionPersisters);
Iterator itr = tmpEntityToCollectionRoleMap.entrySet().iterator();
while ( itr.hasNext() ) {
final Map.Entry entry = ( Map.Entry ) itr.next();
entry.setValue( Collections.unmodifiableSet( ( Set ) entry.getValue() ) );
}
collectionRolesByEntityParticipant = Collections.unmodifiableMap( tmpEntityToCollectionRoleMap );
//Named Queries:
namedQueries = new HashMap<String, NamedQueryDefinition>( cfg.getNamedQueries() );
namedSqlQueries = new HashMap<String, NamedSQLQueryDefinition>( cfg.getNamedSQLQueries() );
sqlResultSetMappings = new HashMap<String, ResultSetMappingDefinition>( cfg.getSqlResultSetMappings() );
imports = new HashMap<String,String>( cfg.getImports() );
// after *all* persisters and named queries are registered
Iterator iter = entityPersisters.values().iterator();
while ( iter.hasNext() ) {
final EntityPersister persister = ( ( EntityPersister ) iter.next() );
persister.postInstantiate();
registerEntityNameResolvers( persister );
}
iter = collectionPersisters.values().iterator();
while ( iter.hasNext() ) {
final CollectionPersister persister = ( ( CollectionPersister ) iter.next() );
persister.postInstantiate();
}
//JNDI + Serialization:
name = settings.getSessionFactoryName();
try {
uuid = (String) UUID_GENERATOR.generate(null, null);
}
catch (Exception e) {
throw new AssertionFailure("Could not generate UUID");
}
SessionFactoryRegistry.INSTANCE.addSessionFactory( uuid, name, this, serviceRegistry.getService( JndiService.class ) );
LOG.debugf( "Instantiated session factory" );
if ( settings.isAutoCreateSchema() ) {
new SchemaExport( serviceRegistry, cfg )
.setImportSqlCommandExtractor( serviceRegistry.getService( ImportSqlCommandExtractor.class ) )
.create( false, true );
}
if ( settings.isAutoUpdateSchema() ) {
new SchemaUpdate( serviceRegistry, cfg ).execute( false, true );
}
if ( settings.isAutoValidateSchema() ) {
new SchemaValidator( serviceRegistry, cfg ).validate();
}
if ( settings.isAutoDropSchema() ) {
schemaExport = new SchemaExport( serviceRegistry, cfg )
.setImportSqlCommandExtractor( serviceRegistry.getService( ImportSqlCommandExtractor.class ) );
}
currentSessionContext = buildCurrentSessionContext();
if ( settings.isQueryCacheEnabled() ) {
updateTimestampsCache = new UpdateTimestampsCache(settings, properties, this);
queryCache = settings.getQueryCacheFactory()
.getQueryCache(null, updateTimestampsCache, settings, properties);
queryCaches = new HashMap<String,QueryCache>();
allCacheRegions.put( updateTimestampsCache.getRegion().getName(), updateTimestampsCache.getRegion() );
allCacheRegions.put( queryCache.getRegion().getName(), queryCache.getRegion() );
}
else {
updateTimestampsCache = null;
queryCache = null;
queryCaches = null;
}
//checking for named queries
if ( settings.isNamedQueryStartupCheckingEnabled() ) {
Map errors = checkNamedQueries();
if ( !errors.isEmpty() ) {
Set keys = errors.keySet();
StringBuffer failingQueries = new StringBuffer( "Errors in named queries: " );
for ( Iterator iterator = keys.iterator() ; iterator.hasNext() ; ) {
String queryName = ( String ) iterator.next();
HibernateException e = ( HibernateException ) errors.get( queryName );
failingQueries.append( queryName );
if ( iterator.hasNext() ) failingQueries.append( ", " );
LOG.namedQueryError( queryName, e );
}
throw new HibernateException( failingQueries.toString() );
}
}
// this needs to happen after persisters are all ready to go...
this.fetchProfiles = new HashMap();
itr = cfg.iterateFetchProfiles();
while ( itr.hasNext() ) {
final org.hibernate.mapping.FetchProfile mappingProfile =
( org.hibernate.mapping.FetchProfile ) itr.next();
final FetchProfile fetchProfile = new FetchProfile( mappingProfile.getName() );
Iterator fetches = mappingProfile.getFetches().iterator();
while ( fetches.hasNext() ) {
final org.hibernate.mapping.FetchProfile.Fetch mappingFetch =
( org.hibernate.mapping.FetchProfile.Fetch ) fetches.next();
// resolve the persister owning the fetch
final String entityName = getImportedClassName( mappingFetch.getEntity() );
final EntityPersister owner = ( EntityPersister ) ( entityName == null ? null : entityPersisters.get( entityName ) );
if ( owner == null ) {
throw new HibernateException(
"Unable to resolve entity reference [" + mappingFetch.getEntity()
+ "] in fetch profile [" + fetchProfile.getName() + "]"
);
}
// validate the specified association fetch
Type associationType = owner.getPropertyType( mappingFetch.getAssociation() );
if ( associationType == null || !associationType.isAssociationType() ) {
throw new HibernateException( "Fetch profile [" + fetchProfile.getName() + "] specified an invalid association" );
}
// resolve the style
final Fetch.Style fetchStyle = Fetch.Style.parse( mappingFetch.getStyle() );
// then construct the fetch instance...
fetchProfile.addFetch( new Association( owner, mappingFetch.getAssociation() ), fetchStyle );
( ( Loadable ) owner ).registerAffectingFetchProfile( fetchProfile.getName() );
}
fetchProfiles.put( fetchProfile.getName(), fetchProfile );
}
this.transactionEnvironment = new TransactionEnvironmentImpl( this );
this.observer.sessionFactoryCreated( this );
}
public SessionFactoryImpl(
MetadataImplementor metadata,
SessionFactoryOptions sessionFactoryOptions,
SessionFactoryObserver observer) throws HibernateException {
LOG.debug( "Building session factory" );
this.sessionFactoryOptions = sessionFactoryOptions;
this.properties = createPropertiesFromMap(
metadata.getServiceRegistry().getService( ConfigurationService.class ).getSettings()
);
// TODO: these should be moved into SessionFactoryOptions
this.settings = new SettingsFactory().buildSettings(
properties,
metadata.getServiceRegistry()
);
this.serviceRegistry =
metadata.getServiceRegistry()
.getService( SessionFactoryServiceRegistryFactory.class )
.buildServiceRegistry( this, metadata );
this.jdbcServices = this.serviceRegistry.getService( JdbcServices.class );
this.dialect = this.jdbcServices.getDialect();
// TODO: get SQL functions from JdbcServices (HHH-6559)
//this.sqlFunctionRegistry = new SQLFunctionRegistry( this.jdbcServices.getSqlFunctions() );
this.sqlFunctionRegistry = new SQLFunctionRegistry( this.dialect, new HashMap<String, SQLFunction>() );
// TODO: get SQL functions from a new service
// this.sqlFunctionRegistry = new SQLFunctionRegistry( getDialect(), cfg.getSqlFunctions() );
if ( observer != null ) {
this.observer.addObserver( observer );
}
this.typeResolver = metadata.getTypeResolver().scope( this );
this.typeHelper = new TypeLocatorImpl( typeResolver );
this.filters = new HashMap<String, FilterDefinition>();
for ( FilterDefinition filterDefinition : metadata.getFilterDefinitions() ) {
filters.put( filterDefinition.getFilterName(), filterDefinition );
}
LOG.debugf( "Session factory constructed with filter configurations : %s", filters );
LOG.debugf( "Instantiating session factory with properties: %s", properties );
// TODO: get RegionFactory from service registry
settings.getRegionFactory().start( settings, properties );
this.queryPlanCache = new QueryPlanCache( this );
class IntegratorObserver implements SessionFactoryObserver {
private ArrayList<Integrator> integrators = new ArrayList<Integrator>();
@Override
public void sessionFactoryCreated(SessionFactory factory) {
}
@Override
public void sessionFactoryClosed(SessionFactory factory) {
for ( Integrator integrator : integrators ) {
integrator.disintegrate( SessionFactoryImpl.this, SessionFactoryImpl.this.serviceRegistry );
}
}
}
final IntegratorObserver integratorObserver = new IntegratorObserver();
this.observer.addObserver(integratorObserver);
for (Integrator integrator : serviceRegistry.getService(IntegratorService.class).getIntegrators()) {
integrator.integrate(metadata, this, this.serviceRegistry);
integratorObserver.integrators.add(integrator);
}
//Generators:
identifierGenerators = new HashMap<String,IdentifierGenerator>();
for ( EntityBinding entityBinding : metadata.getEntityBindings() ) {
if ( entityBinding.isRoot() ) {
identifierGenerators.put(
entityBinding.getEntity().getName(),
entityBinding.getHierarchyDetails().getEntityIdentifier().getIdentifierGenerator()
);
}
}
///////////////////////////////////////////////////////////////////////
// Prepare persisters and link them up with their cache
// region/access-strategy
StringBuilder stringBuilder = new StringBuilder();
if ( settings.getCacheRegionPrefix() != null) {
stringBuilder
.append( settings.getCacheRegionPrefix() )
.append( '.' );
}
final String cacheRegionPrefix = stringBuilder.toString();
entityPersisters = new HashMap();
Map<String, RegionAccessStrategy> entityAccessStrategies = new HashMap<String, RegionAccessStrategy>();
Map<String,ClassMetadata> classMeta = new HashMap<String,ClassMetadata>();
for ( EntityBinding model : metadata.getEntityBindings() ) {
// TODO: should temp table prep happen when metadata is being built?
//model.prepareTemporaryTables( metadata, getDialect() );
// cache region is defined by the root-class in the hierarchy...
EntityBinding rootEntityBinding = metadata.getRootEntityBinding( model.getEntity().getName() );
EntityRegionAccessStrategy accessStrategy = null;
if ( settings.isSecondLevelCacheEnabled() &&
rootEntityBinding.getHierarchyDetails().getCaching() != null &&
model.getHierarchyDetails().getCaching() != null &&
model.getHierarchyDetails().getCaching().getAccessType() != null ) {
final String cacheRegionName = cacheRegionPrefix + rootEntityBinding.getHierarchyDetails().getCaching().getRegion();
accessStrategy = EntityRegionAccessStrategy.class.cast( entityAccessStrategies.get( cacheRegionName ) );
if ( accessStrategy == null ) {
final AccessType accessType = model.getHierarchyDetails().getCaching().getAccessType();
if ( LOG.isTraceEnabled() ) {
LOG.tracev( "Building cache for entity data [{0}]", model.getEntity().getName() );
}
EntityRegion entityRegion = settings.getRegionFactory().buildEntityRegion(
cacheRegionName, properties, CacheDataDescriptionImpl.decode( model )
);
accessStrategy = entityRegion.buildAccessStrategy( accessType );
entityAccessStrategies.put( cacheRegionName, accessStrategy );
allCacheRegions.put( cacheRegionName, entityRegion );
}
}
EntityPersister cp = serviceRegistry.getService( PersisterFactory.class ).createEntityPersister(
model, accessStrategy, this, metadata
);
entityPersisters.put( model.getEntity().getName(), cp );
classMeta.put( model.getEntity().getName(), cp.getClassMetadata() );
}
this.classMetadata = Collections.unmodifiableMap(classMeta);
Map<String,Set<String>> tmpEntityToCollectionRoleMap = new HashMap<String,Set<String>>();
collectionPersisters = new HashMap();
for ( PluralAttributeBinding model : metadata.getCollectionBindings() ) {
if ( model.getAttribute() == null ) {
throw new IllegalStateException( "No attribute defined for a AbstractPluralAttributeBinding: " + model );
}
if ( model.getAttribute().isSingular() ) {
throw new IllegalStateException(
"AbstractPluralAttributeBinding has a Singular attribute defined: " + model.getAttribute().getName()
);
}
final String cacheRegionName = cacheRegionPrefix + model.getCaching().getRegion();
final AccessType accessType = model.getCaching().getAccessType();
CollectionRegionAccessStrategy accessStrategy = null;
if ( accessType != null && settings.isSecondLevelCacheEnabled() ) {
if ( LOG.isTraceEnabled() ) {
LOG.tracev( "Building cache for collection data [{0}]", model.getAttribute().getRole() );
}
CollectionRegion collectionRegion = settings.getRegionFactory().buildCollectionRegion(
cacheRegionName, properties, CacheDataDescriptionImpl.decode( model )
);
accessStrategy = collectionRegion.buildAccessStrategy( accessType );
entityAccessStrategies.put( cacheRegionName, accessStrategy );
allCacheRegions.put( cacheRegionName, collectionRegion );
}
CollectionPersister persister = serviceRegistry
.getService( PersisterFactory.class )
.createCollectionPersister( metadata, model, accessStrategy, this );
collectionPersisters.put( model.getAttribute().getRole(), persister.getCollectionMetadata() );
Type indexType = persister.getIndexType();
if ( indexType != null && indexType.isAssociationType() && !indexType.isAnyType() ) {
String entityName = ( ( AssociationType ) indexType ).getAssociatedEntityName( this );
Set roles = tmpEntityToCollectionRoleMap.get( entityName );
if ( roles == null ) {
roles = new HashSet();
tmpEntityToCollectionRoleMap.put( entityName, roles );
}
roles.add( persister.getRole() );
}
Type elementType = persister.getElementType();
if ( elementType.isAssociationType() && !elementType.isAnyType() ) {
String entityName = ( ( AssociationType ) elementType ).getAssociatedEntityName( this );
Set roles = tmpEntityToCollectionRoleMap.get( entityName );
if ( roles == null ) {
roles = new HashSet();
tmpEntityToCollectionRoleMap.put( entityName, roles );
}
roles.add( persister.getRole() );
}
}
collectionMetadata = Collections.unmodifiableMap(collectionPersisters);
Iterator itr = tmpEntityToCollectionRoleMap.entrySet().iterator();
while ( itr.hasNext() ) {
final Map.Entry entry = ( Map.Entry ) itr.next();
entry.setValue( Collections.unmodifiableSet( ( Set ) entry.getValue() ) );
}
collectionRolesByEntityParticipant = Collections.unmodifiableMap( tmpEntityToCollectionRoleMap );
//Named Queries:
namedQueries = new HashMap<String,NamedQueryDefinition>();
for ( NamedQueryDefinition namedQueryDefinition : metadata.getNamedQueryDefinitions() ) {
namedQueries.put( namedQueryDefinition.getName(), namedQueryDefinition );
}
namedSqlQueries = new HashMap<String, NamedSQLQueryDefinition>();
for ( NamedSQLQueryDefinition namedNativeQueryDefinition: metadata.getNamedNativeQueryDefinitions() ) {
namedSqlQueries.put( namedNativeQueryDefinition.getName(), namedNativeQueryDefinition );
}
sqlResultSetMappings = new HashMap<String, ResultSetMappingDefinition>();
for( ResultSetMappingDefinition resultSetMappingDefinition : metadata.getResultSetMappingDefinitions() ) {
sqlResultSetMappings.put( resultSetMappingDefinition.getName(), resultSetMappingDefinition );
}
imports = new HashMap<String,String>();
for ( Map.Entry<String,String> importEntry : metadata.getImports() ) {
imports.put( importEntry.getKey(), importEntry.getValue() );
}
// after *all* persisters and named queries are registered
Iterator iter = entityPersisters.values().iterator();
while ( iter.hasNext() ) {
final EntityPersister persister = ( ( EntityPersister ) iter.next() );
persister.postInstantiate();
registerEntityNameResolvers( persister );
}
iter = collectionPersisters.values().iterator();
while ( iter.hasNext() ) {
final CollectionPersister persister = ( ( CollectionPersister ) iter.next() );
persister.postInstantiate();
}
//JNDI + Serialization:
name = settings.getSessionFactoryName();
try {
uuid = (String) UUID_GENERATOR.generate(null, null);
}
catch (Exception e) {
throw new AssertionFailure("Could not generate UUID");
}
SessionFactoryRegistry.INSTANCE.addSessionFactory( uuid, name, this, serviceRegistry.getService( JndiService.class ) );
LOG.debugf("Instantiated session factory");
if ( settings.isAutoCreateSchema() ) {
new SchemaExport( metadata )
.setImportSqlCommandExtractor( serviceRegistry.getService( ImportSqlCommandExtractor.class ) )
.create( false, true );
}
/*
if ( settings.isAutoUpdateSchema() ) {
new SchemaUpdate( metadata ).execute( false, true );
}
if ( settings.isAutoValidateSchema() ) {
new SchemaValidator( metadata ).validate();
}
*/
if ( settings.isAutoDropSchema() ) {
schemaExport = new SchemaExport( metadata )
.setImportSqlCommandExtractor( serviceRegistry.getService( ImportSqlCommandExtractor.class ) );
}
currentSessionContext = buildCurrentSessionContext();
if ( settings.isQueryCacheEnabled() ) {
updateTimestampsCache = new UpdateTimestampsCache( settings, properties, this );
queryCache = settings.getQueryCacheFactory()
.getQueryCache( null, updateTimestampsCache, settings, properties );
queryCaches = new HashMap<String,QueryCache>();
allCacheRegions.put( updateTimestampsCache.getRegion().getName(), updateTimestampsCache.getRegion() );
allCacheRegions.put( queryCache.getRegion().getName(), queryCache.getRegion() );
}
else {
updateTimestampsCache = null;
queryCache = null;
queryCaches = null;
}
//checking for named queries
if ( settings.isNamedQueryStartupCheckingEnabled() ) {
Map errors = checkNamedQueries();
if ( ! errors.isEmpty() ) {
Set keys = errors.keySet();
StringBuffer failingQueries = new StringBuffer( "Errors in named queries: " );
for ( Iterator<String> iterator = keys.iterator() ; iterator.hasNext() ; ) {
String queryName = iterator.next();
HibernateException e = ( HibernateException ) errors.get( queryName );
failingQueries.append( queryName );
if ( iterator.hasNext() ) failingQueries.append( ", " );
LOG.namedQueryError( queryName, e );
}
throw new HibernateException( failingQueries.toString() );
}
}
// this needs to happen after persisters are all ready to go...
this.fetchProfiles = new HashMap<String,FetchProfile>();
for ( org.hibernate.metamodel.binding.FetchProfile mappingProfile : metadata.getFetchProfiles() ) {
final FetchProfile fetchProfile = new FetchProfile( mappingProfile.getName() );
for ( org.hibernate.metamodel.binding.FetchProfile.Fetch mappingFetch : mappingProfile.getFetches() ) {
// resolve the persister owning the fetch
final String entityName = getImportedClassName( mappingFetch.getEntity() );
final EntityPersister owner = ( EntityPersister ) ( entityName == null ? null : entityPersisters.get( entityName ) );
if ( owner == null ) {
throw new HibernateException(
"Unable to resolve entity reference [" + mappingFetch.getEntity()
+ "] in fetch profile [" + fetchProfile.getName() + "]"
);
}
// validate the specified association fetch
Type associationType = owner.getPropertyType( mappingFetch.getAssociation() );
if ( associationType == null || ! associationType.isAssociationType() ) {
throw new HibernateException( "Fetch profile [" + fetchProfile.getName() + "] specified an invalid association" );
}
// resolve the style
final Fetch.Style fetchStyle = Fetch.Style.parse( mappingFetch.getStyle() );
// then construct the fetch instance...
fetchProfile.addFetch( new Association( owner, mappingFetch.getAssociation() ), fetchStyle );
( ( Loadable ) owner ).registerAffectingFetchProfile( fetchProfile.getName() );
}
fetchProfiles.put( fetchProfile.getName(), fetchProfile );
}
this.transactionEnvironment = new TransactionEnvironmentImpl( this );
this.observer.sessionFactoryCreated( this );
}
@SuppressWarnings( {"unchecked"} )
private static Properties createPropertiesFromMap(Map map) {
Properties properties = new Properties();
properties.putAll( map );
return properties;
}
public Session openSession() throws HibernateException {
return withOptions().openSession();
}
public Session openTemporarySession() throws HibernateException {
return withOptions()
.autoClose( false )
.flushBeforeCompletion( false )
.connectionReleaseMode( ConnectionReleaseMode.AFTER_STATEMENT )
.openSession();
}
public Session getCurrentSession() throws HibernateException {
if ( currentSessionContext == null ) {
throw new HibernateException( "No CurrentSessionContext configured!" );
}
return currentSessionContext.currentSession();
}
@Override
public SessionBuilder withOptions() {
return new SessionBuilderImpl( this );
}
@Override
public StatelessSessionBuilder withStatelessOptions() {
return new StatelessSessionBuilderImpl( this );
}
public StatelessSession openStatelessSession() {
return withStatelessOptions().openStatelessSession();
}
public StatelessSession openStatelessSession(Connection connection) {
return withStatelessOptions().connection( connection ).openStatelessSession();
}
@Override
public void addObserver(SessionFactoryObserver observer) {
this.observer.addObserver( observer );
}
public TransactionEnvironment getTransactionEnvironment() {
return transactionEnvironment;
}
public Properties getProperties() {
return properties;
}
public IdentifierGeneratorFactory getIdentifierGeneratorFactory() {
return null;
}
public TypeResolver getTypeResolver() {
return typeResolver;
}
private void registerEntityNameResolvers(EntityPersister persister) {
if ( persister.getEntityMetamodel() == null || persister.getEntityMetamodel().getTuplizer() == null ) {
return;
}
registerEntityNameResolvers( persister.getEntityMetamodel().getTuplizer() );
}
private void registerEntityNameResolvers(EntityTuplizer tuplizer) {
EntityNameResolver[] resolvers = tuplizer.getEntityNameResolvers();
if ( resolvers == null ) {
return;
}
for ( EntityNameResolver resolver : resolvers ) {
registerEntityNameResolver( resolver );
}
}
private static final Object ENTITY_NAME_RESOLVER_MAP_VALUE = new Object();
public void registerEntityNameResolver(EntityNameResolver resolver) {
entityNameResolvers.put( resolver, ENTITY_NAME_RESOLVER_MAP_VALUE );
}
public Iterable<EntityNameResolver> iterateEntityNameResolvers() {
return entityNameResolvers.keySet();
}
public QueryPlanCache getQueryPlanCache() {
return queryPlanCache;
}
private Map checkNamedQueries() throws HibernateException {
Map errors = new HashMap();
// Check named HQL queries
if ( LOG.isDebugEnabled() ) {
LOG.debugf( "Checking %s named HQL queries", namedQueries.size() );
}
Iterator itr = namedQueries.entrySet().iterator();
while ( itr.hasNext() ) {
final Map.Entry entry = ( Map.Entry ) itr.next();
final String queryName = ( String ) entry.getKey();
final NamedQueryDefinition qd = ( NamedQueryDefinition ) entry.getValue();
// this will throw an error if there's something wrong.
try {
LOG.debugf( "Checking named query: %s", queryName );
//TODO: BUG! this currently fails for named queries for non-POJO entities
queryPlanCache.getHQLQueryPlan( qd.getQueryString(), false, CollectionHelper.EMPTY_MAP );
}
catch ( QueryException e ) {
errors.put( queryName, e );
}
catch ( MappingException e ) {
errors.put( queryName, e );
}
}
if ( LOG.isDebugEnabled() ) {
LOG.debugf( "Checking %s named SQL queries", namedSqlQueries.size() );
}
itr = namedSqlQueries.entrySet().iterator();
while ( itr.hasNext() ) {
final Map.Entry entry = ( Map.Entry ) itr.next();
final String queryName = ( String ) entry.getKey();
final NamedSQLQueryDefinition qd = ( NamedSQLQueryDefinition ) entry.getValue();
// this will throw an error if there's something wrong.
try {
LOG.debugf( "Checking named SQL query: %s", queryName );
// TODO : would be really nice to cache the spec on the query-def so as to not have to re-calc the hash;
// currently not doable though because of the resultset-ref stuff...
NativeSQLQuerySpecification spec;
if ( qd.getResultSetRef() != null ) {
ResultSetMappingDefinition definition = sqlResultSetMappings.get( qd.getResultSetRef() );
if ( definition == null ) {
throw new MappingException( "Unable to find resultset-ref definition: " + qd.getResultSetRef() );
}
spec = new NativeSQLQuerySpecification(
qd.getQueryString(),
definition.getQueryReturns(),
qd.getQuerySpaces()
);
}
else {
spec = new NativeSQLQuerySpecification(
qd.getQueryString(),
qd.getQueryReturns(),
qd.getQuerySpaces()
);
}
queryPlanCache.getNativeSQLQueryPlan( spec );
}
catch ( QueryException e ) {
errors.put( queryName, e );
}
catch ( MappingException e ) {
errors.put( queryName, e );
}
}
return errors;
}
public EntityPersister getEntityPersister(String entityName) throws MappingException {
EntityPersister result = (EntityPersister) entityPersisters.get(entityName);
if (result==null) {
throw new MappingException( "Unknown entity: " + entityName );
}
return result;
}
public CollectionPersister getCollectionPersister(String role) throws MappingException {
CollectionPersister result = (CollectionPersister) collectionPersisters.get(role);
if (result==null) {
throw new MappingException( "Unknown collection role: " + role );
}
return result;
}
public Settings getSettings() {
return settings;
}
@Override
public SessionFactoryOptions getSessionFactoryOptions() {
return sessionFactoryOptions;
}
public JdbcServices getJdbcServices() {
return jdbcServices;
}
public Dialect getDialect() {
if ( serviceRegistry == null ) {
throw new IllegalStateException( "Cannot determine dialect because serviceRegistry is null." );
}
return dialect;
}
public Interceptor getInterceptor() {
return sessionFactoryOptions.getInterceptor();
}
public SQLExceptionConverter getSQLExceptionConverter() {
return getSQLExceptionHelper().getSqlExceptionConverter();
}
public SqlExceptionHelper getSQLExceptionHelper() {
return getJdbcServices().getSqlExceptionHelper();
}
public Set<String> getCollectionRolesByEntityParticipant(String entityName) {
return collectionRolesByEntityParticipant.get( entityName );
}
@Override
public Reference getReference() {
// from javax.naming.Referenceable
LOG.debug( "Returning a Reference to the SessionFactory" );
return new Reference(
SessionFactoryImpl.class.getName(),
new StringRefAddr("uuid", uuid),
SessionFactoryRegistry.ObjectFactoryImpl.class.getName(),
null
);
}
private Object readResolve() throws ObjectStreamException {
LOG.trace( "Resolving serialized SessionFactory" );
// look for the instance by uuid
Object result = SessionFactoryRegistry.INSTANCE.getSessionFactory( uuid );
if ( result == null ) {
// in case we were deserialized in a different JVM, look for an instance with the same name
// (alternatively we could do an actual JNDI lookup here....)
result = SessionFactoryRegistry.INSTANCE.getNamedSessionFactory( name );
if ( result == null ) {
throw new InvalidObjectException( "Could not find a SessionFactory [uuid=" + uuid + ",name=" + name + "]" );
}
LOG.debugf( "Resolved SessionFactory by name" );
}
else {
LOG.debugf( "Resolved SessionFactory by UUID" );
}
return result;
}
public NamedQueryDefinition getNamedQuery(String queryName) {
return namedQueries.get(queryName);
}
public NamedSQLQueryDefinition getNamedSQLQuery(String queryName) {
return namedSqlQueries.get(queryName);
}
public ResultSetMappingDefinition getResultSetMapping(String resultSetName) {
return sqlResultSetMappings.get(resultSetName);
}
public Type getIdentifierType(String className) throws MappingException {
return getEntityPersister(className).getIdentifierType();
}
public String getIdentifierPropertyName(String className) throws MappingException {
return getEntityPersister(className).getIdentifierPropertyName();
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
LOG.trace( "Deserializing" );
in.defaultReadObject();
LOG.debugf( "Deserialized: %s", uuid );
}
private void writeObject(ObjectOutputStream out) throws IOException {
LOG.debugf( "Serializing: %s", uuid );
out.defaultWriteObject();
LOG.trace( "Serialized" );
}
public Type[] getReturnTypes(String queryString) throws HibernateException {
return queryPlanCache.getHQLQueryPlan( queryString, false, CollectionHelper.EMPTY_MAP ).getReturnMetadata().getReturnTypes();
}
public String[] getReturnAliases(String queryString) throws HibernateException {
return queryPlanCache.getHQLQueryPlan( queryString, false, CollectionHelper.EMPTY_MAP ).getReturnMetadata().getReturnAliases();
}
public ClassMetadata getClassMetadata(Class persistentClass) throws HibernateException {
return getClassMetadata( persistentClass.getName() );
}
public CollectionMetadata getCollectionMetadata(String roleName) throws HibernateException {
return (CollectionMetadata) collectionMetadata.get(roleName);
}
public ClassMetadata getClassMetadata(String entityName) throws HibernateException {
return classMetadata.get(entityName);
}
/**
* @param className
* @return the names of all persistent (mapped) classes that extend or implement the
* given class or interface, accounting for implicit/explicit polymorphism settings
* and excluding mapped subclasses/joined-subclasses of other classes in the result.
* @throws MappingException
*/
public String[] getImplementors(String className) throws MappingException {
final Class clazz;
try {
clazz = ReflectHelper.classForName(className);
}
catch (ClassNotFoundException cnfe) {
return new String[] { className }; //for a dynamic-class
}
ArrayList results = new ArrayList();
Iterator iter = entityPersisters.values().iterator();
while ( iter.hasNext() ) {
//test this entity to see if we must query it
EntityPersister testPersister = (EntityPersister) iter.next();
if ( testPersister instanceof Queryable ) {
Queryable testQueryable = (Queryable) testPersister;
String testClassName = testQueryable.getEntityName();
boolean isMappedClass = className.equals(testClassName);
if ( testQueryable.isExplicitPolymorphism() ) {
if ( isMappedClass ) {
return new String[] {className}; //NOTE EARLY EXIT
}
}
else {
if (isMappedClass) {
results.add(testClassName);
}
else {
final Class mappedClass = testQueryable.getMappedClass();
if ( mappedClass!=null && clazz.isAssignableFrom( mappedClass ) ) {
final boolean assignableSuperclass;
if ( testQueryable.isInherited() ) {
Class mappedSuperclass = getEntityPersister( testQueryable.getMappedSuperclass() ).getMappedClass();
assignableSuperclass = clazz.isAssignableFrom(mappedSuperclass);
}
else {
assignableSuperclass = false;
}
if ( !assignableSuperclass ) {
results.add( testClassName );
}
}
}
}
}
}
return (String[]) results.toArray( new String[ results.size() ] );
}
public String getImportedClassName(String className) {
String result = imports.get(className);
if (result==null) {
try {
ReflectHelper.classForName( className );
return className;
}
catch (ClassNotFoundException cnfe) {
return null;
}
}
else {
return result;
}
}
public Map<String,ClassMetadata> getAllClassMetadata() throws HibernateException {
return classMetadata;
}
public Map getAllCollectionMetadata() throws HibernateException {
return collectionMetadata;
}
public Type getReferencedPropertyType(String className, String propertyName)
throws MappingException {
return getEntityPersister( className ).getPropertyType( propertyName );
}
public ConnectionProvider getConnectionProvider() {
return jdbcServices.getConnectionProvider();
}
/**
* Closes the session factory, releasing all held resources.
*
* <ol>
* <li>cleans up used cache regions and "stops" the cache provider.
* <li>close the JDBC connection
* <li>remove the JNDI binding
* </ol>
*
* Note: Be aware that the sessionFactory instance still can
* be a "heavy" object memory wise after close() has been called. Thus
* it is important to not keep referencing the instance to let the garbage
* collector release the memory.
* @throws HibernateException
*/
public void close() throws HibernateException {
if ( isClosed ) {
LOG.trace( "Already closed" );
return;
}
LOG.closing();
isClosed = true;
Iterator iter = entityPersisters.values().iterator();
while ( iter.hasNext() ) {
EntityPersister p = (EntityPersister) iter.next();
if ( p.hasCache() ) {
p.getCacheAccessStrategy().getRegion().destroy();
}
}
iter = collectionPersisters.values().iterator();
while ( iter.hasNext() ) {
CollectionPersister p = (CollectionPersister) iter.next();
if ( p.hasCache() ) {
p.getCacheAccessStrategy().getRegion().destroy();
}
}
if ( settings.isQueryCacheEnabled() ) {
queryCache.destroy();
iter = queryCaches.values().iterator();
while ( iter.hasNext() ) {
QueryCache cache = (QueryCache) iter.next();
cache.destroy();
}
updateTimestampsCache.destroy();
}
settings.getRegionFactory().stop();
if ( settings.isAutoDropSchema() ) {
schemaExport.drop( false, true );
}
SessionFactoryRegistry.INSTANCE.removeSessionFactory(
uuid, name, serviceRegistry.getService( JndiService.class )
);
observer.sessionFactoryClosed( this );
serviceRegistry.destroy();
}
private class CacheImpl implements Cache {
public boolean containsEntity(Class entityClass, Serializable identifier) {
return containsEntity( entityClass.getName(), identifier );
}
public boolean containsEntity(String entityName, Serializable identifier) {
EntityPersister p = getEntityPersister( entityName );
return p.hasCache() &&
p.getCacheAccessStrategy().getRegion().contains( buildCacheKey( identifier, p ) );
}
public void evictEntity(Class entityClass, Serializable identifier) {
evictEntity( entityClass.getName(), identifier );
}
public void evictEntity(String entityName, Serializable identifier) {
EntityPersister p = getEntityPersister( entityName );
if ( p.hasCache() ) {
if ( LOG.isDebugEnabled() ) {
LOG.debugf( "Evicting second-level cache: %s",
MessageHelper.infoString( p, identifier, SessionFactoryImpl.this ) );
}
p.getCacheAccessStrategy().evict( buildCacheKey( identifier, p ) );
}
}
private CacheKey buildCacheKey(Serializable identifier, EntityPersister p) {
return new CacheKey(
identifier,
p.getIdentifierType(),
p.getRootEntityName(),
null, // have to assume non tenancy
SessionFactoryImpl.this
);
}
public void evictEntityRegion(Class entityClass) {
evictEntityRegion( entityClass.getName() );
}
public void evictEntityRegion(String entityName) {
EntityPersister p = getEntityPersister( entityName );
if ( p.hasCache() ) {
if ( LOG.isDebugEnabled() ) {
LOG.debugf( "Evicting second-level cache: %s", p.getEntityName() );
}
p.getCacheAccessStrategy().evictAll();
}
}
public void evictEntityRegions() {
Iterator entityNames = entityPersisters.keySet().iterator();
while ( entityNames.hasNext() ) {
evictEntityRegion( ( String ) entityNames.next() );
}
}
public boolean containsCollection(String role, Serializable ownerIdentifier) {
CollectionPersister p = getCollectionPersister( role );
return p.hasCache() &&
p.getCacheAccessStrategy().getRegion().contains( buildCacheKey( ownerIdentifier, p ) );
}
public void evictCollection(String role, Serializable ownerIdentifier) {
CollectionPersister p = getCollectionPersister( role );
if ( p.hasCache() ) {
if ( LOG.isDebugEnabled() ) {
LOG.debugf( "Evicting second-level cache: %s",
MessageHelper.collectionInfoString( p, ownerIdentifier, SessionFactoryImpl.this ) );
}
CacheKey cacheKey = buildCacheKey( ownerIdentifier, p );
p.getCacheAccessStrategy().evict( cacheKey );
}
}
private CacheKey buildCacheKey(Serializable ownerIdentifier, CollectionPersister p) {
return new CacheKey(
ownerIdentifier,
p.getKeyType(),
p.getRole(),
null, // have to assume non tenancy
SessionFactoryImpl.this
);
}
public void evictCollectionRegion(String role) {
CollectionPersister p = getCollectionPersister( role );
if ( p.hasCache() ) {
if ( LOG.isDebugEnabled() ) {
LOG.debugf( "Evicting second-level cache: %s", p.getRole() );
}
p.getCacheAccessStrategy().evictAll();
}
}
public void evictCollectionRegions() {
Iterator collectionRoles = collectionPersisters.keySet().iterator();
while ( collectionRoles.hasNext() ) {
evictCollectionRegion( ( String ) collectionRoles.next() );
}
}
public boolean containsQuery(String regionName) {
return queryCaches.get( regionName ) != null;
}
public void evictDefaultQueryRegion() {
if ( settings.isQueryCacheEnabled() ) {
queryCache.clear();
}
}
public void evictQueryRegion(String regionName) {
if (regionName == null) throw new NullPointerException(
"Region-name cannot be null (use Cache#evictDefaultQueryRegion to evict the default query cache)");
if (settings.isQueryCacheEnabled()) {
QueryCache namedQueryCache = queryCaches.get(regionName);
// TODO : cleanup entries in queryCaches + allCacheRegions ?
if (namedQueryCache != null) namedQueryCache.clear();
}
}
public void evictQueryRegions() {
if ( queryCaches != null ) {
for ( QueryCache queryCache : queryCaches.values() ) {
queryCache.clear();
// TODO : cleanup entries in queryCaches + allCacheRegions ?
}
}
}
}
public Cache getCache() {
return cacheAccess;
}
public void evictEntity(String entityName, Serializable id) throws HibernateException {
getCache().evictEntity( entityName, id );
}
public void evictEntity(String entityName) throws HibernateException {
getCache().evictEntityRegion( entityName );
}
public void evict(Class persistentClass, Serializable id) throws HibernateException {
getCache().evictEntity( persistentClass, id );
}
public void evict(Class persistentClass) throws HibernateException {
getCache().evictEntityRegion( persistentClass );
}
public void evictCollection(String roleName, Serializable id) throws HibernateException {
getCache().evictCollection( roleName, id );
}
public void evictCollection(String roleName) throws HibernateException {
getCache().evictCollectionRegion( roleName );
}
public void evictQueries() throws HibernateException {
if ( settings.isQueryCacheEnabled() ) {
queryCache.clear();
}
}
public void evictQueries(String regionName) throws HibernateException {
getCache().evictQueryRegion( regionName );
}
public UpdateTimestampsCache getUpdateTimestampsCache() {
return updateTimestampsCache;
}
public QueryCache getQueryCache() {
return queryCache;
}
public QueryCache getQueryCache(String regionName) throws HibernateException {
if ( regionName == null ) {
return getQueryCache();
}
if ( !settings.isQueryCacheEnabled() ) {
return null;
}
QueryCache currentQueryCache = queryCaches.get( regionName );
if ( currentQueryCache == null ) {
currentQueryCache = settings.getQueryCacheFactory().getQueryCache( regionName, updateTimestampsCache, settings, properties );
queryCaches.put( regionName, currentQueryCache );
allCacheRegions.put( currentQueryCache.getRegion().getName(), currentQueryCache.getRegion() );
}
return currentQueryCache;
}
public Region getSecondLevelCacheRegion(String regionName) {
return allCacheRegions.get( regionName );
}
public Map getAllSecondLevelCacheRegions() {
return new HashMap( allCacheRegions );
}
public boolean isClosed() {
return isClosed;
}
public Statistics getStatistics() {
return getStatisticsImplementor();
}
public StatisticsImplementor getStatisticsImplementor() {
return serviceRegistry.getService( StatisticsImplementor.class );
}
public FilterDefinition getFilterDefinition(String filterName) throws HibernateException {
FilterDefinition def = filters.get( filterName );
if ( def == null ) {
throw new HibernateException( "No such filter configured [" + filterName + "]" );
}
return def;
}
public boolean containsFetchProfileDefinition(String name) {
return fetchProfiles.containsKey( name );
}
public Set getDefinedFilterNames() {
return filters.keySet();
}
public IdentifierGenerator getIdentifierGenerator(String rootEntityName) {
return identifierGenerators.get(rootEntityName);
}
private org.hibernate.engine.transaction.spi.TransactionFactory transactionFactory() {
return serviceRegistry.getService( org.hibernate.engine.transaction.spi.TransactionFactory.class );
}
private boolean canAccessTransactionManager() {
try {
return serviceRegistry.getService( JtaPlatform.class ).retrieveTransactionManager() != null;
}
catch (Exception e) {
return false;
}
}
private CurrentSessionContext buildCurrentSessionContext() {
String impl = properties.getProperty( Environment.CURRENT_SESSION_CONTEXT_CLASS );
// for backward-compatibility
if ( impl == null ) {
if ( canAccessTransactionManager() ) {
impl = "jta";
}
else {
return null;
}
}
if ( "jta".equals( impl ) ) {
if ( ! transactionFactory().compatibleWithJtaSynchronization() ) {
LOG.autoFlushWillNotWork();
}
return new JTASessionContext( this );
}
else if ( "thread".equals( impl ) ) {
return new ThreadLocalSessionContext( this );
}
else if ( "managed".equals( impl ) ) {
return new ManagedSessionContext( this );
}
else {
try {
Class implClass = ReflectHelper.classForName( impl );
return ( CurrentSessionContext ) implClass
.getConstructor( new Class[] { SessionFactoryImplementor.class } )
.newInstance( this );
}
catch( Throwable t ) {
LOG.unableToConstructCurrentSessionContext( impl, t );
return null;
}
}
}
@Override
public ServiceRegistryImplementor getServiceRegistry() {
return serviceRegistry;
}
@Override
public EntityNotFoundDelegate getEntityNotFoundDelegate() {
return sessionFactoryOptions.getEntityNotFoundDelegate();
}
public SQLFunctionRegistry getSqlFunctionRegistry() {
return sqlFunctionRegistry;
}
public FetchProfile getFetchProfile(String name) {
return ( FetchProfile ) fetchProfiles.get( name );
}
public TypeHelper getTypeHelper() {
return typeHelper;
}
/**
* Custom serialization hook used during Session serialization.
*
* @param oos The stream to which to write the factory
* @throws IOException Indicates problems writing out the serial data stream
*/
void serialize(ObjectOutputStream oos) throws IOException {
oos.writeUTF( uuid );
oos.writeBoolean( name != null );
if ( name != null ) {
oos.writeUTF( name );
}
}
/**
* Custom deserialization hook used during Session deserialization.
*
* @param ois The stream from which to "read" the factory
* @return The deserialized factory
* @throws IOException indicates problems reading back serial data stream
* @throws ClassNotFoundException indicates problems reading back serial data stream
*/
static SessionFactoryImpl deserialize(ObjectInputStream ois) throws IOException, ClassNotFoundException {
final String uuid = ois.readUTF();
boolean isNamed = ois.readBoolean();
final String name = isNamed ? ois.readUTF() : null;
Object result = SessionFactoryRegistry.INSTANCE.getSessionFactory( uuid );
if ( result == null ) {
LOG.tracev( "Could not locate session factory by uuid [{0}] during session deserialization; trying name", uuid );
if ( isNamed ) {
result = SessionFactoryRegistry.INSTANCE.getNamedSessionFactory( name );
}
if ( result == null ) {
throw new InvalidObjectException( "could not resolve session factory during session deserialization [uuid=" + uuid + ", name=" + name + "]" );
}
}
return ( SessionFactoryImpl ) result;
}
static class SessionBuilderImpl implements SessionBuilder {
private final SessionFactoryImpl sessionFactory;
private Interceptor interceptor;
private Connection connection;
private ConnectionReleaseMode connectionReleaseMode;
private boolean autoClose;
private boolean autoJoinTransactions = true;
private boolean flushBeforeCompletion;
private String tenantIdentifier;
SessionBuilderImpl(SessionFactoryImpl sessionFactory) {
this.sessionFactory = sessionFactory;
final Settings settings = sessionFactory.settings;
// set up default builder values...
this.interceptor = sessionFactory.getInterceptor();
this.connectionReleaseMode = settings.getConnectionReleaseMode();
this.autoClose = settings.isAutoCloseSessionEnabled();
this.flushBeforeCompletion = settings.isFlushBeforeCompletionEnabled();
}
protected TransactionCoordinatorImpl getTransactionCoordinator() {
return null;
}
@Override
public Session openSession() {
return new SessionImpl(
connection,
sessionFactory,
getTransactionCoordinator(),
autoJoinTransactions,
sessionFactory.settings.getRegionFactory().nextTimestamp(),
interceptor,
flushBeforeCompletion,
autoClose,
connectionReleaseMode,
tenantIdentifier
);
}
@Override
public SessionBuilder interceptor(Interceptor interceptor) {
this.interceptor = interceptor;
return this;
}
@Override
public SessionBuilder noInterceptor() {
this.interceptor = EmptyInterceptor.INSTANCE;
return this;
}
@Override
public SessionBuilder connection(Connection connection) {
this.connection = connection;
return this;
}
@Override
public SessionBuilder connectionReleaseMode(ConnectionReleaseMode connectionReleaseMode) {
this.connectionReleaseMode = connectionReleaseMode;
return this;
}
@Override
public SessionBuilder autoJoinTransactions(boolean autoJoinTransactions) {
this.autoJoinTransactions = autoJoinTransactions;
return this;
}
@Override
public SessionBuilder autoClose(boolean autoClose) {
this.autoClose = autoClose;
return this;
}
@Override
public SessionBuilder flushBeforeCompletion(boolean flushBeforeCompletion) {
this.flushBeforeCompletion = flushBeforeCompletion;
return this;
}
@Override
public SessionBuilder tenantIdentifier(String tenantIdentifier) {
this.tenantIdentifier = tenantIdentifier;
return this;
}
}
public static class StatelessSessionBuilderImpl implements StatelessSessionBuilder {
private final SessionFactoryImpl sessionFactory;
private Connection connection;
private String tenantIdentifier;
public StatelessSessionBuilderImpl(SessionFactoryImpl sessionFactory) {
this.sessionFactory = sessionFactory;
}
@Override
public StatelessSession openStatelessSession() {
return new StatelessSessionImpl( connection, tenantIdentifier, sessionFactory );
}
@Override
public StatelessSessionBuilder connection(Connection connection) {
this.connection = connection;
return this;
}
@Override
public StatelessSessionBuilder tenantIdentifier(String tenantIdentifier) {
this.tenantIdentifier = tenantIdentifier;
return this;
}
}
}