Java tutorial
/* * 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.Serializable; import java.lang.reflect.Method; import java.sql.Connection; import java.sql.SQLException; 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 javax.naming.Reference; import javax.naming.StringRefAddr; import org.hibernate.AssertionFailure; import org.hibernate.Cache; import org.hibernate.ConnectionReleaseMode; import org.hibernate.CustomEntityDirtinessStrategy; import org.hibernate.EmptyInterceptor; import org.hibernate.EntityNameResolver; import org.hibernate.HibernateException; import org.hibernate.Interceptor; import org.hibernate.MappingException; import org.hibernate.MultiTenancyStrategy; 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.CollectionRegion; import org.hibernate.cache.spi.EntityRegion; import org.hibernate.cache.spi.NaturalIdRegion; import org.hibernate.cache.spi.QueryCache; import org.hibernate.cache.spi.Region; import org.hibernate.cache.spi.RegionFactory; 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.NaturalIdRegionAccessStrategy; import org.hibernate.cache.spi.access.RegionAccessStrategy; import org.hibernate.cfg.AvailableSettings; 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.context.spi.CurrentTenantIdentifierResolver; 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.JdbcConnectionAccess; 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.CacheImplementor; 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.SessionBuilderImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionOwner; 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.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.proxy.EntityNotFoundDelegate; import org.hibernate.service.ServiceRegistry; import org.hibernate.service.classloading.spi.ClassLoaderService; import org.hibernate.service.classloading.spi.ClassLoadingException; import org.hibernate.service.config.spi.ConfigurationService; import org.hibernate.service.jdbc.connections.spi.ConnectionProvider; import org.hibernate.service.jdbc.connections.spi.MultiTenantConnectionProvider; 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; import org.jboss.logging.Logger; import org.springframework.util.ReflectionUtils; import com.gary.dao.hibernate.StaticDialect; /** * 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 extends StaticDialect 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<String, EntityPersister> entityPersisters; private final transient Map<String, ClassMetadata> classMetadata; private final transient Map<String, CollectionPersister> collectionPersisters; private final transient Map<String, CollectionMetadata> 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 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 CacheImplementor cacheAccess; 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; private final transient CustomEntityDirtinessStrategy customEntityDirtinessStrategy; private final transient CurrentTenantIdentifierResolver currentTenantIdentifierResolver; @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; public Interceptor getInterceptor() { return cfg.getInterceptor(); } 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.cacheAccess = this.serviceRegistry.getService(CacheImplementor.class); final RegionFactory regionFactory = cacheAccess.getRegionFactory(); 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); 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>(); public void sessionFactoryCreated(SessionFactory factory) { } 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() + "."; final PersisterFactory persisterFactory = serviceRegistry.getService(PersisterFactory.class); 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) { LOG.tracef("Building shared cache region for entity data [%s]", model.getEntityName()); EntityRegion entityRegion = regionFactory.buildEntityRegion(cacheRegionName, properties, CacheDataDescriptionImpl.decode(model)); accessStrategy = entityRegion.buildAccessStrategy(accessType); entityAccessStrategies.put(cacheRegionName, accessStrategy); cacheAccess.addCacheRegion(cacheRegionName, entityRegion); } } NaturalIdRegionAccessStrategy naturalIdAccessStrategy = null; if (model.hasNaturalId() && model.getNaturalIdCacheRegionName() != null) { final String naturalIdCacheRegionName = cacheRegionPrefix + model.getNaturalIdCacheRegionName(); naturalIdAccessStrategy = (NaturalIdRegionAccessStrategy) entityAccessStrategies .get(naturalIdCacheRegionName); if (naturalIdAccessStrategy == null && settings.isSecondLevelCacheEnabled()) { final CacheDataDescriptionImpl cacheDataDescription = CacheDataDescriptionImpl.decode(model); NaturalIdRegion naturalIdRegion = null; try { naturalIdRegion = regionFactory.buildNaturalIdRegion(naturalIdCacheRegionName, properties, cacheDataDescription); } catch (UnsupportedOperationException e) { LOG.warnf( "Shared cache region factory [%s] does not support natural id caching; " + "shared NaturalId caching will be disabled for not be enabled for %s", regionFactory.getClass().getName(), model.getEntityName()); } if (naturalIdRegion != null) { naturalIdAccessStrategy = naturalIdRegion .buildAccessStrategy(regionFactory.getDefaultAccessType()); entityAccessStrategies.put(naturalIdCacheRegionName, naturalIdAccessStrategy); cacheAccess.addCacheRegion(naturalIdCacheRegionName, naturalIdRegion); } } } EntityPersister cp = persisterFactory.createEntityPersister(model, accessStrategy, naturalIdAccessStrategy, 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<String, CollectionPersister>(); Map<String, CollectionMetadata> tmpCollectionMetadata = new HashMap<String, CollectionMetadata>(); 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()) { LOG.tracev("Building shared cache region for collection data [{0}]", model.getRole()); CollectionRegion collectionRegion = regionFactory.buildCollectionRegion(cacheRegionName, properties, CacheDataDescriptionImpl.decode(model)); accessStrategy = collectionRegion.buildAccessStrategy(accessType); entityAccessStrategies.put(cacheRegionName, accessStrategy); cacheAccess.addCacheRegion(cacheRegionName, collectionRegion); } CollectionPersister persister = persisterFactory.createCollectionPersister(cfg, model, accessStrategy, this); collectionPersisters.put(model.getRole(), persister); tmpCollectionMetadata.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(tmpCollectionMetadata); 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, settings.isSessionFactoryNameAlsoJndiName(), this, serviceRegistry.getService(JndiService.class)); LOG.debug("Instantiated session factory"); settings.getMultiTableBulkIdStrategy().prepare(jdbcServices, buildLocalConnectionAccess(), cfg.createMappings(), cfg.buildMapping(), properties); 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(); // checking for named queries if (settings.isNamedQueryStartupCheckingEnabled()) { final Map<String, HibernateException> errors = checkNamedQueries(); if (!errors.isEmpty()) { StringBuilder failingQueries = new StringBuilder("Errors in named queries: "); String sep = ""; for (Map.Entry<String, HibernateException> entry : errors.entrySet()) { LOG.namedQueryError(entry.getKey(), entry.getValue()); failingQueries.append(sep).append(entry.getKey()); sep = ", "; } 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()); for (org.hibernate.mapping.FetchProfile.Fetch mappingFetch : mappingProfile.getFetches()) { // resolve the persister owning the fetch final String entityName = getImportedClassName(mappingFetch.getEntity()); final EntityPersister owner = 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.customEntityDirtinessStrategy = determineCustomEntityDirtinessStrategy(); this.currentTenantIdentifierResolver = determineCurrentTenantIdentifierResolver( cfg.getCurrentTenantIdentifierResolver()); this.transactionEnvironment = new TransactionEnvironmentImpl(this); this.observer.sessionFactoryCreated(this); } private JdbcConnectionAccess buildLocalConnectionAccess() { return new JdbcConnectionAccess() { public Connection obtainConnection() throws SQLException { return settings.getMultiTenancyStrategy() == MultiTenancyStrategy.NONE ? serviceRegistry.getService(ConnectionProvider.class).getConnection() : serviceRegistry.getService(MultiTenantConnectionProvider.class).getAnyConnection(); } public void releaseConnection(Connection connection) throws SQLException { if (settings.getMultiTenancyStrategy() == MultiTenancyStrategy.NONE) { serviceRegistry.getService(ConnectionProvider.class).closeConnection(connection); } else { serviceRegistry.getService(MultiTenantConnectionProvider.class) .releaseAnyConnection(connection); } } public boolean supportsAggressiveRelease() { return false; } }; } @SuppressWarnings({ "unchecked" }) private CustomEntityDirtinessStrategy determineCustomEntityDirtinessStrategy() { CustomEntityDirtinessStrategy defaultValue = new CustomEntityDirtinessStrategy() { public boolean canDirtyCheck(Object entity, EntityPersister persister, Session session) { return false; } public boolean isDirty(Object entity, EntityPersister persister, Session session) { return false; } public void resetDirty(Object entity, EntityPersister persister, Session session) { } public void findDirty(Object entity, EntityPersister persister, Session session, DirtyCheckContext dirtyCheckContext) { // todo : implement proper method body } }; return serviceRegistry.getService(ConfigurationService.class).getSetting( AvailableSettings.CUSTOM_ENTITY_DIRTINESS_STRATEGY, CustomEntityDirtinessStrategy.class, defaultValue); } @SuppressWarnings({ "unchecked" }) private CurrentTenantIdentifierResolver determineCurrentTenantIdentifierResolver( CurrentTenantIdentifierResolver explicitResolver) { if (explicitResolver != null) { return explicitResolver; } return serviceRegistry.getService(ConfigurationService.class).getSetting( AvailableSettings.MULTI_TENANT_IDENTIFIER_RESOLVER, CurrentTenantIdentifierResolver.class, null); } @SuppressWarnings({ "ThrowableResultOfMethodCallIgnored" }) public SessionFactoryImpl(MetadataImplementor metadata, SessionFactoryOptions sessionFactoryOptions, SessionFactoryObserver observer) throws HibernateException { final boolean traceEnabled = LOG.isTraceEnabled(); final boolean debugEnabled = traceEnabled || LOG.isDebugEnabled(); if (debugEnabled) { 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(); this.cacheAccess = this.serviceRegistry.getService(CacheImplementor.class); // 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); } if (debugEnabled) { LOG.debugf("Session factory constructed with filter configurations : %s", filters); LOG.debugf("Instantiating session factory with properties: %s", properties); } this.queryPlanCache = new QueryPlanCache(this); class IntegratorObserver implements SessionFactoryObserver { private ArrayList<Integrator> integrators = new ArrayList<Integrator>(); public void sessionFactoryCreated(SessionFactory factory) { } 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<String, EntityPersister>(); 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 (traceEnabled) { 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); cacheAccess.addCacheRegion(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<String, CollectionPersister>(); Map<String, CollectionMetadata> tmpCollectionMetadata = new HashMap<String, CollectionMetadata>(); 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 (traceEnabled) { 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); cacheAccess.addCacheRegion(cacheRegionName, collectionRegion); } CollectionPersister persister = serviceRegistry.getService(PersisterFactory.class) .createCollectionPersister(metadata, model, accessStrategy, this); collectionPersisters.put(model.getAttribute().getRole(), persister); tmpCollectionMetadata.put(model.getAttribute().getRole(), persister.getCollectionMetadata()); Type indexType = persister.getIndexType(); if (indexType != null && indexType.isAssociationType() && !indexType.isAnyType()) { String entityName = ((AssociationType) indexType).getAssociatedEntityName(this); Set<String> roles = tmpEntityToCollectionRoleMap.get(entityName); if (roles == null) { roles = new HashSet<String>(); tmpEntityToCollectionRoleMap.put(entityName, roles); } roles.add(persister.getRole()); } Type elementType = persister.getElementType(); if (elementType.isAssociationType() && !elementType.isAnyType()) { String entityName = ((AssociationType) elementType).getAssociatedEntityName(this); Set<String> roles = tmpEntityToCollectionRoleMap.get(entityName); if (roles == null) { roles = new HashSet<String>(); tmpEntityToCollectionRoleMap.put(entityName, roles); } roles.add(persister.getRole()); } } collectionMetadata = Collections.unmodifiableMap(tmpCollectionMetadata); for (Map.Entry<String, Set<String>> entry : tmpEntityToCollectionRoleMap.entrySet()) { entry.setValue(Collections.unmodifiableSet(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, settings.isSessionFactoryNameAlsoJndiName(), this, serviceRegistry.getService(JndiService.class)); if (debugEnabled) { LOG.debug("Instantiated session factory"); } if (settings.isAutoCreateSchema()) { new SchemaExport(metadata) .setImportSqlCommandExtractor(serviceRegistry.getService(ImportSqlCommandExtractor.class)) .create(false, true); } if (settings.isAutoDropSchema()) { schemaExport = new SchemaExport(metadata) .setImportSqlCommandExtractor(serviceRegistry.getService(ImportSqlCommandExtractor.class)); } currentSessionContext = buildCurrentSessionContext(); // checking for named queries if (settings.isNamedQueryStartupCheckingEnabled()) { final Map<String, HibernateException> errors = checkNamedQueries(); if (!errors.isEmpty()) { StringBuilder failingQueries = new StringBuilder("Errors in named queries: "); String sep = ""; for (Map.Entry<String, HibernateException> entry : errors.entrySet()) { LOG.namedQueryError(entry.getKey(), entry.getValue()); failingQueries.append(entry.getKey()).append(sep); sep = ", "; } 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 = 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.customEntityDirtinessStrategy = determineCustomEntityDirtinessStrategy(); this.currentTenantIdentifierResolver = determineCurrentTenantIdentifierResolver(null); 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(); } public SessionBuilderImplementor withOptions() { return new SessionBuilderImpl(this); } public StatelessSessionBuilder withStatelessOptions() { return new StatelessSessionBuilderImpl(this); } public StatelessSession openStatelessSession() { return withStatelessOptions().openStatelessSession(); } public StatelessSession openStatelessSession(Connection connection) { return withStatelessOptions().connection(connection).openStatelessSession(); } 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<String, HibernateException> checkNamedQueries() throws HibernateException { Map<String, HibernateException> errors = new HashMap<String, HibernateException>(); final boolean debugEnabled = LOG.isDebugEnabled(); // Check named HQL queries if (debugEnabled) { 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 { if (debugEnabled) { LOG.debugf("Checking named query: %s", queryName); } // TODO: BUG! this currently fails for named queries for // non-POJO entities queryPlanCache.getHQLQueryPlan(qd.getQueryString(), false, Collections.EMPTY_MAP); } catch (QueryException e) { errors.put(queryName, e); } catch (MappingException e) { errors.put(queryName, e); } } if (debugEnabled) { 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 { if (debugEnabled) { 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 = entityPersisters.get(entityName); if (result == null) { throw new MappingException("Unknown entity: " + entityName); } return result; } public Map<String, CollectionPersister> getCollectionPersisters() { return collectionPersisters; } public Map<String, EntityPersister> getEntityPersisters() { return entityPersisters; } public CollectionPersister getCollectionPersister(String role) throws MappingException { CollectionPersister result = collectionPersisters.get(role); if (result == null) { throw new MappingException("Unknown collection role: " + role); } return result; } public Settings getSettings() { return settings; } 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."); } try { if (currentSessionContext != null) { Method m = getCurrentSession().getClass().getMethod("connection"); Connection con = (Connection) ReflectionUtils.invokeMethod(m, getCurrentSession()); return resolveDialectInternal(con.getMetaData()); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } 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); } 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); } 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(); } public Type[] getReturnTypes(String queryString) throws HibernateException { return queryPlanCache.getHQLQueryPlan(queryString, false, Collections.EMPTY_MAP).getReturnMetadata() .getReturnTypes(); } public String[] getReturnAliases(String queryString) throws HibernateException { return queryPlanCache.getHQLQueryPlan(queryString, false, Collections.EMPTY_MAP).getReturnMetadata() .getReturnAliases(); } public ClassMetadata getClassMetadata(Class persistentClass) throws HibernateException { return getClassMetadata(persistentClass.getName()); } public CollectionMetadata getCollectionMetadata(String roleName) throws HibernateException { return collectionMetadata.get(roleName); } public ClassMetadata getClassMetadata(String entityName) throws HibernateException { return classMetadata.get(entityName); } /** * Given the name of an entity class, determine all the class and interface * names by which it can be referenced in an HQL query. * * @param className * The name of the entity class * * @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 = serviceRegistry.getService(ClassLoaderService.class).classForName(className); } catch (ClassLoadingException cnfe) { return new String[] { className }; // for a dynamic-class } ArrayList<String> results = new ArrayList<String>(); for (EntityPersister checkPersister : entityPersisters.values()) { if (!Queryable.class.isInstance(checkPersister)) { continue; } final Queryable checkQueryable = Queryable.class.cast(checkPersister); final String checkQueryableEntityName = checkQueryable.getEntityName(); final boolean isMappedClass = className.equals(checkQueryableEntityName); if (checkQueryable.isExplicitPolymorphism()) { if (isMappedClass) { return new String[] { className }; // NOTE EARLY EXIT } } else { if (isMappedClass) { results.add(checkQueryableEntityName); } else { final Class mappedClass = checkQueryable.getMappedClass(); if (mappedClass != null && clazz.isAssignableFrom(mappedClass)) { final boolean assignableSuperclass; if (checkQueryable.isInherited()) { Class mappedSuperclass = getEntityPersister(checkQueryable.getMappedSuperclass()) .getMappedClass(); assignableSuperclass = clazz.isAssignableFrom(mappedSuperclass); } else { assignableSuperclass = false; } if (!assignableSuperclass) { results.add(checkQueryableEntityName); } } } } } return results.toArray(new String[results.size()]); } public String getImportedClassName(String className) { String result = imports.get(className); if (result == null) { try { serviceRegistry.getService(ClassLoaderService.class).classForName(className); return className; } catch (ClassLoadingException 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; settings.getMultiTableBulkIdStrategy().release(jdbcServices, buildLocalConnectionAccess()); 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(); } } cacheAccess.close(); queryPlanCache.cleanup(); if (settings.isAutoDropSchema()) { schemaExport.drop(false, true); } SessionFactoryRegistry.INSTANCE.removeSessionFactory(uuid, name, settings.isSessionFactoryNameAlsoJndiName(), serviceRegistry.getService(JndiService.class)); observer.sessionFactoryClosed(this); serviceRegistry.destroy(); } 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 { cacheAccess.evictQueries(); } public void evictQueries(String regionName) throws HibernateException { getCache().evictQueryRegion(regionName); } public UpdateTimestampsCache getUpdateTimestampsCache() { return cacheAccess.getUpdateTimestampsCache(); } public QueryCache getQueryCache() { return cacheAccess.getQueryCache(); } public QueryCache getQueryCache(String regionName) throws HibernateException { return cacheAccess.getQueryCache(regionName); } public Region getSecondLevelCacheRegion(String regionName) { return cacheAccess.getSecondLevelCacheRegion(regionName); } public Region getNaturalIdCacheRegion(String regionName) { return cacheAccess.getNaturalIdCacheRegion(regionName); } @SuppressWarnings({ "unchecked" }) public Map getAllSecondLevelCacheRegions() { return cacheAccess.getAllSecondLevelCacheRegions(); } 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 = serviceRegistry.getService(ClassLoaderService.class).classForName(impl); return (CurrentSessionContext) implClass .getConstructor(new Class[] { SessionFactoryImplementor.class }).newInstance(this); } catch (Throwable t) { LOG.unableToConstructCurrentSessionContext(impl, t); return null; } } } public ServiceRegistryImplementor getServiceRegistry() { return serviceRegistry; } public EntityNotFoundDelegate getEntityNotFoundDelegate() { return sessionFactoryOptions.getEntityNotFoundDelegate(); } public SQLFunctionRegistry getSqlFunctionRegistry() { return sqlFunctionRegistry; } public FetchProfile getFetchProfile(String name) { return fetchProfiles.get(name); } public TypeHelper getTypeHelper() { return typeHelper; } static class SessionBuilderImpl implements SessionBuilderImplementor { private final SessionFactoryImpl sessionFactory; private SessionOwner sessionOwner; 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; this.sessionOwner = null; 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(); if (sessionFactory.getCurrentTenantIdentifierResolver() != null) { tenantIdentifier = sessionFactory.getCurrentTenantIdentifierResolver() .resolveCurrentTenantIdentifier(); } } protected TransactionCoordinatorImpl getTransactionCoordinator() { return null; } public Session openSession() { return new SessionImpl(connection, sessionFactory, sessionOwner, getTransactionCoordinator(), autoJoinTransactions, sessionFactory.settings.getRegionFactory().nextTimestamp(), interceptor, flushBeforeCompletion, autoClose, connectionReleaseMode, tenantIdentifier); } public SessionBuilder owner(SessionOwner sessionOwner) { this.sessionOwner = sessionOwner; return this; } public SessionBuilder interceptor(Interceptor interceptor) { this.interceptor = interceptor; return this; } public SessionBuilder noInterceptor() { this.interceptor = EmptyInterceptor.INSTANCE; return this; } public SessionBuilder connection(Connection connection) { this.connection = connection; return this; } public SessionBuilder connectionReleaseMode(ConnectionReleaseMode connectionReleaseMode) { this.connectionReleaseMode = connectionReleaseMode; return this; } public SessionBuilder autoJoinTransactions(boolean autoJoinTransactions) { this.autoJoinTransactions = autoJoinTransactions; return this; } public SessionBuilder autoClose(boolean autoClose) { this.autoClose = autoClose; return this; } public SessionBuilder flushBeforeCompletion(boolean flushBeforeCompletion) { this.flushBeforeCompletion = flushBeforeCompletion; return this; } 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; if (sessionFactory.getCurrentTenantIdentifierResolver() != null) { tenantIdentifier = sessionFactory.getCurrentTenantIdentifierResolver() .resolveCurrentTenantIdentifier(); } } public StatelessSession openStatelessSession() { return new StatelessSessionImpl(connection, tenantIdentifier, sessionFactory); } public StatelessSessionBuilder connection(Connection connection) { this.connection = connection; return this; } public StatelessSessionBuilder tenantIdentifier(String tenantIdentifier) { this.tenantIdentifier = tenantIdentifier; return this; } } public CustomEntityDirtinessStrategy getCustomEntityDirtinessStrategy() { return customEntityDirtinessStrategy; } public CurrentTenantIdentifierResolver getCurrentTenantIdentifierResolver() { return currentTenantIdentifierResolver; } // Serialization handling // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** * Custom serialization hook defined by Java spec. Used when the factory is * directly serialized * * @param out * The stream into which the object is being serialized. * * @throws IOException * Can be thrown by the stream */ private void writeObject(ObjectOutputStream out) throws IOException { LOG.debugf("Serializing: %s", uuid); out.defaultWriteObject(); LOG.trace("Serialized"); } /** * Custom serialization hook defined by Java spec. Used when the factory is * directly deserialized * * @param in * The stream from which the object is being deserialized. * * @throws IOException * Can be thrown by the stream * @throws ClassNotFoundException * Again, can be thrown by the stream */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { LOG.trace("Deserializing"); in.defaultReadObject(); LOG.debugf("Deserialized: %s", uuid); } /** * Custom serialization hook defined by Java spec. Used when the factory is * directly deserialized. Here we resolve the uuid/name read from the stream * previously to resolve the SessionFactory instance to use based on the * registrations with the {@link SessionFactoryRegistry} * * @return The resolved factory to use. * * @throws InvalidObjectException * Thrown if we could not resolve the factory by uuid/name. */ private Object readResolve() throws InvalidObjectException { LOG.trace("Resolving serialized SessionFactory"); return locateSessionFactoryOnDeserialization(uuid, name); } private static SessionFactory locateSessionFactoryOnDeserialization(String uuid, String name) throws InvalidObjectException { final SessionFactory uuidResult = SessionFactoryRegistry.INSTANCE.getSessionFactory(uuid); if (uuidResult != null) { LOG.debugf("Resolved SessionFactory by UUID [%s]", uuid); return uuidResult; } // in case we were deserialized in a different JVM, look for an instance // with the same name // (provided we were given a name) if (name != null) { final SessionFactory namedResult = SessionFactoryRegistry.INSTANCE.getNamedSessionFactory(name); if (namedResult != null) { LOG.debugf("Resolved SessionFactory by name [%s]", name); return namedResult; } } throw new InvalidObjectException("Could not find a SessionFactory [uuid=" + uuid + ",name=" + name + "]"); } /** * 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 { LOG.trace("Deserializing SessionFactory from Session"); final String uuid = ois.readUTF(); boolean isNamed = ois.readBoolean(); final String name = isNamed ? ois.readUTF() : null; return (SessionFactoryImpl) locateSessionFactoryOnDeserialization(uuid, name); } }