package org.hibernate.internal;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import javax.naming.Reference;
import javax.naming.StringRefAddr;
import javax.persistence.EntityGraph;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceContextType;
import javax.persistence.PersistenceException;
import javax.persistence.PersistenceUnitUtil;
import javax.persistence.Query;
import javax.persistence.SynchronizationType;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.spi.PersistenceUnitTransactionType;
import org.hibernate.ConnectionAcquisitionMode;
import org.hibernate.ConnectionReleaseMode;
import org.hibernate.CustomEntityDirtinessStrategy;
import org.hibernate.EmptyInterceptor;
import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
import org.hibernate.Interceptor;
import org.hibernate.MappingException;
import org.hibernate.Session;
import org.hibernate.SessionBuilder;
import org.hibernate.SessionEventListener;
import org.hibernate.SessionFactory;
import org.hibernate.SessionFactoryObserver;
import org.hibernate.StatelessSession;
import org.hibernate.StatelessSessionBuilder;
import org.hibernate.TypeHelper;
import org.hibernate.boot.cfgxml.spi.CfgXmlAccessService;
import org.hibernate.boot.cfgxml.spi.LoadedConfig;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.spi.BootstrapContext;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.boot.spi.SessionFactoryOptions;
import org.hibernate.cache.spi.CacheImplementor;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Environment;
import org.hibernate.cfg.Settings;
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.function.SQLFunctionRegistry;
import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess;
import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.jndi.spi.JndiService;
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.ReturnMetadata;
import org.hibernate.engine.spi.FilterDefinition;
import org.hibernate.engine.spi.NamedQueryDefinition;
import org.hibernate.engine.spi.NamedQueryDefinitionBuilder;
import org.hibernate.engine.spi.NamedSQLQueryDefinition;
import org.hibernate.engine.spi.NamedSQLQueryDefinitionBuilder;
import org.hibernate.engine.spi.SessionBuilderImplementor;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionOwner;
import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform;
import org.hibernate.event.service.spi.EventListenerGroup;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.EventType;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.factory.IdentifierGeneratorFactory;
import org.hibernate.integrator.spi.Integrator;
import org.hibernate.integrator.spi.IntegratorService;
import org.hibernate.internal.util.config.ConfigurationException;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.jpa.internal.AfterCompletionActionLegacyJpaImpl;
import org.hibernate.jpa.internal.ExceptionMapperLegacyJpaImpl;
import org.hibernate.jpa.internal.ManagedFlushCheckerLegacyJpaImpl;
import org.hibernate.jpa.internal.PersistenceUnitUtilImpl;
import org.hibernate.mapping.RootClass;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.metadata.CollectionMetadata;
import org.hibernate.metamodel.internal.MetamodelImpl;
import org.hibernate.metamodel.spi.MetamodelImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Loadable;
import org.hibernate.procedure.ProcedureCall;
import org.hibernate.proxy.EntityNotFoundDelegate;
import org.hibernate.proxy.HibernateProxyHelper;
import org.hibernate.query.criteria.internal.CriteriaBuilderImpl;
import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode;
import org.hibernate.resource.jdbc.spi.StatementInspector;
import org.hibernate.resource.transaction.backend.jta.internal.synchronization.AfterCompletionAction;
import org.hibernate.resource.transaction.backend.jta.internal.synchronization.ExceptionMapper;
import org.hibernate.resource.transaction.backend.jta.internal.synchronization.ManagedFlushChecker;
import org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder;
import org.hibernate.secure.spi.GrantedPermission;
import org.hibernate.secure.spi.JaccPermissionDeclarations;
import org.hibernate.secure.spi.JaccService;
import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.hibernate.service.spi.SessionFactoryServiceRegistry;
import org.hibernate.service.spi.SessionFactoryServiceRegistryFactory;
import org.hibernate.stat.spi.StatisticsImplementor;
import org.hibernate.tool.schema.spi.DelayedDropAction;
import org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator;
import org.hibernate.type.SerializableType;
import org.hibernate.type.Type;
import org.hibernate.type.TypeResolver;
import org.jboss.logging.Logger;
import static org.hibernate.metamodel.internal.JpaMetaModelPopulationSetting.determineJpaMetaModelPopulationSetting;
public final class SessionFactoryImpl implements SessionFactoryImplementor {
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( SessionFactoryImpl.class );
private final String name;
private final String uuid;
private transient boolean isClosed;
private final transient SessionFactoryObserverChain observer = new SessionFactoryObserverChain();
private final transient SessionFactoryOptions sessionFactoryOptions;
private final transient Settings settings;
private final transient Map<String,Object> properties;
private final transient SessionFactoryServiceRegistry serviceRegistry;
private transient JdbcServices jdbcServices;
private final transient SQLFunctionRegistry sqlFunctionRegistry;
private final transient MetamodelImplementor metamodel;
private final transient CriteriaBuilderImpl criteriaBuilder;
private final PersistenceUnitUtil jpaPersistenceUnitUtil;
private final transient CacheImplementor cacheAccess;
private final transient org.hibernate.query.spi.NamedQueryRepository namedQueryRepository;
private final transient QueryPlanCache queryPlanCache;
private final transient CurrentSessionContext currentSessionContext;
private DelayedDropAction delayedDropAction;
private final transient Map<String,IdentifierGenerator> identifierGenerators;
private final transient Map<String, FilterDefinition> filters;
private final transient Map<String, FetchProfile> fetchProfiles;
private final transient TypeHelper typeHelper;
public SessionFactoryImpl(
final BootstrapContext bootstrapContext,
final MetadataImplementor metadata,
SessionFactoryOptions options) {
LOG.debug( "Building session factory" );
this.sessionFactoryOptions = options;
this.settings = new Settings( options, metadata );
this.serviceRegistry = options
.getServiceRegistry()
.getService( SessionFactoryServiceRegistryFactory.class )
.buildServiceRegistry( this, bootstrapContext, options );
prepareEventListeners( metadata );
final CfgXmlAccessService cfgXmlAccessService = serviceRegistry.getService( CfgXmlAccessService.class );
String sfName = settings.getSessionFactoryName();
if ( cfgXmlAccessService.getAggregatedConfig() != null ) {
if ( sfName == null ) {
sfName = cfgXmlAccessService.getAggregatedConfig().getSessionFactoryName();
}
applyCfgXmlValues( cfgXmlAccessService.getAggregatedConfig(), serviceRegistry );
}
this.name = sfName;
this.uuid = options.getUuid();
final JdbcServices jdbcServices = serviceRegistry.getService( JdbcServices.class );
this.properties = new HashMap<>();
this.properties.putAll( serviceRegistry.getService( ConfigurationService.class ).getSettings() );
if ( !properties.containsKey( AvailableSettings.JPA_VALIDATION_FACTORY ) ) {
if ( getSessionFactoryOptions().getValidatorFactoryReference() != null ) {
properties.put(
AvailableSettings.JPA_VALIDATION_FACTORY,
getSessionFactoryOptions().getValidatorFactoryReference()
);
}
}
maskOutSensitiveInformation(this.properties);
logIfEmptyCompositesEnabled( this.properties );
this.sqlFunctionRegistry = new SQLFunctionRegistry( jdbcServices.getJdbcEnvironment().getDialect(), options.getCustomSqlFunctionMap() );
this.cacheAccess = this.serviceRegistry.getService( CacheImplementor.class );
this.criteriaBuilder = new CriteriaBuilderImpl( this );
this.jpaPersistenceUnitUtil = new PersistenceUnitUtilImpl( this );
for ( SessionFactoryObserver sessionFactoryObserver : options.getSessionFactoryObservers() ) {
this.observer.addObserver( sessionFactoryObserver );
}
this.typeHelper = new TypeLocatorImpl( metadata.getTypeConfiguration().getTypeResolver() );
this.filters = new HashMap<>();
this.filters.putAll( metadata.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 );
class IntegratorObserver implements SessionFactoryObserver {
private ArrayList<Integrator> integrators = new ArrayList<>();
@Override
public void sessionFactoryCreated(SessionFactory factory) {
}
@Override
public void sessionFactoryClosed(SessionFactory factory) {
for ( Integrator integrator : integrators ) {
integrator.disintegrate( SessionFactoryImpl.this, SessionFactoryImpl.this.serviceRegistry );
}
integrators.clear();
}
}
final IntegratorObserver integratorObserver = new IntegratorObserver();
this.observer.addObserver( integratorObserver );
try {
for ( Integrator integrator : serviceRegistry.getService( IntegratorService.class ).getIntegrators() ) {
integrator.integrate( metadata, this, this.serviceRegistry );
integratorObserver.integrators.add( integrator );
}
this.identifierGenerators = new HashMap<>();
metadata.getEntityBindings().stream().filter( model -> !model.isInherited() ).forEach( model -> {
IdentifierGenerator generator = model.getIdentifier().createIdentifierGenerator(
metadata.getIdentifierGeneratorFactory(),
jdbcServices.getJdbcEnvironment().getDialect(),
settings.getDefaultCatalogName(),
settings.getDefaultSchemaName(),
(RootClass) model
);
identifierGenerators.put( model.getEntityName(), generator );
} );
LOG.debug( "Instantiated session factory" );
this.metamodel = metadata.getTypeConfiguration().scope( this , bootstrapContext);
( (MetamodelImpl) this.metamodel ).initialize(
metadata,
determineJpaMetaModelPopulationSetting( properties )
);
this.namedQueryRepository = metadata.buildNamedQueryRepository( this );
settings.getMultiTableBulkIdStrategy().prepare(
jdbcServices,
buildLocalConnectionAccess(),
metadata,
sessionFactoryOptions
);
SchemaManagementToolCoordinator.process(
metadata,
serviceRegistry,
properties,
action -> SessionFactoryImpl.this.delayedDropAction = action
);
currentSessionContext = buildCurrentSessionContext();
if ( settings.isNamedQueryStartupCheckingEnabled() ) {
final Map<String, HibernateException> errors = checkNamedQueries();
if ( !errors.isEmpty() ) {
StringBuilder failingQueries = new StringBuilder( "Errors in named queries: " );
String separator = System.lineSeparator();
for ( Map.Entry<String, HibernateException> entry : errors.entrySet() ) {
LOG.namedQueryError( entry.getKey(), entry.getValue() );
failingQueries
.append( separator)
.append( entry.getKey() )
.append( " failed because of: " )
.append( entry.getValue() );
}
throw new HibernateException( failingQueries.toString() );
}
}
this.fetchProfiles = new HashMap<>();
for ( org.hibernate.mapping.FetchProfile mappingProfile : metadata.getFetchProfiles() ) {
final FetchProfile fetchProfile = new FetchProfile( mappingProfile.getName() );
for ( org.hibernate.mapping.FetchProfile.Fetch mappingFetch : mappingProfile.getFetches() ) {
final String entityName = metamodel.getImportedClassName( mappingFetch.getEntity() );
final EntityPersister owner = entityName == null
? null
: metamodel.entityPersister( entityName );
if ( owner == null ) {
throw new HibernateException(
"Unable to resolve entity reference [" + mappingFetch.getEntity()
+ "] in fetch profile [" + fetchProfile.getName() + "]"
);
}
Type associationType = owner.getPropertyType( mappingFetch.getAssociation() );
if ( associationType == null || !associationType.isAssociationType() ) {
throw new HibernateException( "Fetch profile [" + fetchProfile.getName() + "] specified an invalid association" );
}
final Fetch.Style fetchStyle = Fetch.Style.parse( mappingFetch.getStyle() );
fetchProfile.addFetch( new Association( owner, mappingFetch.getAssociation() ), fetchStyle );
((Loadable) owner).registerAffectingFetchProfile( fetchProfile.getName() );
}
fetchProfiles.put( fetchProfile.getName(), fetchProfile );
}
this.observer.sessionFactoryCreated( this );
SessionFactoryRegistry.INSTANCE.addSessionFactory(
getUuid(),
name,
settings.isSessionFactoryNameAlsoJndiName(),
this,
serviceRegistry.getService( JndiService.class )
);
}
catch (Exception e) {
for ( Integrator integrator : serviceRegistry.getService( IntegratorService.class ).getIntegrators() ) {
integrator.disintegrate( this, serviceRegistry );
integratorObserver.integrators.remove( integrator );
}
close();
throw e;
}
}
private void prepareEventListeners(MetadataImplementor metadata) {
final EventListenerRegistry eventListenerRegistry = serviceRegistry.getService( EventListenerRegistry.class );
final ConfigurationService cfgService = serviceRegistry.getService( ConfigurationService.class );
final ClassLoaderService classLoaderService = serviceRegistry.getService( ClassLoaderService.class );
eventListenerRegistry.prepare( metadata );
for ( Map.Entry entry : ( (Map<?, ?>) cfgService.getSettings() ).entrySet() ) {
if ( !String.class.isInstance( entry.getKey() ) ) {
continue;
}
final String propertyName = (String) entry.getKey();
if ( !propertyName.startsWith( org.hibernate.jpa.AvailableSettings.EVENT_LISTENER_PREFIX ) ) {
continue;
}
final String eventTypeName = propertyName.substring(
org.hibernate.jpa.AvailableSettings.EVENT_LISTENER_PREFIX.length() + 1
);
final EventType eventType = EventType.resolveEventTypeByName( eventTypeName );
final EventListenerGroup eventListenerGroup = eventListenerRegistry.getEventListenerGroup( eventType );
for ( String listenerImpl : ( (String) entry.getValue() ).split( " ," ) ) {
eventListenerGroup.appendListener( instantiate( listenerImpl, classLoaderService ) );
}
}
}
private Object instantiate(String listenerImpl, ClassLoaderService classLoaderService) {
try {
return classLoaderService.classForName( listenerImpl ).newInstance();
}
catch (Exception e) {
throw new HibernateException( "Could not instantiate requested listener [" + listenerImpl + "]", e );
}
}
private void applyCfgXmlValues(LoadedConfig aggregatedConfig, SessionFactoryServiceRegistry serviceRegistry) {
final JaccService jaccService = serviceRegistry.getService( JaccService.class );
if ( jaccService.getContextId() != null ) {
final JaccPermissionDeclarations permissions = aggregatedConfig.getJaccPermissions( jaccService.getContextId() );
if ( permissions != null ) {
for ( GrantedPermission grantedPermission : permissions.getPermissionDeclarations() ) {
jaccService.addPermission( grantedPermission );
}
}
}
if ( aggregatedConfig.getEventListenerMap() != null ) {
final ClassLoaderService cls = serviceRegistry.getService( ClassLoaderService.class );
final EventListenerRegistry eventListenerRegistry = serviceRegistry.getService( EventListenerRegistry.class );
for ( Map.Entry<EventType, Set<String>> entry : aggregatedConfig.getEventListenerMap().entrySet() ) {
final EventListenerGroup group = eventListenerRegistry.getEventListenerGroup( entry.getKey() );
for ( String listenerClassName : entry.getValue() ) {
try {
group.appendListener( cls.classForName( listenerClassName ).newInstance() );
}
catch (Exception e) {
throw new ConfigurationException( "Unable to instantiate event listener class : " + listenerClassName, e );
}
}
}
}
}
private JdbcConnectionAccess buildLocalConnectionAccess() {
return new JdbcConnectionAccess() {
@Override
public Connection obtainConnection() throws SQLException {
return !settings.getMultiTenancyStrategy().requiresMultiTenantConnectionProvider()
? serviceRegistry.getService( ConnectionProvider.class ).getConnection()
: serviceRegistry.getService( MultiTenantConnectionProvider.class ).getAnyConnection();
}
@Override
public void releaseConnection(Connection connection) throws SQLException {
if ( !settings.getMultiTenancyStrategy().requiresMultiTenantConnectionProvider() ) {
serviceRegistry.getService( ConnectionProvider.class ).closeConnection( connection );
}
else {
serviceRegistry.getService( MultiTenantConnectionProvider.class ).releaseAnyConnection( connection );
}
}
@Override
public boolean supportsAggressiveRelease() {
return false;
}
};
}
public Session openSession() throws HibernateException {
return withOptions().openSession();
}
public Session openTemporarySession() throws HibernateException {
return withOptions()
.autoClose( false )
.flushMode( FlushMode.MANUAL )
.connectionHandlingMode( PhysicalConnectionHandlingMode.DELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENT )
.openSession();
}
public Session getCurrentSession() throws HibernateException {
if ( currentSessionContext == null ) {
throw new HibernateException( "No CurrentSessionContext configured!" );
}
return currentSessionContext.currentSession();
}
@Override
public SessionBuilderImplementor 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 );
}
@Override
public Map<String, Object> getProperties() {
validateNotClosed();
return properties;
}
protected void validateNotClosed() {
if ( isClosed ) {
throw new IllegalStateException( "EntityManagerFactory is closed" );
}
}
@Override
public String getUuid() {
return uuid;
}
@Override
public String getName() {
return name;
}
@Override
public JdbcServices getJdbcServices() {
if ( jdbcServices == null ) {
jdbcServices = getServiceRegistry().getService( JdbcServices.class );
}
return jdbcServices;
}
public IdentifierGeneratorFactory getIdentifierGeneratorFactory() {
return null;
}
@Deprecated
public TypeResolver getTypeResolver() {
return metamodel.getTypeConfiguration().getTypeResolver();
}
public QueryPlanCache getQueryPlanCache() {
return queryPlanCache;
}
private Map<String,HibernateException> checkNamedQueries() throws HibernateException {
return namedQueryRepository.checkNamedQueries( queryPlanCache );
}
@Override
public DeserializationResolver getDeserializationResolver() {
return new DeserializationResolver() {
@Override
public SessionFactoryImplementor resolve() {
return (SessionFactoryImplementor) SessionFactoryRegistry.INSTANCE.findSessionFactory(
uuid,
name
);
}
};
}
@SuppressWarnings("deprecation")
public Settings getSettings() {
return settings;
}
@Override
public <T> List<EntityGraph<? super T>> findEntityGraphsByType(Class<T> entityClass) {
return getMetamodel().findEntityGraphsByType( entityClass );
}
private transient SynchronizationType synchronizationType;
private transient PersistenceContextType persistenceContextType;
@Override
public Session createEntityManager() {
validateNotClosed();
return buildEntityManager( SynchronizationType.SYNCHRONIZED, Collections.emptyMap() );
}
private Session buildEntityManager(SynchronizationType synchronizationType, Map map) {
assert !isClosed;
SessionBuilderImplementor builder = withOptions();
if ( synchronizationType == SynchronizationType.SYNCHRONIZED ) {
builder.autoJoinTransactions( true );
}
else {
builder.autoJoinTransactions( false );
}
final Session session = builder.openSession();
if ( map != null ) {
map.keySet().forEach( key -> {
if ( key instanceof String ) {
session.setProperty( (String) key, map.get( key ) );
}
} );
}
return session;
}
@Override
public Session createEntityManager(Map map) {
validateNotClosed();
return buildEntityManager( SynchronizationType.SYNCHRONIZED, map );
}
@Override
public Session createEntityManager(SynchronizationType synchronizationType) {
validateNotClosed();
errorIfResourceLocalDueToExplicitSynchronizationType();
return buildEntityManager( synchronizationType, Collections.emptyMap() );
}
private void errorIfResourceLocalDueToExplicitSynchronizationType() {
if ( !getServiceRegistry().getService( TransactionCoordinatorBuilder.class ).isJta() ) {
throw new IllegalStateException(
"Illegal attempt to specify a SynchronizationType when building an EntityManager from a " +
"EntityManagerFactory defined as RESOURCE_LOCAL (as opposed to JTA)"
);
}
}
@Override
public Session createEntityManager(SynchronizationType synchronizationType, Map map) {
validateNotClosed();
errorIfResourceLocalDueToExplicitSynchronizationType();
return buildEntityManager( synchronizationType, map );
}
@Override
public CriteriaBuilder getCriteriaBuilder() {
validateNotClosed();
return criteriaBuilder;
}
@Override
public MetamodelImplementor getMetamodel() {
validateNotClosed();
return metamodel;
}
@Override
public boolean isOpen() {
return !isClosed;
}
@Override
public EntityGraph findEntityGraphByName(String name) {
return getMetamodel().findEntityGraphByName( name );
}
@Override
public SessionFactoryOptions getSessionFactoryOptions() {
return sessionFactoryOptions;
}
public Interceptor getInterceptor() {
return sessionFactoryOptions.getInterceptor();
}
@Override
public Reference getReference() {
LOG.debug( "Returning a Reference to the SessionFactory" );
return new Reference(
SessionFactoryImpl.class.getName(),
new StringRefAddr("uuid", getUuid()),
SessionFactoryRegistry.ObjectFactoryImpl.class.getName(),
null
);
}
@Override
public org.hibernate.query.spi.NamedQueryRepository getNamedQueryRepository() {
return namedQueryRepository;
}
public Type getIdentifierType(String className) throws MappingException {
return getMetamodel().entityPersister( className ).getIdentifierType();
}
public String getIdentifierPropertyName(String className) throws MappingException {
return getMetamodel().entityPersister( className ).getIdentifierPropertyName();
}
public Type[] getReturnTypes(String queryString) throws HibernateException {
final ReturnMetadata metadata = queryPlanCache.getHQLQueryPlan( queryString, false, Collections.EMPTY_MAP )
.getReturnMetadata();
return metadata == null ? null : metadata.getReturnTypes();
}
public String[] getReturnAliases(String queryString) throws HibernateException {
final ReturnMetadata metadata = queryPlanCache.getHQLQueryPlan( queryString, false, Collections.EMPTY_MAP )
.getReturnMetadata();
return metadata == null ? null : metadata.getReturnAliases();
}
public ClassMetadata getClassMetadata(Class persistentClass) throws HibernateException {
return getClassMetadata( persistentClass.getName() );
}
public CollectionMetadata getCollectionMetadata(String roleName) throws HibernateException {
return (CollectionMetadata) getMetamodel().collectionPersister( roleName );
}
public ClassMetadata getClassMetadata(String entityName) throws HibernateException {
return (ClassMetadata) getMetamodel().entityPersister( entityName );
}
@Override
public Map<String,ClassMetadata> getAllClassMetadata() throws HibernateException {
throw new UnsupportedOperationException( "org.hibernate.SessionFactory.getAllClassMetadata is no longer supported" );
}
public Map getAllCollectionMetadata() throws HibernateException {
throw new UnsupportedOperationException( "org.hibernate.SessionFactory.getAllCollectionMetadata is no longer supported" );
}
public Type getReferencedPropertyType(String className, String propertyName)
throws MappingException {
return getMetamodel().entityPersister( className ).getPropertyType( propertyName );
}
public void close() throws HibernateException {
Environment.getBytecodeProvider().resetCaches();
if ( isClosed ) {
if ( getSessionFactoryOptions().getJpaCompliance().isJpaClosedComplianceEnabled() ) {
throw new IllegalStateException( "EntityManagerFactory is already closed" );
}
LOG.trace( "Already closed" );
return;
}
LOG.closing();
observer.sessionFactoryClosing( this );
isClosed = true;
settings.getMultiTableBulkIdStrategy().release( serviceRegistry.getService( JdbcServices.class ), buildLocalConnectionAccess() );
if ( cacheAccess != null ) {
cacheAccess.close();
}
if ( metamodel != null ) {
metamodel.close();
}
if ( queryPlanCache != null ) {
queryPlanCache.cleanup();
}
if ( delayedDropAction != null ) {
delayedDropAction.perform( serviceRegistry );
}
SessionFactoryRegistry.INSTANCE.removeSessionFactory(
getUuid(),
name,
settings.isSessionFactoryNameAlsoJndiName(),
serviceRegistry.getService( JndiService.class )
);
observer.sessionFactoryClosed( this );
serviceRegistry.destroy();
}
public CacheImplementor getCache() {
validateNotClosed();
return cacheAccess;
}
@Override
public PersistenceUnitUtil getPersistenceUnitUtil() {
validateNotClosed();
return jpaPersistenceUnitUtil;
}
@Override
public void addNamedQuery(String name, Query query) {
validateNotClosed();
try {
final ProcedureCall unwrapped = query.unwrap( ProcedureCall.class );
if ( unwrapped != null ) {
addNamedStoredProcedureQuery( name, unwrapped );
return;
}
}
catch ( PersistenceException ignore ) {
}
try {
org.hibernate.query.Query hibernateQuery = query.unwrap( org.hibernate.query.Query.class );
if ( hibernateQuery != null ) {
if ( org.hibernate.query.NativeQuery.class.isInstance( hibernateQuery ) ) {
getNamedQueryRepository().registerNamedSQLQueryDefinition(
name,
extractSqlQueryDefinition( (org.hibernate.query.NativeQuery) hibernateQuery, name )
);
}
else {
getNamedQueryRepository().registerNamedQueryDefinition(
name,
extractHqlQueryDefinition( hibernateQuery, name )
);
}
return;
}
}
catch ( PersistenceException ignore ) {
}
throw new PersistenceException(
String.format(
"Unsure how to how to properly unwrap given Query [%s] as basis for named query",
query
)
);
}
private void addNamedStoredProcedureQuery(String name, ProcedureCall procedureCall) {
getNamedQueryRepository().registerNamedProcedureCallMemento(
name,
procedureCall.extractMemento( procedureCall.getHints() )
);
}
private NamedSQLQueryDefinition (org.hibernate.query.NativeQuery nativeSqlQuery, String name) {
final NamedSQLQueryDefinitionBuilder builder = new NamedSQLQueryDefinitionBuilder( name );
fillInNamedQueryBuilder( builder, nativeSqlQuery );
builder.setCallable( nativeSqlQuery.isCallable() )
.setQuerySpaces( nativeSqlQuery.getSynchronizedQuerySpaces() )
.setQueryReturns( nativeSqlQuery.getQueryReturns() );
return builder.createNamedQueryDefinition();
}
private NamedQueryDefinition (org.hibernate.query.Query hqlQuery, String name) {
final NamedQueryDefinitionBuilder builder = new NamedQueryDefinitionBuilder( name );
fillInNamedQueryBuilder( builder, hqlQuery );
builder.setLockOptions( hqlQuery.getLockOptions().makeCopy() );
return builder.createNamedQueryDefinition();
}
private void fillInNamedQueryBuilder(NamedQueryDefinitionBuilder builder, org.hibernate.query.Query query) {
builder.setQuery( query.getQueryString() )
.setComment( query.getComment() )
.setCacheable( query.isCacheable() )
.setCacheRegion( query.getCacheRegion() )
.setCacheMode( query.getCacheMode() )
.setReadOnly( query.isReadOnly() )
.setFlushMode( query.getHibernateFlushMode() );
if ( query.getQueryOptions().getFirstRow() != null ) {
builder.setFirstResult( query.getQueryOptions().getFirstRow() );
}
if ( query.getQueryOptions().getMaxRows() != null ) {
builder.setMaxResults( query.getQueryOptions().getMaxRows() );
}
if ( query.getQueryOptions().getTimeout() != null ) {
builder.setTimeout( query.getQueryOptions().getTimeout() );
}
if ( query.getQueryOptions().getFetchSize() != null ) {
builder.setFetchSize( query.getQueryOptions().getFetchSize() );
}
}
@Override
public <T> T unwrap(Class<T> type) {
if ( type.isAssignableFrom( SessionFactory.class ) ) {
return type.cast( this );
}
if ( type.isAssignableFrom( SessionFactoryImplementor.class ) ) {
return type.cast( this );
}
if ( type.isAssignableFrom( SessionFactoryImpl.class ) ) {
return type.cast( this );
}
if ( type.isAssignableFrom( EntityManagerFactory.class ) ) {
return type.cast( this );
}
throw new PersistenceException( "Hibernate cannot unwrap EntityManagerFactory as '" + type.getName() + "'" );
}
@Override
public <T> void addNamedEntityGraph(String graphName, EntityGraph<T> entityGraph) {
getMetamodel().addNamedEntityGraph( graphName, entityGraph );
}
public boolean isClosed() {
return isClosed;
}
private transient StatisticsImplementor statistics;
public StatisticsImplementor getStatistics() {
if ( statistics == null ) {
statistics = serviceRegistry.getService( StatisticsImplementor.class );
}
return statistics;
}
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 boolean canAccessTransactionManager() {
try {
return serviceRegistry.getService( JtaPlatform.class ).retrieveTransactionManager() != null;
}
catch (Exception e) {
return false;
}
}
private CurrentSessionContext buildCurrentSessionContext() {
String impl = (String) properties.get( Environment.CURRENT_SESSION_CONTEXT_CLASS );
if ( impl == null ) {
if ( canAccessTransactionManager() ) {
impl = "jta";
}
else {
return null;
}
}
if ( "jta".equals( impl ) ) {
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;
}
}
}
@Override
public ServiceRegistryImplementor getServiceRegistry() {
return serviceRegistry;
}
@Override
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;
}
@Override
public Type resolveParameterBindType(Object bindValue) {
if ( bindValue == null ) {
return null;
}
return resolveParameterBindType( HibernateProxyHelper.getClassWithoutInitializingProxy( bindValue ) );
}
@Override
public Type resolveParameterBindType(Class clazz){
String typename = clazz.getName();
Type type = getTypeResolver().heuristicType( typename );
boolean serializable = type != null && type instanceof SerializableType;
if ( type == null || serializable ) {
try {
getMetamodel().entityPersister( clazz.getName() );
}
catch (MappingException me) {
if ( serializable ) {
return type;
}
else {
throw new HibernateException( "Could not determine a type for class: " + typename );
}
}
return getTypeHelper().entity( clazz );
}
else {
return type;
}
}
public static Interceptor configuredInterceptor(Interceptor interceptor, SessionFactoryOptions options) {
if ( interceptor != null && interceptor != EmptyInterceptor.INSTANCE ) {
return interceptor;
}
if ( options.getInterceptor() != null && options.getInterceptor() != EmptyInterceptor.INSTANCE ) {
return options.getInterceptor();
}
if ( options.getStatelessInterceptorImplementor() != null && options.getStatelessInterceptorImplementorSupplier() != null ) {
throw new HibernateException(
"A session scoped interceptor class or supplier are allowed, but not both!" );
}
else if ( options.getStatelessInterceptorImplementor() != null ) {
try {
return options.getStatelessInterceptorImplementor().newInstance();
}
catch (InstantiationException | IllegalAccessException e) {
throw new HibernateException( "Could not supply session-scoped SessionFactory Interceptor", e );
}
}
else if ( options.getStatelessInterceptorImplementorSupplier() != null ) {
return options.getStatelessInterceptorImplementorSupplier().get();
}
return null;
}
static class SessionBuilderImpl<T extends SessionBuilder> implements SessionBuilderImplementor<T>, SessionCreationOptions {
private static final Logger log = CoreLogging.logger( SessionBuilderImpl.class );
private final SessionFactoryImpl sessionFactory;
private SessionOwner sessionOwner;
private Interceptor interceptor;
private StatementInspector statementInspector;
private Connection connection;
private PhysicalConnectionHandlingMode connectionHandlingMode;
private boolean autoJoinTransactions = true;
private FlushMode flushMode;
private boolean autoClose;
private boolean autoClear;
private String tenantIdentifier;
private TimeZone jdbcTimeZone;
private boolean queryParametersValidationEnabled;
private List<SessionEventListener> listeners;
private SessionOwnerBehavior sessionOwnerBehavior = SessionOwnerBehavior.LEGACY_NATIVE;
private PersistenceUnitTransactionType persistenceUnitTransactionType;
SessionBuilderImpl(SessionFactoryImpl sessionFactory) {
this.sessionFactory = sessionFactory;
this.sessionOwner = null;
this.statementInspector = sessionFactory.getSessionFactoryOptions().getStatementInspector();
this.connectionHandlingMode = sessionFactory.getSessionFactoryOptions().getPhysicalConnectionHandlingMode();
this.autoClose = sessionFactory.getSessionFactoryOptions().isAutoCloseSessionEnabled();
this.flushMode = sessionFactory.getSessionFactoryOptions().isFlushBeforeCompletionEnabled()
? FlushMode.AUTO
: FlushMode.MANUAL;
if ( sessionFactory.getCurrentTenantIdentifierResolver() != null ) {
tenantIdentifier = sessionFactory.getCurrentTenantIdentifierResolver().resolveCurrentTenantIdentifier();
}
this.jdbcTimeZone = sessionFactory.getSessionFactoryOptions().getJdbcTimeZone();
listeners = sessionFactory.getSessionFactoryOptions().getBaselineSessionEventsListenerBuilder().buildBaselineList();
queryParametersValidationEnabled = sessionFactory.getSessionFactoryOptions().isQueryParametersValidationEnabled();
}
@Override
public SessionOwner getSessionOwner() {
return sessionOwner;
}
@Override
public ExceptionMapper getExceptionMapper() {
if ( sessionOwner != null ) {
return sessionOwner.getExceptionMapper();
}
else {
return sessionOwnerBehavior == SessionOwnerBehavior.LEGACY_JPA
? ExceptionMapperLegacyJpaImpl.INSTANCE
: null;
}
}
@Override
public AfterCompletionAction getAfterCompletionAction() {
if ( sessionOwner != null ) {
return sessionOwner.getAfterCompletionAction();
}
return sessionOwnerBehavior == SessionOwnerBehavior.LEGACY_JPA
? AfterCompletionActionLegacyJpaImpl.INSTANCE
: null;
}
@Override
public ManagedFlushChecker getManagedFlushChecker() {
if ( sessionOwner != null ) {
return sessionOwner.getManagedFlushChecker();
}
return sessionOwnerBehavior == SessionOwnerBehavior.LEGACY_JPA
? ManagedFlushCheckerLegacyJpaImpl.INSTANCE
: null;
}
@Override
public boolean isQueryParametersValidationEnabled() {
return this.queryParametersValidationEnabled;
}
@Override
public boolean shouldAutoJoinTransactions() {
return autoJoinTransactions;
}
@Override
public FlushMode getInitialSessionFlushMode() {
return flushMode;
}
@Override
public boolean shouldAutoClose() {
return autoClose;
}
@Override
public boolean shouldAutoClear() {
return autoClear;
}
@Override
public Connection getConnection() {
return connection;
}
@Override
public Interceptor getInterceptor() {
return configuredInterceptor( interceptor, sessionFactory.getSessionFactoryOptions() );
}
@Override
public StatementInspector getStatementInspector() {
return statementInspector;
}
@Override
public PhysicalConnectionHandlingMode getPhysicalConnectionHandlingMode() {
return connectionHandlingMode;
}
@Override
public String getTenantIdentifier() {
return tenantIdentifier;
}
@Override
public TimeZone getJdbcTimeZone() {
return jdbcTimeZone;
}
@Override
public Session openSession() {
log.tracef( "Opening Hibernate Session. tenant=%s, owner=%s", tenantIdentifier, sessionOwner );
final SessionImpl session = new SessionImpl( sessionFactory, this );
for ( SessionEventListener listener : listeners ) {
session.getEventListenerManager().addListener( listener );
}
return session;
}
@Override
@SuppressWarnings("unchecked")
public T owner(SessionOwner sessionOwner) {
this.sessionOwner = sessionOwner;
return (T) this;
}
@Override
@SuppressWarnings("unchecked")
public T interceptor(Interceptor interceptor) {
this.interceptor = interceptor;
return (T) this;
}
@Override
@SuppressWarnings("unchecked")
public T noInterceptor() {
this.interceptor = EmptyInterceptor.INSTANCE;
return (T) this;
}
@Override
@SuppressWarnings("unchecked")
public T statementInspector(StatementInspector statementInspector) {
this.statementInspector = statementInspector;
return (T) this;
}
@Override
@SuppressWarnings("unchecked")
public T connection(Connection connection) {
this.connection = connection;
return (T) this;
}
@Override
@SuppressWarnings("unchecked")
public T connectionReleaseMode(ConnectionReleaseMode connectionReleaseMode) {
final PhysicalConnectionHandlingMode handlingMode = PhysicalConnectionHandlingMode.interpret(
ConnectionAcquisitionMode.AS_NEEDED,
connectionReleaseMode
);
connectionHandlingMode( handlingMode );
return (T) this;
}
@Override
@SuppressWarnings("unchecked")
public T connectionHandlingMode(PhysicalConnectionHandlingMode connectionHandlingMode) {
this.connectionHandlingMode = connectionHandlingMode;
return (T) this;
}
@Override
@SuppressWarnings("unchecked")
public T autoJoinTransactions(boolean autoJoinTransactions) {
this.autoJoinTransactions = autoJoinTransactions;
return (T) this;
}
@Override
@SuppressWarnings("unchecked")
public T autoClose(boolean autoClose) {
this.autoClose = autoClose;
return (T) this;
}
@Override
@SuppressWarnings("unchecked")
public T autoClear(boolean autoClear) {
this.autoClear = autoClear;
return (T) this;
}
@Override
@SuppressWarnings("unchecked")
public T flushMode(FlushMode flushMode) {
this.flushMode = flushMode;
return (T) this;
}
@Override
@SuppressWarnings("unchecked")
public T tenantIdentifier(String tenantIdentifier) {
this.tenantIdentifier = tenantIdentifier;
return (T) this;
}
@Override
@SuppressWarnings("unchecked")
public T eventListeners(SessionEventListener... listeners) {
Collections.addAll( this.listeners, listeners );
return (T) this;
}
@Override
@SuppressWarnings("unchecked")
public T clearEventListeners() {
listeners.clear();
return (T) this;
}
@Override
public T jdbcTimeZone(TimeZone timeZone) {
jdbcTimeZone = timeZone;
return (T) this;
}
@Override
public T setQueryParameterValidation(boolean enabled) {
queryParametersValidationEnabled = enabled;
return (T) this;
}
}
public static class StatelessSessionBuilderImpl implements StatelessSessionBuilder, SessionCreationOptions {
private final SessionFactoryImpl sessionFactory;
private Connection connection;
private String tenantIdentifier;
private boolean queryParametersValidationEnabled;
public StatelessSessionBuilderImpl(SessionFactoryImpl sessionFactory) {
this.sessionFactory = sessionFactory;
if ( sessionFactory.getCurrentTenantIdentifierResolver() != null ) {
tenantIdentifier = sessionFactory.getCurrentTenantIdentifierResolver().resolveCurrentTenantIdentifier();
}
queryParametersValidationEnabled = sessionFactory.getSessionFactoryOptions().isQueryParametersValidationEnabled();
}
@Override
public StatelessSession openStatelessSession() {
return new StatelessSessionImpl( sessionFactory, this );
}
@Override
public StatelessSessionBuilder connection(Connection connection) {
this.connection = connection;
return this;
}
@Override
public StatelessSessionBuilder tenantIdentifier(String tenantIdentifier) {
this.tenantIdentifier = tenantIdentifier;
return this;
}
@Override
public boolean shouldAutoJoinTransactions() {
return true;
}
@Override
public FlushMode getInitialSessionFlushMode() {
return FlushMode.ALWAYS;
}
@Override
public boolean shouldAutoClose() {
return false;
}
@Override
public boolean shouldAutoClear() {
return false;
}
@Override
public Connection getConnection() {
return connection;
}
@Override
public Interceptor getInterceptor() {
return configuredInterceptor( EmptyInterceptor.INSTANCE, sessionFactory.getSessionFactoryOptions() );
}
@Override
public StatementInspector getStatementInspector() {
return null;
}
@Override
public PhysicalConnectionHandlingMode getPhysicalConnectionHandlingMode() {
return null;
}
@Override
public String getTenantIdentifier() {
return tenantIdentifier;
}
@Override
public TimeZone getJdbcTimeZone() {
return sessionFactory.getSessionFactoryOptions().getJdbcTimeZone();
}
@Override
public SessionOwner getSessionOwner() {
return null;
}
@Override
public ExceptionMapper getExceptionMapper() {
return null;
}
@Override
public AfterCompletionAction getAfterCompletionAction() {
return null;
}
@Override
public ManagedFlushChecker getManagedFlushChecker() {
return null;
}
@Override
public boolean isQueryParametersValidationEnabled() {
return queryParametersValidationEnabled;
}
@Override
public StatelessSessionBuilder setQueryParameterValidation(boolean enabled) {
queryParametersValidationEnabled = enabled;
return this;
}
}
@Override
public CustomEntityDirtinessStrategy getCustomEntityDirtinessStrategy() {
return getSessionFactoryOptions().getCustomEntityDirtinessStrategy();
}
@Override
public CurrentTenantIdentifierResolver getCurrentTenantIdentifierResolver() {
return getSessionFactoryOptions().getCurrentTenantIdentifierResolver();
}
private void writeObject(ObjectOutputStream out) throws IOException {
LOG.debugf( "Serializing: %s", getUuid() );
out.defaultWriteObject();
LOG.trace( "Serialized" );
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
LOG.trace( "Deserializing" );
in.defaultReadObject();
LOG.debugf( "Deserialized: %s", getUuid() );
}
private Object readResolve() throws InvalidObjectException {
LOG.trace( "Resolving serialized SessionFactory" );
return locateSessionFactoryOnDeserialization( getUuid(), 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;
}
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 + "]" );
}
void serialize(ObjectOutputStream oos) throws IOException {
oos.writeUTF( getUuid() );
oos.writeBoolean( name != null );
if ( name != null ) {
oos.writeUTF( name );
}
}
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 );
}
private void maskOutSensitiveInformation(Map<String, Object> props) {
maskOutIfSet( props, AvailableSettings.JPA_JDBC_USER );
maskOutIfSet( props, AvailableSettings.JPA_JDBC_PASSWORD );
maskOutIfSet( props, AvailableSettings.USER );
maskOutIfSet( props, AvailableSettings.PASS );
}
private void maskOutIfSet(Map<String, Object> props, String setting) {
if ( props.containsKey( setting ) ) {
props.put( setting, "****" );
}
}
private void logIfEmptyCompositesEnabled(Map<String, Object> props ) {
final boolean isEmptyCompositesEnabled = ConfigurationHelper.getBoolean(
AvailableSettings.CREATE_EMPTY_COMPOSITES_ENABLED,
props,
false
);
if ( isEmptyCompositesEnabled ) {
LOG.emptyCompositesEnabled();
}
}
}