HHH-17782, HHH-17901 Support enum literals in annotation processor HQL validation
This commit is contained in:
parent
de3a4c0af9
commit
5a889f7d56
|
@ -90,12 +90,11 @@ public interface JpaMetamodel extends Metamodel {
|
|||
|
||||
String qualifyImportableName(String queryName);
|
||||
|
||||
@Nullable
|
||||
Set<String> getEnumTypesForValue(String enumValue);
|
||||
@Nullable Set<String> getEnumTypesForValue(String enumValue);
|
||||
|
||||
EnumJavaType<?> getEnumType(String prefix);
|
||||
EnumJavaType<?> getEnumType(String className);
|
||||
|
||||
<E extends Enum<E>> E enumValue(EnumJavaType<E> enumType, String terminal);
|
||||
<E extends Enum<E>> E enumValue(EnumJavaType<E> enumType, String enumValueName);
|
||||
|
||||
JavaType<?> getJavaConstantType(String className, String fieldName);
|
||||
|
||||
|
|
|
@ -58,7 +58,6 @@ import org.hibernate.type.descriptor.java.EnumJavaType;
|
|||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.java.spi.DynamicModelJavaType;
|
||||
import org.hibernate.type.descriptor.java.spi.EntityJavaType;
|
||||
import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
import jakarta.persistence.EntityGraph;
|
||||
|
@ -95,7 +94,8 @@ public class JpaMetamodelImpl implements JpaMetamodelImplementor, Serializable {
|
|||
private final Map<String, ManagedDomainType<?>> managedTypeByName = new TreeMap<>();
|
||||
private final Map<Class<?>, ManagedDomainType<?>> managedTypeByClass = new HashMap<>();
|
||||
private JpaMetaModelPopulationSetting jpaMetaModelPopulationSetting;
|
||||
private final Map<String, Set<String>> allowedEnumLiteralTexts = new HashMap<>();
|
||||
private final Map<String, Set<String>> allowedEnumLiteralsToEnumTypeNames = new HashMap<>();
|
||||
private final Map<String, EnumJavaType<?>> enumJavaTypes = new HashMap<>();
|
||||
|
||||
private final transient Map<String, RootGraphImplementor<?>> entityGraphMap = new ConcurrentHashMap<>();
|
||||
|
||||
|
@ -282,43 +282,19 @@ public class JpaMetamodelImpl implements JpaMetamodelImplementor, Serializable {
|
|||
.collect( Collectors.toSet() );
|
||||
}
|
||||
|
||||
@Override @Nullable
|
||||
public Set<String> getEnumTypesForValue(String enumValue) {
|
||||
return allowedEnumLiteralTexts.get(enumValue);
|
||||
@Override
|
||||
public @Nullable Set<String> getEnumTypesForValue(String enumValue) {
|
||||
return allowedEnumLiteralsToEnumTypeNames.get( enumValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumJavaType<?> getEnumType(String prefix) {
|
||||
final ClassLoaderService classLoaderService =
|
||||
getServiceRegistry().requireService(ClassLoaderService.class);
|
||||
final JavaTypeRegistry registry = getTypeConfiguration().getJavaTypeRegistry();
|
||||
try {
|
||||
final Class<?> namedClass = classLoaderService.classForName( prefix );
|
||||
if ( namedClass != null && namedClass.isEnum() ) {
|
||||
return (EnumJavaType) registry.resolveDescriptor(namedClass);
|
||||
}
|
||||
}
|
||||
catch (ClassLoadingException classLoadingException) {
|
||||
try {
|
||||
final int lastDot = prefix.lastIndexOf('.');
|
||||
if ( lastDot>0) {
|
||||
final String replaced =
|
||||
prefix.substring(0, lastDot) + '$' + prefix.substring(lastDot+1);
|
||||
final Class<?> namedClass = classLoaderService.classForName( replaced );
|
||||
if ( namedClass != null && namedClass.isEnum() ) {
|
||||
return (EnumJavaType) registry.resolveDescriptor(namedClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ClassLoadingException ignore) {
|
||||
}
|
||||
}
|
||||
return null;
|
||||
public EnumJavaType<?> getEnumType(String className) {
|
||||
return enumJavaTypes.get( className );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E extends Enum<E>> E enumValue(EnumJavaType<E> enumType, String terminal) {
|
||||
return Enum.valueOf( enumType.getJavaTypeClass(), terminal );
|
||||
public <E extends Enum<E>> E enumValue(EnumJavaType<E> enumType, String enumValueName) {
|
||||
return Enum.valueOf( enumType.getJavaTypeClass(), enumValueName );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -671,14 +647,15 @@ public class JpaMetamodelImpl implements JpaMetamodelImplementor, Serializable {
|
|||
final Class<? extends Enum<?>> enumJavaClass = enumJavaType.getJavaTypeClass();
|
||||
final Enum<?>[] enumConstants = enumJavaClass.getEnumConstants();
|
||||
for ( Enum<?> enumConstant : enumConstants ) {
|
||||
allowedEnumLiteralTexts
|
||||
.computeIfAbsent( enumConstant.name(), s -> new HashSet<>() )
|
||||
.add( enumJavaClass.getName() );
|
||||
|
||||
final String simpleQualifiedName = enumJavaClass.getSimpleName() + "." + enumConstant.name();
|
||||
allowedEnumLiteralTexts
|
||||
.computeIfAbsent( simpleQualifiedName, s -> new HashSet<>() )
|
||||
.add( enumJavaClass.getName() );
|
||||
addAllowedEnumLiteralsToEnumTypesMap(
|
||||
allowedEnumLiteralsToEnumTypeNames,
|
||||
enumConstant.name(),
|
||||
enumJavaClass.getSimpleName(),
|
||||
enumJavaClass.getCanonicalName(),
|
||||
enumJavaClass.getName()
|
||||
);
|
||||
enumJavaTypes.put( enumJavaClass.getName(), enumJavaType );
|
||||
enumJavaTypes.put( enumJavaClass.getCanonicalName(), enumJavaType );
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
@ -686,6 +663,33 @@ public class JpaMetamodelImpl implements JpaMetamodelImplementor, Serializable {
|
|||
applyNamedEntityGraphs( namedEntityGraphDefinitions );
|
||||
}
|
||||
|
||||
public static void addAllowedEnumLiteralsToEnumTypesMap(
|
||||
Map<String, Set<String>> allowedEnumLiteralsToEnumTypeNames,
|
||||
String enumConstantName,
|
||||
String enumSimpleName,
|
||||
String enumAlternativeName,
|
||||
String enumClassName
|
||||
) {
|
||||
allowedEnumLiteralsToEnumTypeNames
|
||||
.computeIfAbsent( enumConstantName, s -> new HashSet<>() )
|
||||
.add( enumClassName );
|
||||
|
||||
final String simpleQualifiedName = enumSimpleName + "." + enumConstantName;
|
||||
allowedEnumLiteralsToEnumTypeNames
|
||||
.computeIfAbsent( simpleQualifiedName, s -> new HashSet<>() )
|
||||
.add( enumClassName );
|
||||
|
||||
final String qualifiedAlternativeName = enumAlternativeName + "." + enumConstantName;
|
||||
allowedEnumLiteralsToEnumTypeNames
|
||||
.computeIfAbsent( qualifiedAlternativeName, s -> new HashSet<>() )
|
||||
.add( enumClassName );
|
||||
|
||||
final String qualifiedName = enumClassName + "." + enumConstantName;
|
||||
allowedEnumLiteralsToEnumTypeNames
|
||||
.computeIfAbsent( qualifiedName, s -> new HashSet<>() )
|
||||
.add( enumClassName );
|
||||
}
|
||||
|
||||
private EntityDomainType<?> locateOrBuildEntityType(
|
||||
PersistentClass persistentClass,
|
||||
MetadataContext context,
|
||||
|
|
|
@ -550,8 +550,8 @@ public class MappingMetamodelImpl extends QueryParameterBindingTypeResolverImpl
|
|||
}
|
||||
|
||||
@Override
|
||||
public <E extends Enum<E>> E enumValue(EnumJavaType<E> enumType, String terminal) {
|
||||
return jpaMetamodel.enumValue(enumType, terminal);
|
||||
public <E extends Enum<E>> E enumValue(EnumJavaType<E> enumType, String enumValueName) {
|
||||
return jpaMetamodel.enumValue( enumType, enumValueName );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -873,8 +873,8 @@ public abstract class MockSessionFactory
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override @Nullable
|
||||
public Set<String> getEnumTypesForValue(String enumValue) {
|
||||
@Override
|
||||
public @Nullable Set<String> getEnumTypesForValue(String enumValue) {
|
||||
return MockSessionFactory.this.getEnumTypesForValue(enumValue);
|
||||
}
|
||||
|
||||
|
@ -884,8 +884,7 @@ public abstract class MockSessionFactory
|
|||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
Set<String> getEnumTypesForValue(String value) {
|
||||
@Nullable Set<String> getEnumTypesForValue(String value) {
|
||||
return emptySet();
|
||||
}
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@ import java.io.IOException;
|
|||
import java.io.Reader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -58,6 +59,7 @@ import static org.hibernate.internal.util.StringHelper.qualify;
|
|||
import static org.hibernate.internal.util.StringHelper.root;
|
||||
import static org.hibernate.internal.util.StringHelper.split;
|
||||
import static org.hibernate.internal.util.StringHelper.unroot;
|
||||
import static org.hibernate.metamodel.model.domain.internal.JpaMetamodelImpl.addAllowedEnumLiteralsToEnumTypesMap;
|
||||
import static org.hibernate.processor.util.Constants.JAVA_OBJECT;
|
||||
|
||||
/**
|
||||
|
@ -90,7 +92,7 @@ public abstract class ProcessorSessionFactory extends MockSessionFactory {
|
|||
private final Types typeUtil;
|
||||
private final Filer filer;
|
||||
private final Map<String, String> entityNameMappings;
|
||||
private final Map<String, Set<String>> enumTypesByValue;
|
||||
private final Map<String, Set<String>> allowedEnumLiteralsToEnumTypeNames;
|
||||
|
||||
public ProcessorSessionFactory(
|
||||
ProcessingEnvironment processingEnvironment,
|
||||
|
@ -100,7 +102,23 @@ public abstract class ProcessorSessionFactory extends MockSessionFactory {
|
|||
typeUtil = processingEnvironment.getTypeUtils();
|
||||
filer = processingEnvironment.getFiler();
|
||||
this.entityNameMappings = entityNameMappings;
|
||||
this.enumTypesByValue = enumTypesByValue;
|
||||
final Map<String, Set<String>> allowedEnumLiteralsToEnumTypeNames = new HashMap<>( enumTypesByValue.size() << 2 );
|
||||
for ( Map.Entry<String, Set<String>> entry : enumTypesByValue.entrySet() ) {
|
||||
final String enumConstantName = entry.getKey();
|
||||
for ( String enumClassName : entry.getValue() ) {
|
||||
final TypeElement enumTypeElement = elementUtil.getTypeElement( enumClassName );
|
||||
if ( enumTypeElement != null ) {
|
||||
addAllowedEnumLiteralsToEnumTypesMap(
|
||||
allowedEnumLiteralsToEnumTypeNames,
|
||||
enumConstantName,
|
||||
enumTypeElement.getSimpleName().toString(),
|
||||
elementUtil.getBinaryName( enumTypeElement ).toString(),
|
||||
enumClassName
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.allowedEnumLiteralsToEnumTypeNames = allowedEnumLiteralsToEnumTypeNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -217,7 +235,7 @@ public abstract class ProcessorSessionFactory extends MockSessionFactory {
|
|||
|
||||
@Override @Nullable
|
||||
Set<String> getEnumTypesForValue(String value) {
|
||||
Set<String> result = enumTypesByValue.get(value);
|
||||
Set<String> result = allowedEnumLiteralsToEnumTypeNames.get( value);
|
||||
if ( result != null ) {
|
||||
return result;
|
||||
}
|
||||
|
@ -631,7 +649,18 @@ public abstract class ProcessorSessionFactory extends MockSessionFactory {
|
|||
|
||||
@Override
|
||||
boolean isEnum(String className) {
|
||||
final TypeElement typeElement = elementUtil.getTypeElement(className);
|
||||
TypeElement typeElement = elementUtil.getTypeElement( className );
|
||||
int startIdx = 0;
|
||||
int dollarIdx;
|
||||
while ( typeElement == null && ( dollarIdx = className.indexOf( '$', startIdx ) ) != -1 ) {
|
||||
final String potentialBaseTypeName = className.substring( 0, dollarIdx );
|
||||
final TypeElement potentialBaseType = elementUtil.getTypeElement( potentialBaseTypeName );
|
||||
if ( potentialBaseType != null ) {
|
||||
className = potentialBaseTypeName + className.substring( dollarIdx + 1 );
|
||||
typeElement = elementUtil.getTypeElement( className );
|
||||
}
|
||||
startIdx = dollarIdx + 1;
|
||||
}
|
||||
return typeElement != null && typeElement.getKind() == ElementKind.ENUM;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,4 +11,7 @@ public class Book {
|
|||
@NaturalId String author;
|
||||
String text;
|
||||
int pages;
|
||||
Type type;
|
||||
|
||||
enum Type { Book, Magazine, Journal }
|
||||
}
|
||||
|
|
|
@ -147,4 +147,7 @@ public interface Dao {
|
|||
|
||||
@HQL("select b\nfrom Book b\nwhere b.isbn = :isbn")
|
||||
Book findByIsbnMultiline(String isbn);
|
||||
|
||||
@HQL("from Book b where b.type = Magazine")
|
||||
List<Book> findMagazines();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
package org.hibernate.processor.test.dao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.annotations.processing.HQL;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
|
||||
public interface Dao2 {
|
||||
|
||||
EntityManager getEntityManager();
|
||||
|
||||
// Simple name
|
||||
@HQL("from Book b where b.type = Magazine")
|
||||
List<Book> findMagazines1();
|
||||
|
||||
// Simple qualified name
|
||||
@HQL("from Book b where b.type = Type.Magazine")
|
||||
List<Book> findMagazines2();
|
||||
|
||||
// Canonical FQN
|
||||
@HQL("from Book b where b.type = org.hibernate.processor.test.dao.Book.Type.Magazine")
|
||||
List<Book> findMagazines3();
|
||||
|
||||
// Binary FQN
|
||||
@HQL("from Book b where b.type = org.hibernate.processor.test.dao.Book$Type.Magazine")
|
||||
List<Book> findMagazines4();
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* 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>.
|
||||
*/
|
||||
package org.hibernate.processor.test.dao;
|
||||
|
||||
import org.hibernate.processor.test.util.CompilationTest;
|
||||
import org.hibernate.processor.test.util.TestUtil;
|
||||
import org.hibernate.processor.test.util.WithClasses;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hibernate.processor.test.util.TestUtil.assertMetamodelClassGeneratedFor;
|
||||
|
||||
/**
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class DaoTest2 extends CompilationTest {
|
||||
@Test
|
||||
@WithClasses({ Book.class, Dao2.class })
|
||||
public void testDao() {
|
||||
System.out.println( TestUtil.getMetaModelSourceAsString( Dao2.class ) );
|
||||
assertMetamodelClassGeneratedFor( Book.class );
|
||||
assertMetamodelClassGeneratedFor( Dao2.class );
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue