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);
|
String qualifyImportableName(String queryName);
|
||||||
|
|
||||||
@Nullable
|
@Nullable Set<String> getEnumTypesForValue(String enumValue);
|
||||||
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);
|
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.JavaType;
|
||||||
import org.hibernate.type.descriptor.java.spi.DynamicModelJavaType;
|
import org.hibernate.type.descriptor.java.spi.DynamicModelJavaType;
|
||||||
import org.hibernate.type.descriptor.java.spi.EntityJavaType;
|
import org.hibernate.type.descriptor.java.spi.EntityJavaType;
|
||||||
import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry;
|
|
||||||
import org.hibernate.type.spi.TypeConfiguration;
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
import jakarta.persistence.EntityGraph;
|
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<String, ManagedDomainType<?>> managedTypeByName = new TreeMap<>();
|
||||||
private final Map<Class<?>, ManagedDomainType<?>> managedTypeByClass = new HashMap<>();
|
private final Map<Class<?>, ManagedDomainType<?>> managedTypeByClass = new HashMap<>();
|
||||||
private JpaMetaModelPopulationSetting jpaMetaModelPopulationSetting;
|
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<>();
|
private final transient Map<String, RootGraphImplementor<?>> entityGraphMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
@ -282,43 +282,19 @@ public class JpaMetamodelImpl implements JpaMetamodelImplementor, Serializable {
|
||||||
.collect( Collectors.toSet() );
|
.collect( Collectors.toSet() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override @Nullable
|
@Override
|
||||||
public Set<String> getEnumTypesForValue(String enumValue) {
|
public @Nullable Set<String> getEnumTypesForValue(String enumValue) {
|
||||||
return allowedEnumLiteralTexts.get(enumValue);
|
return allowedEnumLiteralsToEnumTypeNames.get( enumValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EnumJavaType<?> getEnumType(String prefix) {
|
public EnumJavaType<?> getEnumType(String className) {
|
||||||
final ClassLoaderService classLoaderService =
|
return enumJavaTypes.get( className );
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <E extends Enum<E>> E enumValue(EnumJavaType<E> enumType, String terminal) {
|
public <E extends Enum<E>> E enumValue(EnumJavaType<E> enumType, String enumValueName) {
|
||||||
return Enum.valueOf( enumType.getJavaTypeClass(), terminal );
|
return Enum.valueOf( enumType.getJavaTypeClass(), enumValueName );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -671,14 +647,15 @@ public class JpaMetamodelImpl implements JpaMetamodelImplementor, Serializable {
|
||||||
final Class<? extends Enum<?>> enumJavaClass = enumJavaType.getJavaTypeClass();
|
final Class<? extends Enum<?>> enumJavaClass = enumJavaType.getJavaTypeClass();
|
||||||
final Enum<?>[] enumConstants = enumJavaClass.getEnumConstants();
|
final Enum<?>[] enumConstants = enumJavaClass.getEnumConstants();
|
||||||
for ( Enum<?> enumConstant : enumConstants ) {
|
for ( Enum<?> enumConstant : enumConstants ) {
|
||||||
allowedEnumLiteralTexts
|
addAllowedEnumLiteralsToEnumTypesMap(
|
||||||
.computeIfAbsent( enumConstant.name(), s -> new HashSet<>() )
|
allowedEnumLiteralsToEnumTypeNames,
|
||||||
.add( enumJavaClass.getName() );
|
enumConstant.name(),
|
||||||
|
enumJavaClass.getSimpleName(),
|
||||||
final String simpleQualifiedName = enumJavaClass.getSimpleName() + "." + enumConstant.name();
|
enumJavaClass.getCanonicalName(),
|
||||||
allowedEnumLiteralTexts
|
enumJavaClass.getName()
|
||||||
.computeIfAbsent( simpleQualifiedName, s -> new HashSet<>() )
|
);
|
||||||
.add( enumJavaClass.getName() );
|
enumJavaTypes.put( enumJavaClass.getName(), enumJavaType );
|
||||||
|
enumJavaTypes.put( enumJavaClass.getCanonicalName(), enumJavaType );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
@ -686,6 +663,33 @@ public class JpaMetamodelImpl implements JpaMetamodelImplementor, Serializable {
|
||||||
applyNamedEntityGraphs( namedEntityGraphDefinitions );
|
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(
|
private EntityDomainType<?> locateOrBuildEntityType(
|
||||||
PersistentClass persistentClass,
|
PersistentClass persistentClass,
|
||||||
MetadataContext context,
|
MetadataContext context,
|
||||||
|
|
|
@ -550,8 +550,8 @@ public class MappingMetamodelImpl extends QueryParameterBindingTypeResolverImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <E extends Enum<E>> E enumValue(EnumJavaType<E> enumType, String terminal) {
|
public <E extends Enum<E>> E enumValue(EnumJavaType<E> enumType, String enumValueName) {
|
||||||
return jpaMetamodel.enumValue(enumType, terminal);
|
return jpaMetamodel.enumValue( enumType, enumValueName );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -873,8 +873,8 @@ public abstract class MockSessionFactory
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override @Nullable
|
@Override
|
||||||
public Set<String> getEnumTypesForValue(String enumValue) {
|
public @Nullable Set<String> getEnumTypesForValue(String enumValue) {
|
||||||
return MockSessionFactory.this.getEnumTypesForValue(enumValue);
|
return MockSessionFactory.this.getEnumTypesForValue(enumValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -884,8 +884,7 @@ public abstract class MockSessionFactory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable Set<String> getEnumTypesForValue(String value) {
|
||||||
Set<String> getEnumTypesForValue(String value) {
|
|
||||||
return emptySet();
|
return emptySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,7 @@ import java.io.IOException;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
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.root;
|
||||||
import static org.hibernate.internal.util.StringHelper.split;
|
import static org.hibernate.internal.util.StringHelper.split;
|
||||||
import static org.hibernate.internal.util.StringHelper.unroot;
|
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;
|
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 Types typeUtil;
|
||||||
private final Filer filer;
|
private final Filer filer;
|
||||||
private final Map<String, String> entityNameMappings;
|
private final Map<String, String> entityNameMappings;
|
||||||
private final Map<String, Set<String>> enumTypesByValue;
|
private final Map<String, Set<String>> allowedEnumLiteralsToEnumTypeNames;
|
||||||
|
|
||||||
public ProcessorSessionFactory(
|
public ProcessorSessionFactory(
|
||||||
ProcessingEnvironment processingEnvironment,
|
ProcessingEnvironment processingEnvironment,
|
||||||
|
@ -100,7 +102,23 @@ public abstract class ProcessorSessionFactory extends MockSessionFactory {
|
||||||
typeUtil = processingEnvironment.getTypeUtils();
|
typeUtil = processingEnvironment.getTypeUtils();
|
||||||
filer = processingEnvironment.getFiler();
|
filer = processingEnvironment.getFiler();
|
||||||
this.entityNameMappings = entityNameMappings;
|
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
|
@Override
|
||||||
|
@ -217,7 +235,7 @@ public abstract class ProcessorSessionFactory extends MockSessionFactory {
|
||||||
|
|
||||||
@Override @Nullable
|
@Override @Nullable
|
||||||
Set<String> getEnumTypesForValue(String value) {
|
Set<String> getEnumTypesForValue(String value) {
|
||||||
Set<String> result = enumTypesByValue.get(value);
|
Set<String> result = allowedEnumLiteralsToEnumTypeNames.get( value);
|
||||||
if ( result != null ) {
|
if ( result != null ) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -631,7 +649,18 @@ public abstract class ProcessorSessionFactory extends MockSessionFactory {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
boolean isEnum(String className) {
|
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;
|
return typeElement != null && typeElement.getKind() == ElementKind.ENUM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,4 +11,7 @@ public class Book {
|
||||||
@NaturalId String author;
|
@NaturalId String author;
|
||||||
String text;
|
String text;
|
||||||
int pages;
|
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")
|
@HQL("select b\nfrom Book b\nwhere b.isbn = :isbn")
|
||||||
Book findByIsbnMultiline(String 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