diff --git a/hibernate-core/src/main/java/org/hibernate/SessionFactory.java b/hibernate-core/src/main/java/org/hibernate/SessionFactory.java index c2d53cdb9d17..9f671608bcc9 100644 --- a/hibernate-core/src/main/java/org/hibernate/SessionFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/SessionFactory.java @@ -9,6 +9,8 @@ import jakarta.persistence.EntityManagerFactory; import jakarta.persistence.FindOption; import jakarta.persistence.SynchronizationType; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.TypedQueryReference; import org.hibernate.boot.spi.SessionFactoryOptions; import org.hibernate.engine.spi.FilterDefinition; import org.hibernate.engine.spi.SessionFactoryImplementor; @@ -557,6 +559,27 @@ default boolean containsFetchProfileDefinition(String name) { return getDefinedFilterNames().contains( name ); } + /** + * Add or override the definition of a named query, returning + * a {@linkplain TypedQueryReference reference} to the query. + * Settings such as first and max results, hints, flush mode, + * cache mode, timeout, and lock options are preserved as part + * of the named query definition. Any arguments bound to query + * parameters are discarded. + * + * @param name the name to be assigned to the query + * @param query the query, including first and max results, hints, + * flush mode, cache mode, timeout, and lock options + * + * @see #addNamedQuery(String, jakarta.persistence.Query) + * @see org.hibernate.query.QueryProducer#createQuery(TypedQueryReference) + * @see org.hibernate.query.QueryProducer#createSelectionQuery(String, Class) + * + * @since 7.0 + */ + @Incubating + TypedQueryReference addNamedQuery(String name, TypedQuery query); + /** * The name assigned to this {@code SessionFactory}, if any. *
    diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryDelegatingImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryDelegatingImpl.java index aa3764063c2d..274015be5b4e 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryDelegatingImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryDelegatingImpl.java @@ -19,6 +19,7 @@ import jakarta.persistence.PersistenceUnitUtil; import jakarta.persistence.Query; import jakarta.persistence.SynchronizationType; +import jakarta.persistence.TypedQuery; import jakarta.persistence.TypedQueryReference; import org.hibernate.CustomEntityDirtinessStrategy; @@ -416,4 +417,9 @@ public ManagedBeanRegistry getManagedBeanRegistry() { public EventListenerRegistry getEventListenerRegistry() { return delegate.getEventListenerRegistry(); } + + @Override + public TypedQueryReference addNamedQuery(String name, TypedQuery query) { + return delegate.addNamedQuery( name, query ); + } } 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 73093ad237b4..a14e14354d37 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java @@ -25,6 +25,7 @@ import javax.naming.Reference; import javax.naming.StringRefAddr; +import jakarta.persistence.TypedQuery; import org.hibernate.CustomEntityDirtinessStrategy; import org.hibernate.EntityNameResolver; import org.hibernate.FlushMode; @@ -96,6 +97,7 @@ import org.hibernate.proxy.EntityNotFoundDelegate; import org.hibernate.proxy.LazyInitializer; import org.hibernate.query.internal.QueryEngineImpl; +import org.hibernate.query.named.NamedObjectRepository; import org.hibernate.query.spi.QueryEngine; import org.hibernate.query.sql.internal.SqlTranslationEngineImpl; import org.hibernate.query.sql.spi.SqlTranslationEngine; @@ -871,10 +873,19 @@ public PersistenceUnitTransactionType getTransactionType() { } + private NamedObjectRepository getNamedObjectRepository() { + validateNotClosed(); + return getQueryEngine().getNamedObjectRepository(); + } + @Override public void addNamedQuery(String name, Query query) { - validateNotClosed(); - getQueryEngine().getNamedObjectRepository().registerNamedQuery( name, query ); + getNamedObjectRepository().registerNamedQuery( name, query ); + } + + @Override + public TypedQueryReference addNamedQuery(String name, TypedQuery query) { + return getNamedObjectRepository().registerNamedQuery( name, query ); } @Override 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 a38d77b03723..4eb252765d24 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 @@ -10,6 +10,7 @@ import jakarta.persistence.PersistenceException; import jakarta.persistence.Query; +import jakarta.persistence.TypedQuery; import org.hibernate.AssertionFailure; import org.hibernate.HibernateException; import org.hibernate.QueryException; @@ -123,6 +124,23 @@ else if ( queryImplementor instanceof SqmQueryImplementor sqmQueryImplementor throw new PersistenceException( "Could not register named query: " + name ); } + @Override + public TypedQueryReference registerNamedQuery(String name, TypedQuery query) { + if ( query instanceof NativeQueryImplementor nativeQueryImplementor ) { + final NamedNativeQueryMemento memento = nativeQueryImplementor.toMemento( name ); + registerNativeQueryMemento( name, memento ); + return memento; + } + else if ( query instanceof SqmQueryImplementor sqmQueryImplementor ) { + final NamedSqmQueryMemento memento = sqmQueryImplementor.toMemento( name ); + registerSqmQueryMemento( name, memento ); + return memento; + } + else { + throw new IllegalArgumentException( "unknown implementation of TypedQuery" ); + } + } + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // 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 72839c59ab58..8416e45a5b51 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 @@ -8,6 +8,7 @@ import java.util.function.Consumer; import jakarta.persistence.Query; +import jakarta.persistence.TypedQuery; import org.hibernate.HibernateException; import org.hibernate.Incubating; import org.hibernate.boot.Metadata; @@ -36,6 +37,8 @@ public interface NamedObjectRepository { void registerNamedQuery(String name, Query query); + TypedQueryReference registerNamedQuery(String name, TypedQuery query); + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Named SQM Memento diff --git a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java index ebd6636fec4d..e82bd89b4c2e 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java @@ -523,7 +523,7 @@ public Class getResultType() { } @Override - public NamedNativeQueryMemento toMemento(String name) { + public NamedNativeQueryMemento toMemento(String name) { return new NamedNativeQueryMementoImpl<>( name, resultType != null ? resultType : extractResultClass( resultSetMapping ), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sql/spi/NativeQueryImplementor.java b/hibernate-core/src/main/java/org/hibernate/query/sql/spi/NativeQueryImplementor.java index 60cd7ff13452..0eb64239abf1 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sql/spi/NativeQueryImplementor.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sql/spi/NativeQueryImplementor.java @@ -60,7 +60,7 @@ default NativeQueryImplementor setResultTransformer(ResultTransformer } @Override - NamedNativeQueryMemento toMemento(String name); + NamedNativeQueryMemento toMemento(String name); @Override NativeQueryImplementor addScalar(String columnAlias); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java index 9a73398beedb..1172c2b6ab7d 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java @@ -1072,6 +1072,7 @@ else if ( expectedResultClass.isArray() ) { verifySelectionType( componentType, jpaCompliance, selection.getSelectableNode() ); } } + //TODO: else check that the expectedResultClass has an appropriate constructor } /** @@ -1221,9 +1222,10 @@ private static boolean isMatchingDateJdbcType(Class resultClass, JdbcType jdb private static void throwQueryTypeMismatchException(Class resultClass, SqmExpressible sqmExpressible) { throw new QueryTypeMismatchException( String.format( - "Specified result type [%s] did not match Query selection type [%s] - multiple selections: use Tuple or array", - resultClass.getName(), - sqmExpressible.getTypeName() + Locale.ROOT, + "Incorrect query result type: query produces '%s' but type '%s' was given", + sqmExpressible.getTypeName(), + resultClass.getName() ) ); } }