Skip to content

Commit ce31796

Browse files
committed
handle unqualified enum values in @query
Signed-off-by: Gavin King <[email protected]>
1 parent f0c9d4e commit ce31796

File tree

8 files changed

+125
-51
lines changed

8 files changed

+125
-51
lines changed

hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/JpaMetamodel.java

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
package org.hibernate.metamodel.model.domain;
88

99
import java.util.List;
10-
import java.util.Map;
1110
import java.util.Set;
1211
import jakarta.persistence.metamodel.EmbeddableType;
1312
import jakarta.persistence.metamodel.EntityType;
@@ -79,12 +78,7 @@ public interface JpaMetamodel extends Metamodel {
7978

8079
String qualifyImportableName(String queryName);
8180

82-
/**
83-
* Returns a map that gives access to the enum literal expressions that can be used in queries.
84-
* The key is the shorthand enum literal. The value is a map, from enum class to the actual enum value.
85-
* This is needed for parsing shorthand enum literals that don't use FQNs.
86-
*/
87-
Map<String, Map<Class<?>, Enum<?>>> getAllowedEnumLiteralTexts();
81+
Set<String> getAllowedEnumLiteralTexts(String enumValue);
8882

8983
EnumJavaType<?> getEnumType(String prefix);
9084

hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/JpaMetamodelImpl.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ private static class ImportInfo<T> {
9191
private final Map<Class<?>, ManagedDomainType<?>> jpaManagedTypeMap = new HashMap<>();
9292
private final Set<ManagedDomainType<?>> jpaManagedTypes = new HashSet<>();
9393
private final Set<EmbeddableDomainType<?>> jpaEmbeddables = new HashSet<>();
94-
private final Map<String, Map<Class<?>, Enum<?>>> allowedEnumLiteralTexts = new HashMap<>();
94+
private final Map<String, Set<String>> allowedEnumLiteralTexts = new HashMap<>();
9595

9696
private final transient Map<String, RootGraphImplementor<?>> entityGraphMap = new ConcurrentHashMap<>();
9797

@@ -239,8 +239,8 @@ public Set<EmbeddableType<?>> getEmbeddables() {
239239
}
240240

241241
@Override
242-
public Map<String, Map<Class<?>, Enum<?>>> getAllowedEnumLiteralTexts() {
243-
return allowedEnumLiteralTexts;
242+
public Set<String> getAllowedEnumLiteralTexts(String enumValue) {
243+
return allowedEnumLiteralTexts.get(enumValue);
244244
}
245245

246246
@Override
@@ -604,13 +604,13 @@ public void processJpa(
604604
final Enum<?>[] enumConstants = enumJavaClass.getEnumConstants();
605605
for ( Enum<?> enumConstant : enumConstants ) {
606606
allowedEnumLiteralTexts
607-
.computeIfAbsent( enumConstant.name(), (s) -> new HashMap<>() )
608-
.put( enumJavaClass, enumConstant );
607+
.computeIfAbsent( enumConstant.name(), s -> new HashSet<>() )
608+
.add( enumJavaClass.getName() );
609609

610610
final String simpleQualifiedName = enumJavaClass.getSimpleName() + "." + enumConstant.name();
611611
allowedEnumLiteralTexts
612-
.computeIfAbsent( simpleQualifiedName, (s) -> new HashMap<>() )
613-
.put( enumJavaClass, enumConstant );
612+
.computeIfAbsent( simpleQualifiedName, s -> new HashSet<>() )
613+
.add( enumJavaClass.getName() );
614614
}
615615
}
616616
} );

hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappingMetamodelImpl.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -520,8 +520,8 @@ public String qualifyImportableName(String queryName) {
520520
}
521521

522522
@Override
523-
public Map<String, Map<Class<?>, Enum<?>>> getAllowedEnumLiteralTexts() {
524-
return jpaMetamodel.getAllowedEnumLiteralTexts();
523+
public Set<String> getAllowedEnumLiteralTexts(String enumValue) {
524+
return jpaMetamodel.getAllowedEnumLiteralTexts(enumValue);
525525
}
526526

527527
@Override

hibernate-core/src/main/java/org/hibernate/query/criteria/JpaTupleElement.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import jakarta.persistence.TupleElement;
1010

11+
import org.hibernate.type.descriptor.java.EnumJavaType;
1112
import org.hibernate.type.descriptor.java.JavaType;
1213

1314
/**
@@ -23,4 +24,12 @@ default Class<? extends T> getJavaType() {
2324
// todo (6.0) : can this signature just return `Class<T>`?
2425
return getJavaTypeDescriptor() == null ? null : getJavaTypeDescriptor().getJavaTypeClass();
2526
}
27+
28+
default String getJavaTypeName() {
29+
return getJavaTypeDescriptor() == null ? null : getJavaTypeDescriptor().getTypeName();
30+
}
31+
32+
default boolean isEnum() {
33+
return getJavaTypeDescriptor() instanceof EnumJavaType;
34+
}
2635
}

hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java

Lines changed: 68 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -586,18 +586,19 @@ public SqmInsertStatement<R> visitInsertStatement(HqlParser.InsertStatementConte
586586
final Iterator<SqmPath<?>> iterator = insertStatement.getInsertionTargetPaths().iterator();
587587
for ( int j = 1; j < values.getChildCount(); j += 2 ) {
588588
final SqmPath<?> targetPath = iterator.next();
589-
final Class<?> targetPathJavaType = targetPath.getJavaType();
590-
final boolean isEnum = targetPathJavaType != null && targetPathJavaType.isEnum();
589+
final String targetPathJavaType = targetPath.getJavaTypeName();
590+
final boolean isEnum = targetPath.isEnum();
591591
final ParseTree valuesContext = values.getChild( j );
592592
final HqlParser.ExpressionContext expressionContext;
593-
final Map<Class<?>, Enum<?>> possibleEnumValues;
593+
final Set<String> possibleEnumTypes;
594594
final SqmExpression<?> value;
595595
if ( isEnum && valuesContext.getChild( 0 ) instanceof HqlParser.ExpressionContext
596-
&& ( possibleEnumValues = getPossibleEnumValues( expressionContext = (HqlParser.ExpressionContext) valuesContext.getChild( 0 ) ) ) != null ) {
596+
&& ( possibleEnumTypes = getPossibleEnumTypes( expressionContext = (HqlParser.ExpressionContext) valuesContext.getChild( 0 ) ) ) != null ) {
597597
value = resolveEnumShorthandLiteral(
598598
expressionContext,
599-
possibleEnumValues,
600-
targetPathJavaType
599+
getPossibleEnumValue( expressionContext ),
600+
targetPathJavaType,
601+
possibleEnumTypes
601602
);
602603
}
603604
else {
@@ -692,18 +693,19 @@ public SqmUpdateStatement<R> visitUpdateStatement(HqlParser.UpdateStatementConte
692693
public SqmAssignment<?> visitAssignment(HqlParser.AssignmentContext ctx) {
693694
//noinspection unchecked
694695
final SqmPath<Object> targetPath = (SqmPath<Object>) consumeDomainPath( ctx.simplePath() );
695-
final Class<?> targetPathJavaType = targetPath.getJavaType();
696-
final boolean isEnum = targetPathJavaType != null && targetPathJavaType.isEnum();
696+
final String targetPathJavaType = targetPath.getJavaTypeName();
697+
final boolean isEnum = targetPath.isEnum();
697698
final ParseTree rightSide = ctx.getChild( 2 );
698699
final HqlParser.ExpressionContext expressionContext;
699-
final Map<Class<?>, Enum<?>> possibleEnumValues;
700+
final Set<String> possibleEnumValues;
700701
final SqmExpression<?> value;
701702
if ( isEnum && rightSide.getChild( 0 ) instanceof HqlParser.ExpressionContext
702-
&& ( possibleEnumValues = getPossibleEnumValues( expressionContext = (HqlParser.ExpressionContext) rightSide.getChild( 0 ) ) ) != null ) {
703+
&& ( possibleEnumValues = getPossibleEnumTypes( expressionContext = (HqlParser.ExpressionContext) rightSide.getChild( 0 ) ) ) != null ) {
703704
value = resolveEnumShorthandLiteral(
704705
expressionContext,
705-
possibleEnumValues,
706-
targetPathJavaType
706+
getPossibleEnumValue( expressionContext ),
707+
targetPathJavaType,
708+
possibleEnumValues
707709
);
708710
}
709711
else {
@@ -2518,21 +2520,23 @@ private SqmComparisonPredicate createComparisonPredicate(
25182520
HqlParser.ExpressionContext rightExpressionContext) {
25192521
final SqmExpression<?> right;
25202522
final SqmExpression<?> left;
2521-
Map<Class<?>, Enum<?>> possibleEnumValues;
2522-
if ( ( possibleEnumValues = getPossibleEnumValues( leftExpressionContext ) ) != null ) {
2523+
Set<String> possibleEnumTypes;
2524+
if ( ( possibleEnumTypes = getPossibleEnumTypes( leftExpressionContext ) ) != null ) {
25232525
right = (SqmExpression<?>) rightExpressionContext.accept( this );
25242526
left = resolveEnumShorthandLiteral(
25252527
leftExpressionContext,
2526-
possibleEnumValues,
2527-
right.getJavaType()
2528+
getPossibleEnumValue( leftExpressionContext ),
2529+
right.getJavaTypeName(),
2530+
possibleEnumTypes
25282531
);
25292532
}
2530-
else if ( ( possibleEnumValues = getPossibleEnumValues( rightExpressionContext ) ) != null ) {
2533+
else if ( ( possibleEnumTypes = getPossibleEnumTypes( rightExpressionContext ) ) != null ) {
25312534
left = (SqmExpression<?>) leftExpressionContext.accept( this );
25322535
right = resolveEnumShorthandLiteral(
25332536
rightExpressionContext,
2534-
possibleEnumValues,
2535-
left.getJavaType()
2537+
getPossibleEnumValue( rightExpressionContext ),
2538+
left.getJavaTypeName(),
2539+
possibleEnumTypes
25362540
);
25372541
}
25382542
else {
@@ -2570,20 +2574,21 @@ private <T> SqmExpression<T> createDiscriminatorValue(
25702574
);
25712575
}
25722576

2573-
private SqmExpression<?> resolveEnumShorthandLiteral(HqlParser.ExpressionContext expressionContext, Map<Class<?>, Enum<?>> possibleEnumValues, Class<?> enumType) {
2574-
final Enum<?> enumValue;
2575-
if ( possibleEnumValues != null && ( enumValue = possibleEnumValues.get( enumType ) ) != null ) {
2577+
private SqmExpression<?> resolveEnumShorthandLiteral(
2578+
HqlParser.ExpressionContext expressionContext,
2579+
String enumValue, String enumType, Set<String> enumTypes) {
2580+
if ( enumValue != null && enumType != null && enumTypes.contains(enumType) ) {
25762581
DotIdentifierConsumer dotIdentifierConsumer = dotIdentifierConsumerStack.getCurrent();
2577-
dotIdentifierConsumer.consumeIdentifier( enumValue.getClass().getName(), true, false );
2578-
dotIdentifierConsumer.consumeIdentifier( enumValue.name(), false, true );
2582+
dotIdentifierConsumer.consumeIdentifier( enumType, true, false );
2583+
dotIdentifierConsumer.consumeIdentifier( enumValue, false, true );
25792584
return (SqmExpression<?>) dotIdentifierConsumerStack.getCurrent().getConsumedPart();
25802585
}
25812586
else {
25822587
return (SqmExpression<?>) expressionContext.accept( this );
25832588
}
25842589
}
25852590

2586-
private Map<Class<?>, Enum<?>> getPossibleEnumValues(HqlParser.ExpressionContext expressionContext) {
2591+
private Set<String> getPossibleEnumTypes(HqlParser.ExpressionContext expressionContext) {
25872592
ParseTree ctx;
25882593
// Traverse the expression structure according to the grammar
25892594
if ( expressionContext instanceof HqlParser.BarePrimaryExpressionContext && expressionContext.getChildCount() == 1 ) {
@@ -2597,7 +2602,29 @@ private Map<Class<?>, Enum<?>> getPossibleEnumValues(HqlParser.ExpressionContext
25972602
ctx = ctx.getChild( 0 );
25982603

25992604
if ( ctx instanceof HqlParser.SimplePathContext ) {
2600-
return creationContext.getJpaMetamodel().getAllowedEnumLiteralTexts().get( ctx.getText() );
2605+
return creationContext.getJpaMetamodel().getAllowedEnumLiteralTexts( ctx.getText() );
2606+
}
2607+
}
2608+
}
2609+
2610+
return null;
2611+
}
2612+
2613+
private String getPossibleEnumValue(HqlParser.ExpressionContext expressionContext) {
2614+
ParseTree ctx;
2615+
// Traverse the expression structure according to the grammar
2616+
if ( expressionContext instanceof HqlParser.BarePrimaryExpressionContext && expressionContext.getChildCount() == 1 ) {
2617+
ctx = expressionContext.getChild( 0 );
2618+
2619+
while ( ctx instanceof HqlParser.PrimaryExpressionContext && ctx.getChildCount() == 1 ) {
2620+
ctx = ctx.getChild( 0 );
2621+
}
2622+
2623+
if ( ctx instanceof HqlParser.GeneralPathFragmentContext && ctx.getChildCount() == 1 ) {
2624+
ctx = ctx.getChild( 0 );
2625+
2626+
if ( ctx instanceof HqlParser.SimplePathContext ) {
2627+
return ctx.getText();
26012628
}
26022629
}
26032630
}
@@ -2689,8 +2716,8 @@ public SqmPredicate visitInPredicate(HqlParser.InPredicateContext ctx) {
26892716
final HqlParser.ExplicitTupleInListContext tupleExpressionListContext = (HqlParser.ExplicitTupleInListContext) inListContext;
26902717
final int size = tupleExpressionListContext.getChildCount();
26912718
final int estimatedSize = size >> 1;
2692-
final Class<?> testExpressionJavaType = testExpression.getJavaType();
2693-
final boolean isEnum = testExpressionJavaType != null && testExpressionJavaType.isEnum();
2719+
final String testExpressionJavaType = testExpression.getJavaTypeName();
2720+
final boolean isEnum = testExpression.isEnum();
26942721
// Multivalued bindings are only allowed if there is a single list item, hence size 3 (LP, RP and param)
26952722
parameterDeclarationContextStack.push( () -> size == 3 );
26962723
try {
@@ -2700,14 +2727,15 @@ public SqmPredicate visitInPredicate(HqlParser.InPredicateContext ctx) {
27002727
if ( parseTree instanceof HqlParser.ExpressionOrPredicateContext ) {
27012728
final ParseTree child = parseTree.getChild( 0 );
27022729
final HqlParser.ExpressionContext expressionContext;
2703-
final Map<Class<?>, Enum<?>> possibleEnumValues;
2730+
final Set<String> possibleEnumTypes;
27042731
if ( isEnum && child instanceof HqlParser.ExpressionContext
2705-
&& ( possibleEnumValues = getPossibleEnumValues( expressionContext = (HqlParser.ExpressionContext) child ) ) != null ) {
2732+
&& ( possibleEnumTypes = getPossibleEnumTypes( expressionContext = (HqlParser.ExpressionContext) child ) ) != null ) {
27062733
listExpressions.add(
27072734
resolveEnumShorthandLiteral(
27082735
expressionContext,
2709-
possibleEnumValues,
2710-
testExpressionJavaType
2736+
getPossibleEnumValue( expressionContext ),
2737+
testExpressionJavaType,
2738+
possibleEnumTypes
27112739
)
27122740
);
27132741
}
@@ -3229,9 +3257,14 @@ public Object visitCaseExpression(HqlParser.CaseExpressionContext ctx) {
32293257
final HqlParser.SimpleCaseWhenContext simpleCaseWhenContext = ctx.simpleCaseWhen( i );
32303258
final HqlParser.ExpressionContext testExpression = simpleCaseWhenContext.expression();
32313259
final SqmExpression<?> test;
3232-
final Map<Class<?>, Enum<?>> possibleEnumValues;
3233-
if ( ( possibleEnumValues = getPossibleEnumValues( testExpression ) ) != null ) {
3234-
test = resolveEnumShorthandLiteral( testExpression, possibleEnumValues, expression.getJavaType() );
3260+
final Set<String> possibleEnumTypes;
3261+
if ( ( possibleEnumTypes = getPossibleEnumTypes( testExpression ) ) != null ) {
3262+
test = resolveEnumShorthandLiteral(
3263+
testExpression,
3264+
getPossibleEnumValue( testExpression ),
3265+
expression.getJavaTypeName(),
3266+
possibleEnumTypes
3267+
);
32353268
}
32363269
else {
32373270
test = (SqmExpression<?>) testExpression.accept( this );

tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/eg/Library.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,4 +105,7 @@ record BookWithAuthor(Book book, Author author) {}
105105

106106
@Query("where type = org.hibernate.processor.test.data.eg.Type.Magazine")
107107
List<Book> magazines();
108+
109+
@Query("where type = Journal")
110+
List<Book> journals();
108111
}

tooling/metamodel-generator/src/main/java/org/hibernate/processor/validation/MockSessionFactory.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -846,12 +846,21 @@ public <E extends Enum<E>> E enumValue(EnumJavaType<E> enumType, String terminal
846846
return null;
847847
}
848848

849+
@Override
850+
public Set<String> getAllowedEnumLiteralTexts(String enumValue) {
851+
return MockSessionFactory.this.getAllowedEnumLiteralTexts().get(enumValue);
852+
}
853+
849854
@Override
850855
public JpaCompliance getJpaCompliance() {
851856
return jpaCompliance;
852857
}
853858
}
854859

860+
Map<String, Set<String>> getAllowedEnumLiteralTexts() {
861+
return emptyMap();
862+
}
863+
855864
class MockMappedDomainType<X> extends MappedSuperclassTypeImpl<X>{
856865
public MockMappedDomainType(String typeName) {
857866
super(typeName, false, true, false, null, null, metamodel.getJpaMetamodel());

tooling/metamodel-generator/src/main/java/org/hibernate/processor/validation/ProcessorSessionFactory.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,10 @@
4343
import java.beans.Introspector;
4444
import java.util.ArrayList;
4545
import java.util.HashMap;
46+
import java.util.HashSet;
4647
import java.util.List;
4748
import java.util.Map;
49+
import java.util.Set;
4850

4951
import static java.util.Arrays.stream;
5052
import static org.hibernate.internal.util.StringHelper.qualify;
@@ -194,6 +196,30 @@ private static JdbcType enumJdbcType(Element member) {
194196
: IntegerJdbcType.INSTANCE;
195197
}
196198

199+
final Map<String, Set<String>> result = new HashMap<>();
200+
201+
@Override
202+
Map<String, Set<String>> getAllowedEnumLiteralTexts() {
203+
//TODO: elementUtil.getAllModuleElements();
204+
if ( result.isEmpty() ) {
205+
for (Element mod : elementUtil.getModuleElement("").getEnclosedElements()) {
206+
for (Element element : mod.getEnclosedElements()) {
207+
if (element.getKind() == ElementKind.ENUM) {
208+
TypeElement typeElement = (TypeElement) element;
209+
for (Element member : element.getEnclosedElements()) {
210+
if (member.getKind() == ElementKind.ENUM_CONSTANT) {
211+
String name = member.getSimpleName().toString();
212+
result.computeIfAbsent( name, s -> new HashSet<>() )
213+
.add( typeElement.getQualifiedName().toString() );
214+
}
215+
}
216+
}
217+
}
218+
}
219+
}
220+
return result;
221+
}
222+
197223
private static Type elementCollectionElementType(TypeElement elementType,
198224
String role, String path,
199225
AccessType defaultAccessType) {

0 commit comments

Comments
 (0)