make fully-qualified enum literals in @Query pass the validation
JD examples and TCK require this, though it's not really correct Signed-off-by: Gavin King <gavin@hibernate.org>
This commit is contained in:
parent
fe4a3fbaf3
commit
f0c9d4ec4c
|
@ -19,6 +19,7 @@ import org.hibernate.graph.spi.RootGraphImplementor;
|
||||||
import org.hibernate.jpa.spi.JpaCompliance;
|
import org.hibernate.jpa.spi.JpaCompliance;
|
||||||
import org.hibernate.metamodel.MappingMetamodel;
|
import org.hibernate.metamodel.MappingMetamodel;
|
||||||
import org.hibernate.service.ServiceRegistry;
|
import org.hibernate.service.ServiceRegistry;
|
||||||
|
import org.hibernate.type.descriptor.java.EnumJavaType;
|
||||||
import org.hibernate.type.spi.TypeConfiguration;
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -85,6 +86,10 @@ public interface JpaMetamodel extends Metamodel {
|
||||||
*/
|
*/
|
||||||
Map<String, Map<Class<?>, Enum<?>>> getAllowedEnumLiteralTexts();
|
Map<String, Map<Class<?>, Enum<?>>> getAllowedEnumLiteralTexts();
|
||||||
|
|
||||||
|
EnumJavaType<?> getEnumType(String prefix);
|
||||||
|
|
||||||
|
<E extends Enum<E>> E enumValue(EnumJavaType<E> enumType, String terminal);
|
||||||
|
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
// Covariant returns
|
// Covariant returns
|
||||||
|
|
||||||
|
|
|
@ -243,6 +243,28 @@ public class JpaMetamodelImpl implements JpaMetamodelImplementor, Serializable {
|
||||||
return allowedEnumLiteralTexts;
|
return allowedEnumLiteralTexts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EnumJavaType<?> getEnumType(String prefix) {
|
||||||
|
try {
|
||||||
|
final Class<?> namedClass =
|
||||||
|
getServiceRegistry().requireService( ClassLoaderService.class )
|
||||||
|
.classForName( prefix );
|
||||||
|
if ( namedClass != null && namedClass.isEnum() ) {
|
||||||
|
return (EnumJavaType) getTypeConfiguration()
|
||||||
|
.getJavaTypeRegistry()
|
||||||
|
.resolveDescriptor(namedClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ignore) {
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <E extends Enum<E>> E enumValue(EnumJavaType<E> enumType, String terminal) {
|
||||||
|
return Enum.valueOf( enumType.getJavaTypeClass(), terminal );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> void addNamedEntityGraph(String graphName, RootGraphImplementor<T> entityGraph) {
|
public <T> void addNamedEntityGraph(String graphName, RootGraphImplementor<T> entityGraph) {
|
||||||
final EntityGraph<?> old = entityGraphMap.put(
|
final EntityGraph<?> old = entityGraphMap.put(
|
||||||
|
|
|
@ -72,6 +72,7 @@ import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||||
import org.hibernate.type.BasicType;
|
import org.hibernate.type.BasicType;
|
||||||
import org.hibernate.type.ComponentType;
|
import org.hibernate.type.ComponentType;
|
||||||
import org.hibernate.type.Type;
|
import org.hibernate.type.Type;
|
||||||
|
import org.hibernate.type.descriptor.java.EnumJavaType;
|
||||||
import org.hibernate.type.descriptor.java.JavaType;
|
import org.hibernate.type.descriptor.java.JavaType;
|
||||||
import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry;
|
import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry;
|
||||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
|
@ -523,6 +524,16 @@ public class MappingMetamodelImpl extends QueryParameterBindingTypeResolverImpl
|
||||||
return jpaMetamodel.getAllowedEnumLiteralTexts();
|
return jpaMetamodel.getAllowedEnumLiteralTexts();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EnumJavaType<?> getEnumType(String className) {
|
||||||
|
return jpaMetamodel.getEnumType(className);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <E extends Enum<E>> E enumValue(EnumJavaType<E> enumType, String terminal) {
|
||||||
|
return jpaMetamodel.enumValue(enumType, terminal);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getImplementors(String className) throws MappingException {
|
public String[] getImplementors(String className) throws MappingException {
|
||||||
// computeIfAbsent() can be a contention point and we expect all the values to be in the map at some point so
|
// computeIfAbsent() can be a contention point and we expect all the values to be in the map at some point so
|
||||||
|
|
|
@ -10,12 +10,14 @@ import java.lang.reflect.Field;
|
||||||
|
|
||||||
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
|
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
|
||||||
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
||||||
|
import org.hibernate.metamodel.model.domain.spi.JpaMetamodelImplementor;
|
||||||
import org.hibernate.query.SemanticException;
|
import org.hibernate.query.SemanticException;
|
||||||
import org.hibernate.query.hql.HqlLogging;
|
import org.hibernate.query.hql.HqlLogging;
|
||||||
import org.hibernate.query.hql.spi.DotIdentifierConsumer;
|
import org.hibernate.query.hql.spi.DotIdentifierConsumer;
|
||||||
import org.hibernate.query.hql.spi.SemanticPathPart;
|
import org.hibernate.query.hql.spi.SemanticPathPart;
|
||||||
import org.hibernate.query.hql.spi.SqmCreationState;
|
import org.hibernate.query.hql.spi.SqmCreationState;
|
||||||
import org.hibernate.query.hql.spi.SqmPathRegistry;
|
import org.hibernate.query.hql.spi.SqmPathRegistry;
|
||||||
|
import org.hibernate.query.sqm.NodeBuilder;
|
||||||
import org.hibernate.query.sqm.function.SqmFunctionDescriptor;
|
import org.hibernate.query.sqm.function.SqmFunctionDescriptor;
|
||||||
import org.hibernate.query.sqm.spi.SqmCreationContext;
|
import org.hibernate.query.sqm.spi.SqmCreationContext;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
||||||
|
@ -26,7 +28,6 @@ import org.hibernate.query.sqm.tree.expression.SqmLiteralEntityType;
|
||||||
import org.hibernate.query.sqm.tree.from.SqmFrom;
|
import org.hibernate.query.sqm.tree.from.SqmFrom;
|
||||||
import org.hibernate.type.descriptor.java.EnumJavaType;
|
import org.hibernate.type.descriptor.java.EnumJavaType;
|
||||||
import org.hibernate.type.descriptor.java.JavaType;
|
import org.hibernate.type.descriptor.java.JavaType;
|
||||||
import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @asciidoc
|
* @asciidoc
|
||||||
|
@ -177,17 +178,20 @@ public class BasicDotIdentifierConsumer implements DotIdentifierConsumer {
|
||||||
}
|
}
|
||||||
|
|
||||||
final String path = pathSoFar.toString();
|
final String path = pathSoFar.toString();
|
||||||
final String importableName = creationContext.getJpaMetamodel().qualifyImportableName( path );
|
final JpaMetamodelImplementor jpaMetamodel = creationContext.getJpaMetamodel();
|
||||||
|
final String importableName = jpaMetamodel.qualifyImportableName( path );
|
||||||
|
final NodeBuilder nodeBuilder = creationContext.getNodeBuilder();
|
||||||
if ( importableName != null ) {
|
if ( importableName != null ) {
|
||||||
final EntityDomainType<?> entityDomainType = creationContext.getJpaMetamodel().entity( importableName );
|
final EntityDomainType<?> entityDomainType = jpaMetamodel.entity( importableName );
|
||||||
if ( entityDomainType != null ) {
|
if ( entityDomainType != null ) {
|
||||||
return new SqmLiteralEntityType( entityDomainType, creationContext.getNodeBuilder() );
|
return new SqmLiteralEntityType( entityDomainType, nodeBuilder );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final SqmFunctionDescriptor functionDescriptor = creationContext.getQueryEngine()
|
final SqmFunctionDescriptor functionDescriptor =
|
||||||
.getSqmFunctionRegistry()
|
creationContext.getQueryEngine()
|
||||||
.findFunctionDescriptor( path );
|
.getSqmFunctionRegistry()
|
||||||
|
.findFunctionDescriptor( path );
|
||||||
if ( functionDescriptor != null ) {
|
if ( functionDescriptor != null ) {
|
||||||
return functionDescriptor.generateSqmExpression(
|
return functionDescriptor.generateSqmExpression(
|
||||||
null,
|
null,
|
||||||
|
@ -195,55 +199,37 @@ public class BasicDotIdentifierConsumer implements DotIdentifierConsumer {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// // see if it is a Class name...
|
|
||||||
// try {
|
|
||||||
// final Class<?> namedClass = creationState.getCreationContext()
|
|
||||||
// .getServiceRegistry()
|
|
||||||
// .getService( ClassLoaderService.class )
|
|
||||||
// .classForName( pathSoFar );
|
|
||||||
// if ( namedClass != null ) {
|
|
||||||
// return new
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// catch (Exception ignore) {
|
|
||||||
// }
|
|
||||||
|
|
||||||
// see if it is a named field/enum reference
|
// see if it is a named field/enum reference
|
||||||
final int splitPosition = path.lastIndexOf( '.' );
|
final int splitPosition = path.lastIndexOf( '.' );
|
||||||
if ( splitPosition > 0 ) {
|
if ( splitPosition > 0 ) {
|
||||||
final String prefix = path.substring( 0, splitPosition );
|
final String prefix = path.substring( 0, splitPosition );
|
||||||
final String terminal = path.substring( splitPosition + 1 );
|
final String terminal = path.substring( splitPosition + 1 );
|
||||||
|
|
||||||
//TODO: try interpreting paths of form foo.bar.Foo.Bar as foo.bar.Foo$Bar
|
//TODO: try interpreting paths of form foo.bar.Foo.Bar as foo.bar.Foo$Bar
|
||||||
|
final EnumJavaType<?> enumType = jpaMetamodel.getEnumType(prefix);
|
||||||
|
if ( enumType != null ) {
|
||||||
|
return new SqmEnumLiteral(
|
||||||
|
jpaMetamodel.enumValue(enumType, terminal),
|
||||||
|
enumType,
|
||||||
|
terminal,
|
||||||
|
nodeBuilder
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final Class<?> namedClass = creationContext
|
final Class<?> namedClass =
|
||||||
.getServiceRegistry()
|
creationContext.getServiceRegistry()
|
||||||
.requireService( ClassLoaderService.class )
|
.requireService( ClassLoaderService.class )
|
||||||
.classForName( prefix );
|
.classForName( prefix );
|
||||||
if ( namedClass != null ) {
|
if ( namedClass != null ) {
|
||||||
final JavaTypeRegistry javaTypeRegistry = creationContext.getJpaMetamodel()
|
final Field referencedField = namedClass.getDeclaredField( terminal );
|
||||||
.getTypeConfiguration()
|
if ( referencedField != null ) {
|
||||||
.getJavaTypeRegistry();
|
final JavaType<?> fieldJtd =
|
||||||
|
jpaMetamodel
|
||||||
if ( namedClass.isEnum() ) {
|
.getTypeConfiguration()
|
||||||
return new SqmEnumLiteral(
|
.getJavaTypeRegistry()
|
||||||
Enum.valueOf( (Class) namedClass, terminal ),
|
.getDescriptor( referencedField.getType() );
|
||||||
(EnumJavaType) javaTypeRegistry.resolveDescriptor( namedClass ),
|
return new SqmFieldLiteral( referencedField, fieldJtd, nodeBuilder);
|
||||||
terminal,
|
|
||||||
creationContext.getNodeBuilder()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
final Field referencedField = namedClass.getDeclaredField( terminal );
|
|
||||||
if ( referencedField != null ) {
|
|
||||||
final JavaType<?> fieldJtd = javaTypeRegistry
|
|
||||||
.getDescriptor( referencedField.getType() );
|
|
||||||
//noinspection unchecked
|
|
||||||
return new SqmFieldLiteral( referencedField, fieldJtd, creationContext.getNodeBuilder() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ignore) {
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package org.hibernate.processor.test.data;
|
package org.hibernate.processor.test.data.basic;
|
||||||
|
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
import jakarta.persistence.Id;
|
import jakarta.persistence.Id;
|
|
@ -1,4 +1,4 @@
|
||||||
package org.hibernate.processor.test.data;
|
package org.hibernate.processor.test.data.basic;
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import org.hibernate.annotations.NaturalId;
|
import org.hibernate.annotations.NaturalId;
|
|
@ -1,4 +1,4 @@
|
||||||
package org.hibernate.processor.test.data;
|
package org.hibernate.processor.test.data.basic;
|
||||||
|
|
||||||
import jakarta.data.Limit;
|
import jakarta.data.Limit;
|
||||||
import jakarta.data.Order;
|
import jakarta.data.Order;
|
||||||
|
@ -22,7 +22,6 @@ import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@Repository(dataStore = "myds")
|
@Repository(dataStore = "myds")
|
|
@ -4,7 +4,7 @@
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
*/
|
*/
|
||||||
package org.hibernate.processor.test.data;
|
package org.hibernate.processor.test.data.basic;
|
||||||
|
|
||||||
import org.hibernate.processor.test.util.CompilationTest;
|
import org.hibernate.processor.test.util.CompilationTest;
|
||||||
import org.hibernate.processor.test.util.WithClasses;
|
import org.hibernate.processor.test.util.WithClasses;
|
|
@ -102,4 +102,7 @@ public interface Library {
|
||||||
|
|
||||||
@Find
|
@Find
|
||||||
List<Author> authorsByCityAndPostcode(String address_city, String address_postcode);
|
List<Author> authorsByCityAndPostcode(String address_city, String address_postcode);
|
||||||
|
|
||||||
|
@Query("where type = org.hibernate.processor.test.data.eg.Type.Magazine")
|
||||||
|
List<Book> magazines();
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,6 +108,7 @@ import org.hibernate.type.MapType;
|
||||||
import org.hibernate.type.SetType;
|
import org.hibernate.type.SetType;
|
||||||
import org.hibernate.type.SqlTypes;
|
import org.hibernate.type.SqlTypes;
|
||||||
import org.hibernate.type.Type;
|
import org.hibernate.type.Type;
|
||||||
|
import org.hibernate.type.descriptor.java.EnumJavaType;
|
||||||
import org.hibernate.type.descriptor.java.JavaType;
|
import org.hibernate.type.descriptor.java.JavaType;
|
||||||
import org.hibernate.type.descriptor.java.spi.UnknownBasicJavaType;
|
import org.hibernate.type.descriptor.java.spi.UnknownBasicJavaType;
|
||||||
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
|
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
|
||||||
|
@ -283,6 +284,8 @@ public abstract class MockSessionFactory
|
||||||
|
|
||||||
abstract boolean isClassDefined(String qualifiedName);
|
abstract boolean isClassDefined(String qualifiedName);
|
||||||
|
|
||||||
|
abstract boolean isEnum(String className);
|
||||||
|
|
||||||
abstract boolean isFieldDefined(String qualifiedClassName, String fieldName);
|
abstract boolean isFieldDefined(String qualifiedClassName, String fieldName);
|
||||||
|
|
||||||
abstract boolean isConstructorDefined(String qualifiedClassName, List<Type> argumentTypes);
|
abstract boolean isConstructorDefined(String qualifiedClassName, List<Type> argumentTypes);
|
||||||
|
@ -823,6 +826,26 @@ public abstract class MockSessionFactory
|
||||||
throw new UnsupportedOperationException("operation not supported");
|
throw new UnsupportedOperationException("operation not supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EnumJavaType<?> getEnumType(String className) {
|
||||||
|
if ( isEnum(className) ) {
|
||||||
|
return new EnumJavaType( Enum.class ) {
|
||||||
|
@Override
|
||||||
|
public String getTypeName() {
|
||||||
|
return className;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <E extends Enum<E>> E enumValue(EnumJavaType<E> enumType, String terminal) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JpaCompliance getJpaCompliance() {
|
public JpaCompliance getJpaCompliance() {
|
||||||
return jpaCompliance;
|
return jpaCompliance;
|
||||||
|
|
|
@ -563,6 +563,12 @@ public abstract class ProcessorSessionFactory extends MockSessionFactory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean isEnum(String className) {
|
||||||
|
final TypeElement typeElement = elementUtil.getTypeElement(className);
|
||||||
|
return typeElement != null && typeElement.getKind() == ElementKind.ENUM;
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean isEmbeddableType(TypeElement type) {
|
private static boolean isEmbeddableType(TypeElement type) {
|
||||||
return hasAnnotation(type, "Embeddable");
|
return hasAnnotation(type, "Embeddable");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue