diff --git a/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java b/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java index 84af378f5079..592f012d6910 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java @@ -9,6 +9,7 @@ import java.io.IOException; import java.sql.SQLException; import java.sql.SQLWarning; +import java.util.Map; import java.util.Properties; import java.util.ServiceConfigurationError; import java.util.Set; @@ -73,8 +74,20 @@ public interface CoreMessageLogger extends BasicLogger { void callingJoinTransactionOnNonJtaEntityManager(); @LogMessage(level = DEBUG) - @Message(value = "Closing", id = 31) - void closing(); + @Message(value = "Instantiating factory with settings: %s", id = 30) + void instantiatingFactory(Map settings); + + @LogMessage(level = DEBUG) + @Message(value = "Closing factory", id = 31) + void closingFactory(); + + @LogMessage(level = DEBUG) + @Message(value = "Serializing factory: %s", id = 32) + void serializingFactory(String uuid); + + @LogMessage(level = DEBUG) + @Message(value = "Deserialized factory: %s", id = 33) + void deserializedFactory(String uuid); @LogMessage(level = WARN) @Message(value = "Composite-id class does not override equals(): %s", id = 38) diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java index 1199f21fe359..e1948d68b488 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java @@ -25,7 +25,6 @@ import javax.naming.Reference; import javax.naming.StringRefAddr; -import org.hibernate.AssertionFailure; import org.hibernate.CustomEntityDirtinessStrategy; import org.hibernate.EntityNameResolver; import org.hibernate.FlushMode; @@ -90,17 +89,11 @@ import org.hibernate.metamodel.spi.MappingMetamodelImplementor; import org.hibernate.metamodel.spi.RuntimeMetamodelsImplementor; import org.hibernate.metamodel.spi.RuntimeModelCreationContext; -import org.hibernate.procedure.spi.ProcedureCallImplementor; import org.hibernate.proxy.EntityNotFoundDelegate; import org.hibernate.proxy.LazyInitializer; -import org.hibernate.query.BindingContext; -import org.hibernate.query.hql.spi.SqmQueryImplementor; import org.hibernate.query.internal.QueryEngineImpl; -import org.hibernate.query.named.NamedObjectRepository; import org.hibernate.query.spi.QueryEngine; -import org.hibernate.query.spi.QueryImplementor; import org.hibernate.query.sql.internal.SqlTranslationEngineImpl; -import org.hibernate.query.sql.spi.NativeQueryImplementor; import org.hibernate.query.sql.spi.SqlTranslationEngine; import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.function.SqmFunctionRegistry; @@ -167,7 +160,7 @@ * @author Steve Ebersole * @author Chris Cranford */ -public class SessionFactoryImpl implements SessionFactoryImplementor, BindingContext { +public class SessionFactoryImpl implements SessionFactoryImplementor { private static final CoreMessageLogger LOG = CoreLogging.messageLogger( SessionFactoryImpl.class ); private final String name; @@ -182,12 +175,10 @@ public class SessionFactoryImpl implements SessionFactoryImplementor, BindingCon private final transient Map settings; private final transient SessionFactoryServiceRegistry serviceRegistry; - private final transient EventEngine eventEngine;//Needs to be closed! + private final transient EventEngine eventEngine; private final transient JdbcServices jdbcServices; private final transient SqlStringGenerationContext sqlStringGenerationContext; - // todo : org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor too? - private final transient RuntimeMetamodelsImplementor runtimeMetamodels; private final PersistenceUnitUtil jpaPersistenceUnitUtil; private final transient CacheImplementor cacheAccess; @@ -245,7 +236,7 @@ public SessionFactoryImpl( settings = getSettings( options, serviceRegistry ); maskOutSensitiveInformation( settings ); deprecationCheck( settings ); - LOG.debugf( "Instantiating SessionFactory with settings: %s", settings ); + LOG.instantiatingFactory( settings ); sqlStringGenerationContext = createSqlStringGenerationContext( bootMetamodel, options, jdbcServices ); @@ -258,7 +249,6 @@ public SessionFactoryImpl( } filters = new HashMap<>( bootMetamodel.getFilterDefinitions() ); - LOG.debugf( "Session factory constructed with filter configurations : %s", filters ); final FilterDefinition tenantFilter = filters.get( TenantIdBinder.FILTER_NAME ); if ( tenantFilter == null ) { @@ -293,21 +283,26 @@ public SessionFactoryImpl( primeSecondLevelCacheRegions( bootMetamodel ); - // we build this before creating the runtime metamodel + // create the empty runtime metamodels object + final RuntimeMetamodelsImpl runtimeMetamodelsImpl = new RuntimeMetamodelsImpl( typeConfiguration ); + runtimeMetamodels = runtimeMetamodelsImpl; + + // we build this before creating the runtime metamodels // because the persisters need the SqmFunctionRegistry - // to translate SQL formulas ... but, if we fix Dialect + // to translate SQL formulas. But, if we fix Dialect // as I proposed, so that it can contribute functions // to the SqmFunctionRegistry before the QueryEngine is // created, then we can split creation of QueryEngine // and SqmFunctionRegistry, instantiating just the - // registry here, and doing the engine later - queryEngine = new QueryEngineImpl( bootMetamodel, options, this, serviceRegistry, settings, name ); + // registry here, and doing the engine later, and we + // can thus untie this nasty little knot. Alternatively, + // perhaps it's not really appropriate that they use the + // SqmFunctionRegistry for that purpose at all? + queryEngine = new QueryEngineImpl( bootMetamodel, options, runtimeMetamodels, serviceRegistry, settings, name ); final Map fetchProfiles = new HashMap<>(); sqlTranslationEngine = new SqlTranslationEngineImpl( this, typeConfiguration, fetchProfiles ); - // create runtime metamodels (mapping and JPA) - final RuntimeMetamodelsImpl runtimeMetamodelsImpl = new RuntimeMetamodelsImpl(); - runtimeMetamodels = runtimeMetamodelsImpl; + // now actually create the mapping and JPA metamodels final MappingMetamodelImpl mappingMetamodelImpl = new MappingMetamodelImpl( typeConfiguration, serviceRegistry ); runtimeMetamodelsImpl.setMappingMetamodel( mappingMetamodelImpl ); mappingMetamodelImpl.finishInitialization( @@ -359,12 +354,12 @@ public SessionFactoryImpl( close(); } catch (Exception closeException) { - LOG.debug( "Eating error closing the SessionFactory after a failed attempt to start it" ); + LOG.trace( "Eating error closing factory after failed instantiation" ); } throw e; } - LOG.debug( "Instantiated SessionFactory" ); + LOG.debug( "Instantiated factory" ); } private EventMonitor loadEventMonitor() { @@ -481,10 +476,12 @@ private SessionBuilderImpl buildTemporarySessionOpenOptions() { private void primeSecondLevelCacheRegions(MetadataImplementor mappingMetadata) { final Map regionConfigBuilders = new ConcurrentHashMap<>(); - // todo : ultimately this code can be made more efficient when we have a better intrinsic understanding of the hierarchy as a whole + // TODO: ultimately this code can be made more efficient when we have + // a better intrinsic understanding of the hierarchy as a whole for ( PersistentClass bootEntityDescriptor : mappingMetadata.getEntityBindings() ) { - final AccessType accessType = AccessType.fromExternalName( bootEntityDescriptor.getCacheConcurrencyStrategy() ); + final AccessType accessType = + AccessType.fromExternalName( bootEntityDescriptor.getCacheConcurrencyStrategy() ); if ( accessType != null ) { if ( bootEntityDescriptor.isCached() ) { @@ -533,29 +530,25 @@ private void primeSecondLevelCacheRegions(MetadataImplementor mappingMetadata) { } @Override - public SessionImplementor openSession() throws HibernateException { - //The defaultSessionOpenOptions can't be used in some cases; for example when using a TenantIdentifierResolver. - if ( defaultSessionOpenOptions != null ) { - return defaultSessionOpenOptions.openSession(); - } - else { - return withOptions().openSession(); - } + public SessionImplementor openSession() { + // The defaultSessionOpenOptions can't be used in some cases; + // for example when using a TenantIdentifierResolver. + return defaultSessionOpenOptions != null + ? defaultSessionOpenOptions.openSession() + : withOptions().openSession(); } @Override - public SessionImpl openTemporarySession() throws HibernateException { - //The temporarySessionOpenOptions can't be used in some cases; for example when using a TenantIdentifierResolver. - if ( temporarySessionOpenOptions != null ) { - return temporarySessionOpenOptions.openSession(); - } - else { - return buildTemporarySessionOpenOptions().openSession(); - } + public SessionImpl openTemporarySession() { + // The temporarySessionOpenOptions can't be used in some cases; + // for example when using a TenantIdentifierResolver. + return temporarySessionOpenOptions != null + ? temporarySessionOpenOptions.openSession() + : buildTemporarySessionOpenOptions().openSession(); } @Override - public Session getCurrentSession() throws HibernateException { + public Session getCurrentSession() { if ( currentSessionContext == null ) { throw new HibernateException( "No CurrentSessionContext configured" ); } @@ -770,7 +763,7 @@ public Interceptor getInterceptor() { @Override public Reference getReference() { // from javax.naming.Referenceable - LOG.debug( "Returning a Reference to the SessionFactory" ); + LOG.debug( "Returning a Reference to the factory" ); return new Reference( SessionFactoryImpl.class.getName(), new StringRefAddr( "uuid", getUuid() ), @@ -794,7 +787,7 @@ public Reference getReference() { * collector release the memory. */ @Override - public void close() throws HibernateException { + public void close() { synchronized (this) { if ( status != Status.OPEN ) { if ( getSessionFactoryOptions().getJpaCompliance().isJpaClosedComplianceEnabled() ) { @@ -809,7 +802,7 @@ public void close() throws HibernateException { } try { - LOG.closing(); + LOG.closingFactory(); observer.sessionFactoryClosing( this ); // NOTE : the null checks below handle cases where close is called from @@ -874,58 +867,7 @@ public PersistenceUnitTransactionType getTransactionType() { @Override public void addNamedQuery(String name, Query query) { validateNotClosed(); - - // NOTE : we use Query#unwrap here (rather than direct type checking) - // to account for possibly wrapped query implementations - - // first, handle StoredProcedureQuery - final NamedObjectRepository namedObjectRepository = getQueryEngine().getNamedObjectRepository(); - try { - final ProcedureCallImplementor unwrapped = query.unwrap( ProcedureCallImplementor.class ); - if ( unwrapped != null ) { - namedObjectRepository.registerCallableQueryMemento( name, unwrapped.toMemento( name ) ); - return; - } - } - catch ( PersistenceException ignore ) { - // this means 'query' is not a ProcedureCallImplementor - } - - // then try as a native-SQL or JPQL query - try { - final QueryImplementor queryImplementor = query.unwrap( QueryImplementor.class ); - if ( queryImplementor != null ) { - // create and register the proper NamedQueryDefinition... - if ( queryImplementor instanceof NativeQueryImplementor nativeQueryImplementor ) { - namedObjectRepository.registerNativeQueryMemento( - name, - nativeQueryImplementor.toMemento( name ) - ); - - } - else if ( queryImplementor instanceof SqmQueryImplementor sqmQueryImplementor ) { - namedObjectRepository.registerSqmQueryMemento( - name, - sqmQueryImplementor.toMemento( name ) - ); - } - else { - throw new AssertionFailure("unknown QueryImplementor"); - } - return; - } - } - catch ( PersistenceException ignore ) { - // this means 'query' is not a native-SQL or JPQL query - } - - // if we get here, we are unsure how to properly unwrap the incoming query to extract the needed information - throw new PersistenceException( - String.format( - "Unsure how to properly unwrap given Query [%s] as basis for named query", - query - ) - ); + getQueryEngine().getNamedObjectRepository().registerNamedQuery( name, query ); } @Override @@ -1004,7 +946,7 @@ public StatisticsImplementor getStatistics() { return statistics; } - public FilterDefinition getFilterDefinition(String filterName) throws HibernateException { + public FilterDefinition getFilterDefinition(String filterName) { final FilterDefinition filterDefinition = filters.get( filterName ); if ( filterDefinition == null ) { throw new UnknownFilterException( filterName ); @@ -1543,11 +1485,9 @@ public JavaType getTenantIdentifierJavaType() { */ @Serial private void writeObject(ObjectOutputStream out) throws IOException { - if ( LOG.isDebugEnabled() ) { - LOG.debugf( "Serializing: %s", getUuid() ); - } + LOG.serializingFactory( getUuid() ); out.defaultWriteObject(); - LOG.trace( "Serialized" ); + LOG.trace( "Serialized factory" ); } /** @@ -1560,11 +1500,9 @@ private void writeObject(ObjectOutputStream out) throws IOException { */ @Serial private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { - LOG.trace( "Deserializing" ); + LOG.trace( "Deserializing factory" ); in.defaultReadObject(); - if ( LOG.isDebugEnabled() ) { - LOG.debugf( "Deserialized: %s", getUuid() ); - } + LOG.deserializedFactory( getUuid() ); } /** @@ -1580,7 +1518,7 @@ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundE */ @Serial private Object readResolve() throws InvalidObjectException { - LOG.trace( "Resolving serialized SessionFactory" ); + LOG.trace( "Resolving serialized factory" ); return locateSessionFactoryOnDeserialization( getUuid(), name ); } @@ -1588,7 +1526,7 @@ private static SessionFactory locateSessionFactoryOnDeserialization(String uuid, throws InvalidObjectException{ final SessionFactory uuidResult = SessionFactoryRegistry.INSTANCE.getSessionFactory( uuid ); if ( uuidResult != null ) { - LOG.debugf( "Resolved SessionFactory by UUID [%s]", uuid ); + LOG.debug( "Resolved factory by UUID: " + uuid ); return uuidResult; } @@ -1597,12 +1535,12 @@ private static SessionFactory locateSessionFactoryOnDeserialization(String uuid, if ( name != null ) { final SessionFactory namedResult = SessionFactoryRegistry.INSTANCE.getNamedSessionFactory( name ); if ( namedResult != null ) { - LOG.debugf( "Resolved SessionFactory by name [%s]", name ); + LOG.debug( "Resolved factory by name: " + name ); return namedResult; } } - throw new InvalidObjectException( "Could not find a SessionFactory [uuid=" + uuid + ",name=" + name + "]" ); + throw new InvalidObjectException( "No SessionFactory with uuid [" + uuid + "] and name [" + name + "]" ); } /** @@ -1627,7 +1565,7 @@ void serialize(ObjectOutputStream oos) throws IOException { * @throws IOException indicates problems reading back serial data stream */ static SessionFactoryImpl deserialize(ObjectInputStream ois) throws IOException { - LOG.trace( "Deserializing SessionFactory from Session" ); + LOG.trace( "Resolving factory from deserialized session" ); final String uuid = ois.readUTF(); boolean isNamed = ois.readBoolean(); final String name = isNamed ? ois.readUTF() : null; diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/RuntimeMetamodelsImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/RuntimeMetamodelsImpl.java index 36109bb3f962..e0c8a77c6c0a 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/RuntimeMetamodelsImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/RuntimeMetamodelsImpl.java @@ -9,16 +9,24 @@ import org.hibernate.metamodel.spi.MappingMetamodelImplementor; import org.hibernate.metamodel.spi.RuntimeMetamodelsImplementor; import org.hibernate.type.Type; +import org.hibernate.type.spi.TypeConfiguration; /** * @author Steve Ebersole */ public class RuntimeMetamodelsImpl implements RuntimeMetamodelsImplementor { + private final TypeConfiguration typeConfiguration; private JpaMetamodelImplementor jpaMetamodel; private MappingMetamodelImplementor mappingMetamodel; - public RuntimeMetamodelsImpl() { + public RuntimeMetamodelsImpl(TypeConfiguration typeConfiguration) { + this.typeConfiguration = typeConfiguration; + } + + @Override + public TypeConfiguration getTypeConfiguration() { + return typeConfiguration; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/RuntimeMetamodelsImplementor.java b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/RuntimeMetamodelsImplementor.java index e1907ead37f3..defc6338adb2 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/RuntimeMetamodelsImplementor.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/RuntimeMetamodelsImplementor.java @@ -6,6 +6,7 @@ import org.hibernate.metamodel.RuntimeMetamodels; import org.hibernate.metamodel.model.domain.spi.JpaMetamodelImplementor; +import org.hibernate.query.BindingContext; import org.hibernate.type.MappingContext; /** @@ -13,7 +14,7 @@ * * @author Steve Ebersole */ -public interface RuntimeMetamodelsImplementor extends RuntimeMetamodels, MappingContext { +public interface RuntimeMetamodelsImplementor extends RuntimeMetamodels, MappingContext, BindingContext { @Override MappingMetamodelImplementor getMappingMetamodel(); diff --git a/hibernate-core/src/main/java/org/hibernate/query/internal/NamedObjectRepositoryImpl.java b/hibernate-core/src/main/java/org/hibernate/query/internal/NamedObjectRepositoryImpl.java index f32c2211c423..659fe0da13b2 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/internal/NamedObjectRepositoryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/internal/NamedObjectRepositoryImpl.java @@ -8,6 +8,9 @@ import java.util.Map; import java.util.function.Consumer; +import jakarta.persistence.PersistenceException; +import jakarta.persistence.Query; +import org.hibernate.AssertionFailure; import org.hibernate.HibernateException; import org.hibernate.QueryException; import org.hibernate.boot.Metadata; @@ -17,6 +20,10 @@ import org.hibernate.boot.spi.MetadataImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.procedure.spi.NamedCallableQueryMemento; +import org.hibernate.procedure.spi.ProcedureCallImplementor; +import org.hibernate.query.hql.spi.SqmQueryImplementor; +import org.hibernate.query.spi.QueryImplementor; +import org.hibernate.query.sql.spi.NativeQueryImplementor; import org.hibernate.query.sqm.EntityTypeException; import org.hibernate.query.NamedQueryValidationException; import org.hibernate.query.sqm.PathElementException; @@ -76,6 +83,46 @@ public Map> getNamedQueries(Class resultTy return namedQueries; } + @Override + public void registerNamedQuery(String name, Query query) { + // use unwrap() here instead of 'instanceof' because the Query might be wrapped + + // first, handle stored procedures + try { + final var unwrapped = query.unwrap( ProcedureCallImplementor.class ); + if ( unwrapped != null ) { + registerCallableQueryMemento( name, unwrapped.toMemento( name ) ); + return; + } + } + catch ( PersistenceException ignore ) { + // this means 'query' is not a ProcedureCallImplementor + } + + // then try as a native SQL or JPQL query + try { + final var queryImplementor = query.unwrap( QueryImplementor.class ); + if ( queryImplementor != null ) { + if ( queryImplementor instanceof NativeQueryImplementor nativeQueryImplementor ) { + registerNativeQueryMemento( name, nativeQueryImplementor.toMemento( name ) ); + + } + else if ( queryImplementor instanceof SqmQueryImplementor sqmQueryImplementor ) { + registerSqmQueryMemento( name, sqmQueryImplementor.toMemento( name ) ); + } + else { + throw new AssertionFailure( "unknown QueryImplementor" ); + } + return; + } + } + catch ( PersistenceException ignore ) { + // this means 'query' is not a native SQL or JPQL query + } + + throw new PersistenceException( "Could not register named query: " + name ); + } + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Named SQM Memento diff --git a/hibernate-core/src/main/java/org/hibernate/query/named/NamedObjectRepository.java b/hibernate-core/src/main/java/org/hibernate/query/named/NamedObjectRepository.java index 80294a4b2e0d..8712a7ae00f4 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/named/NamedObjectRepository.java +++ b/hibernate-core/src/main/java/org/hibernate/query/named/NamedObjectRepository.java @@ -7,6 +7,7 @@ import java.util.Map; import java.util.function.Consumer; +import jakarta.persistence.Query; import org.hibernate.HibernateException; import org.hibernate.Incubating; import org.hibernate.boot.Metadata; @@ -33,6 +34,8 @@ public interface NamedObjectRepository { Map> getNamedQueries(Class resultType); + void registerNamedQuery(String name, Query query); + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Named SQM Memento