HHH-17729 move validation of constructors in HQL instantiations to SemanticQueryBuilder

report SemanticExceptions with the query string
fix some warnings in ReflectHelper
This commit is contained in:
Gavin King 2024-02-11 13:40:51 +01:00
parent 0bce456e3a
commit 24937b4e67
15 changed files with 337 additions and 254 deletions

View File

@ -9,14 +9,13 @@ package org.hibernate.boot.model;
import org.hibernate.InstantiationException;
import org.hibernate.Internal;
import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.resource.beans.spi.BeanInstanceProducer;
import org.hibernate.type.spi.TypeBootstrapContext;
import java.lang.reflect.Constructor;
import java.util.Map;
import static org.hibernate.internal.util.ReflectHelper.getConstructor;
/**
* {@link BeanInstanceProducer} implementation for building beans related to custom types.
*
@ -33,7 +32,7 @@ public class TypeBeanInstanceProducer implements BeanInstanceProducer, TypeBoots
@Override
public <B> B produceBeanInstance(Class<B> beanType) {
final Constructor<B> bootstrapContextAwareConstructor =
getConstructor( beanType, TypeBootstrapContext.class );
ReflectHelper.getConstructorOrNull( beanType, TypeBootstrapContext.class );
if ( bootstrapContextAwareConstructor != null ) {
try {
return bootstrapContextAwareConstructor.newInstance( this );
@ -43,7 +42,7 @@ public class TypeBeanInstanceProducer implements BeanInstanceProducer, TypeBoots
}
}
else {
final Constructor<B> constructor = getConstructor( beanType );
final Constructor<B> constructor = ReflectHelper.getConstructorOrNull( beanType );
if ( constructor != null ) {
try {
return constructor.newInstance();

View File

@ -27,8 +27,8 @@ public class CustomOptimizerDescriptor implements OptimizerDescriptor {
return className;
}
@Override
@Override @SuppressWarnings("unchecked")
public Class<? extends Optimizer> getOptimizerClass() throws ClassNotFoundException {
return ReflectHelper.classForName( className );
return (Class<? extends Optimizer>) ReflectHelper.classForName( className );
}
}

View File

@ -42,9 +42,9 @@ import jakarta.persistence.Transient;
@SuppressWarnings("unchecked")
public final class ReflectHelper {
public static final Class[] NO_PARAM_SIGNATURE = ArrayHelper.EMPTY_CLASS_ARRAY;
public static final Class<?>[] NO_PARAM_SIGNATURE = ArrayHelper.EMPTY_CLASS_ARRAY;
public static final Class[] SINGLE_OBJECT_PARAM_SIGNATURE = new Class[] { Object.class };
public static final Class<?>[] SINGLE_OBJECT_PARAM_SIGNATURE = new Class[] { Object.class };
private static final Method OBJECT_EQUALS;
private static final Method OBJECT_HASHCODE;
@ -99,7 +99,7 @@ public final class ReflectHelper {
* @return The equals method reference
* @throws NoSuchMethodException Should indicate an attempt to extract equals method from interface.
*/
public static Method extractEqualsMethod(Class clazz) throws NoSuchMethodException {
public static Method extractEqualsMethod(Class<?> clazz) throws NoSuchMethodException {
return clazz.getMethod( "equals", SINGLE_OBJECT_PARAM_SIGNATURE );
}
@ -110,7 +110,7 @@ public final class ReflectHelper {
* @return The hashCode method reference
* @throws NoSuchMethodException Should indicate an attempt to extract hashCode method from interface.
*/
public static Method extractHashCodeMethod(Class clazz) throws NoSuchMethodException {
public static Method extractHashCodeMethod(Class<?> clazz) throws NoSuchMethodException {
return clazz.getMethod( "hashCode", NO_PARAM_SIGNATURE );
}
@ -120,7 +120,7 @@ public final class ReflectHelper {
* @param clazz The class to check
* @return True if clazz defines an equals override.
*/
public static boolean overridesEquals(Class clazz) {
public static boolean overridesEquals(Class<?> clazz) {
Method equals;
try {
equals = extractEqualsMethod( clazz );
@ -137,7 +137,7 @@ public final class ReflectHelper {
* @param clazz The class to check
* @return True if clazz defines an hashCode override.
*/
public static boolean overridesHashCode(Class clazz) {
public static boolean overridesHashCode(Class<?> clazz) {
Method hashCode;
try {
hashCode = extractHashCodeMethod( clazz );
@ -155,7 +155,7 @@ public final class ReflectHelper {
* @param intf The interface to check it against.
* @return True if the class does implement the interface, false otherwise.
*/
public static boolean implementsInterface(Class clazz, Class intf) {
public static boolean implementsInterface(Class<?> clazz, Class<?> intf) {
assert intf.isInterface() : "Interface to check was not an interface";
return intf.isAssignableFrom( clazz );
}
@ -171,7 +171,7 @@ public final class ReflectHelper {
* @return The class reference.
* @throws ClassNotFoundException From {@link Class#forName(String, boolean, ClassLoader)}.
*/
public static Class classForName(String name, Class caller) throws ClassNotFoundException {
public static Class<?> classForName(String name, Class<?> caller) throws ClassNotFoundException {
try {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if ( classLoader != null ) {
@ -198,7 +198,7 @@ public final class ReflectHelper {
* or {@link org.hibernate.boot.spi.ClassLoaderAccess} should be preferred
*/
@Deprecated
public static Class classForName(String name) throws ClassNotFoundException {
public static Class<?> classForName(String name) throws ClassNotFoundException {
try {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if ( classLoader != null ) {
@ -217,7 +217,7 @@ public final class ReflectHelper {
* @param member The member.
* @return True if the member is publicly accessible, false otherwise.
*/
public static boolean isPublic(Class clazz, Member member) {
public static boolean isPublic(Class<?> clazz, Member member) {
return Modifier.isPublic( member.getModifiers() ) && Modifier.isPublic( clazz.getModifiers() );
}
@ -232,12 +232,12 @@ public final class ReflectHelper {
*
* @throws MappingException Indicates we were unable to locate the property.
*/
public static Class reflectedPropertyClass(
public static Class<?> reflectedPropertyClass(
String className,
String name,
ClassLoaderService classLoaderService) throws MappingException {
try {
Class clazz = classLoaderService.classForName( className );
Class<?> clazz = classLoaderService.classForName( className );
return getter( clazz, name ).getReturnTypeClass();
}
catch ( ClassLoadingException e ) {
@ -266,11 +266,11 @@ public final class ReflectHelper {
* @return The type of the property.
* @throws MappingException Indicates we were unable to locate the property.
*/
public static Class reflectedPropertyClass(Class clazz, String name) throws MappingException {
public static Class<?> reflectedPropertyClass(Class<?> clazz, String name) throws MappingException {
return getter( clazz, name ).getReturnTypeClass();
}
private static Getter getter(Class clazz, String name) throws MappingException {
private static Getter getter(Class<?> clazz, String name) throws MappingException {
return PropertyAccessStrategyMixedImpl.INSTANCE.buildPropertyAccess( clazz, name, true ).getGetter();
}
@ -304,7 +304,7 @@ public final class ReflectHelper {
* @param clazz The class to check.
* @return True if the class is abstract, false otherwise.
*/
public static boolean isAbstractClass(Class clazz) {
public static boolean isAbstractClass(Class<?> clazz) {
int modifier = clazz.getModifiers();
return Modifier.isAbstract(modifier) || Modifier.isInterface(modifier);
}
@ -315,7 +315,7 @@ public final class ReflectHelper {
* @param clazz The class to check.
* @return True if the class is final, false otherwise.
*/
public static boolean isFinalClass(Class clazz) {
public static boolean isFinalClass(Class<?> clazz) {
return Modifier.isFinal( clazz.getModifiers() );
}
@ -325,15 +325,19 @@ public final class ReflectHelper {
*
* @param clazz The class needing instantiation
* @param types The types representing the required ctor param signature
* @return The matching constructor.
* @throws PropertyNotFoundException Indicates we could not locate an appropriate constructor (todo : again with PropertyNotFoundException???)
* @return The matching constructor
* @throws PropertyNotFoundException Indicates we could not locate an appropriate constructor
*
* @deprecated no longer used, since we moved away from the {@link Type} interface
*/
public static Constructor getConstructor(Class clazz, Type[] types) throws PropertyNotFoundException {
final Constructor[] candidates = clazz.getConstructors();
Constructor constructor = null;
// todo : again with PropertyNotFoundException???
@Deprecated(since = "6", forRemoval = true)
public static Constructor<?> getConstructor(Class<?> clazz, Type[] types) throws PropertyNotFoundException {
final Constructor<?>[] candidates = clazz.getConstructors();
Constructor<?> constructor = null;
int numberOfMatchingConstructors = 0;
for ( final Constructor candidate : candidates ) {
final Class[] params = candidate.getParameterTypes();
for ( final Constructor<?> candidate : candidates ) {
final Class<?>[] params = candidate.getParameterTypes();
if ( params.length == types.length ) {
boolean found = true;
for ( int j = 0; j < params.length; j++ ) {
@ -361,9 +365,18 @@ public final class ReflectHelper {
}
public static <T> Constructor<T> getConstructor(
/**
* Retrieve a constructor for the given class, with arguments matching
* the specified Java {@linkplain Class types}, or return {@code null}
* if no such constructor exists.
*
* @param clazz The class needing instantiation
* @param constructorArgs The types representing the required ctor param signature
* @return The matching constructor, or {@code null}
*/
public static <T> Constructor<T> getConstructorOrNull(
Class<T> clazz,
Class... constructorArgs) {
Class<?>... constructorArgs) {
Constructor<T> constructor = null;
try {
constructor = clazz.getDeclaredConstructor( constructorArgs );
@ -380,7 +393,7 @@ public final class ReflectHelper {
return constructor;
}
public static Method getMethod(Class clazz, Method method) {
public static Method getMethod(Class<?> clazz, Method method) {
try {
return clazz.getMethod( method.getName(), method.getParameterTypes() );
}
@ -389,7 +402,7 @@ public final class ReflectHelper {
}
}
public static Method getMethod(Class clazz, String methodName, Class... paramTypes) {
public static Method getMethod(Class<?> clazz, String methodName, Class<?>... paramTypes) {
try {
return clazz.getMethod( methodName, paramTypes );
}
@ -398,9 +411,9 @@ public final class ReflectHelper {
}
}
public static Field findField(Class containerClass, String propertyName) {
public static Field findField(Class<?> containerClass, String propertyName) {
if ( containerClass == null ) {
throw new IllegalArgumentException( "Class on which to find field [" + propertyName + "] cannot be null" );
throw new IllegalArgumentException( "Class<?> on which to find field [" + propertyName + "] cannot be null" );
}
else if ( containerClass == Object.class ) {
throw new IllegalArgumentException( "Illegal attempt to locate field [" + propertyName + "] on Object.class" );
@ -430,7 +443,7 @@ public final class ReflectHelper {
}
}
private static Field locateField(Class clazz, String propertyName) {
private static Field locateField(Class<?> clazz, String propertyName) {
if ( clazz == null || Object.class.equals( clazz ) ) {
return null;
}
@ -451,8 +464,8 @@ public final class ReflectHelper {
return field != null && ( field.getModifiers() & Modifier.STATIC ) == Modifier.STATIC;
}
public static Method findGetterMethod(Class containerClass, String propertyName) {
Class checkClass = containerClass;
public static Method findGetterMethod(Class<?> containerClass, String propertyName) {
Class<?> checkClass = containerClass;
Method getter = null;
if ( isRecord( containerClass ) ) {
@ -497,10 +510,10 @@ public final class ReflectHelper {
return getter;
}
private static Method getGetterOrNull(Class[] interfaces, String propertyName) {
private static Method getGetterOrNull(Class<?>[] interfaces, String propertyName) {
Method getter = null;
for ( int i = 0; getter == null && i < interfaces.length; ++i ) {
final Class anInterface = interfaces[i];
final Class<?> anInterface = interfaces[i];
getter = getGetterOrNull( anInterface, propertyName );
if ( getter == null ) {
// if no getter found yet, check all implemented interfaces of interface
@ -513,14 +526,14 @@ public final class ReflectHelper {
/**
* Find the method that can be used as the getter for this property.
*
* @param containerClass The Class which contains the property
* @param containerClass The Class<?> which contains the property
* @param propertyName The name of the property
*
* @return The getter method, or {@code null} if there is none.
*
* @throws MappingException If the {@code containerClass} has both a get- and an is- form.
*/
public static Method getGetterOrNull(Class containerClass, String propertyName) {
public static Method getGetterOrNull(Class<?> containerClass, String propertyName) {
if ( isRecord( containerClass ) ) {
try {
return containerClass.getMethod( propertyName, NO_PARAM_SIGNATURE );
@ -583,7 +596,7 @@ public final class ReflectHelper {
String propertyName,
Method getMethod,
String stemName) {
// verify that the Class does not also define a method with the same stem name with 'is'
// verify that the Class<?> does not also define a method with the same stem name with 'is'
try {
final Method isMethod = containerClass.getDeclaredMethod( "is" + stemName );
if ( !Modifier.isStatic( isMethod.getModifiers() ) && isMethod.getAnnotation( Transient.class ) == null ) {
@ -598,7 +611,7 @@ public final class ReflectHelper {
public static void checkGetAndIsVariants(
Class containerClass,
Class<?> containerClass,
String propertyName,
Method getMethod,
Method isMethod) {
@ -608,7 +621,7 @@ public final class ReflectHelper {
throw new MappingException(
String.format(
Locale.ROOT,
"Class '%s' declares both 'get' [%s] and 'is' [%s] variants of getter for property '%s'",
"Class<?> '%s' declares both 'get' [%s] and 'is' [%s] variants of getter for property '%s'",
containerClass.getName(),
getMethod,
isMethod,
@ -619,11 +632,11 @@ public final class ReflectHelper {
}
public static void verifyNoGetVariantExists(
Class containerClass,
Class<?> containerClass,
String propertyName,
Method isMethod,
String stemName) {
// verify that the Class does not also define a method with the same stem name with 'is'
// verify that the Class<?> does not also define a method with the same stem name with 'is'
try {
final Method getMethod = containerClass.getDeclaredMethod( "get" + stemName );
// No such method should throw the caught exception. So if we get here, there was
@ -636,7 +649,7 @@ public final class ReflectHelper {
}
}
public static Method getterMethodOrNull(Class containerJavaType, String propertyName) {
public static Method getterMethodOrNull(Class<?> containerJavaType, String propertyName) {
try {
return findGetterMethod( containerJavaType, propertyName );
}
@ -645,8 +658,8 @@ public final class ReflectHelper {
}
}
public static Method setterMethodOrNull(final Class containerClass, final String propertyName, final Class propertyType) {
Class checkClass = containerClass;
public static Method setterMethodOrNull(final Class<?> containerClass, final String propertyName, final Class<?> propertyType) {
Class<?> checkClass = containerClass;
Method setter = null;
// check containerClass, and then its super types (if any)
@ -670,8 +683,8 @@ public final class ReflectHelper {
return setter; // might be null
}
public static Method setterMethodOrNullBySetterName(final Class containerClass, final String setterName, final Class propertyType) {
Class checkClass = containerClass;
public static Method setterMethodOrNullBySetterName(final Class<?> containerClass, final String setterName, final Class<?> propertyType) {
Class<?> checkClass = containerClass;
Method setter = null;
// check containerClass, and then its super types (if any)
@ -695,10 +708,10 @@ public final class ReflectHelper {
return setter; // might be null
}
private static Method setterOrNullBySetterName(Class[] interfaces, String setterName, Class propertyType) {
private static Method setterOrNullBySetterName(Class<?>[] interfaces, String setterName, Class<?> propertyType) {
Method setter = null;
for ( int i = 0; setter == null && i < interfaces.length; ++i ) {
final Class anInterface = interfaces[i];
final Class<?> anInterface = interfaces[i];
setter = setterOrNullBySetterName( anInterface, setterName, propertyType );
if ( setter == null ) {
// if no setter found yet, check all implemented interfaces of interface
@ -708,7 +721,7 @@ public final class ReflectHelper {
return setter;
}
private static Method setterOrNullBySetterName(Class theClass, String setterName, Class propertyType) {
private static Method setterOrNullBySetterName(Class<?> theClass, String setterName, Class<?> propertyType) {
Method potentialSetter = null;
for ( Method method : theClass.getDeclaredMethods() ) {
@ -724,7 +737,7 @@ public final class ReflectHelper {
return potentialSetter;
}
public static Method findSetterMethod(final Class containerClass, final String propertyName, final Class propertyType) {
public static Method findSetterMethod(final Class<?> containerClass, final String propertyName, final Class<?> propertyType) {
final Method setter = setterMethodOrNull( containerClass, propertyName, propertyType );
if ( setter == null ) {
throw new PropertyNotFoundException(
@ -739,10 +752,10 @@ public final class ReflectHelper {
return setter;
}
private static Method setterOrNull(Class[] interfaces, String propertyName, Class propertyType) {
private static Method setterOrNull(Class<?>[] interfaces, String propertyName, Class<?> propertyType) {
Method setter = null;
for ( int i = 0; setter == null && i < interfaces.length; ++i ) {
final Class anInterface = interfaces[i];
final Class<?> anInterface = interfaces[i];
setter = setterOrNull( anInterface, propertyName, propertyType );
if ( setter == null ) {
// if no setter found yet, check all implemented interfaces of interface
@ -752,7 +765,7 @@ public final class ReflectHelper {
return setter;
}
private static Method setterOrNull(Class theClass, String propertyName, Class propertyType) {
private static Method setterOrNull(Class<?> theClass, String propertyName, Class<?> propertyType) {
Method potentialSetter = null;
for ( Method method : theClass.getDeclaredMethods() ) {
@ -775,7 +788,7 @@ public final class ReflectHelper {
/**
* Similar to {@link #getterMethodOrNull}, except that here we are just looking for the
* corresponding getter for a field (defined as field access) if one exists.
*
* <p>
* We do not look at supers, although conceivably the super could declare the method
* as an abstract - but again, that is such an edge case...
*/
@ -876,7 +889,7 @@ public final class ReflectHelper {
return (Class<T>) ( (ParameterizedType) type ).getRawType();
}
else if ( type instanceof TypeVariable ) {
return getClass( ( (TypeVariable) type ).getBounds()[0] );
return getClass( ( (TypeVariable<?>) type ).getBounds()[0] );
}
else if ( type instanceof WildcardType ) {
return getClass( ( (WildcardType) type ).getUpperBounds()[0] );

View File

@ -25,7 +25,7 @@ public class EmbeddableInstantiatorRecordStandard extends AbstractPojoInstantiat
super( javaType );
final Class<?>[] componentTypes = ReflectHelper.getRecordComponentTypes( javaType );
this.constructor = ReflectHelper.getConstructor( javaType, componentTypes );
this.constructor = ReflectHelper.getConstructorOrNull( javaType, componentTypes );
}
@Override

View File

@ -24,7 +24,6 @@ import java.time.ZonedDateTime;
import java.time.temporal.TemporalAccessor;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
@ -88,7 +87,6 @@ import org.hibernate.query.sqm.ParsingException;
import org.hibernate.query.sqm.SetOperator;
import org.hibernate.query.sqm.SqmExpressible;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.SqmQuerySource;
import org.hibernate.query.sqm.SqmTreeCreationLogger;
import org.hibernate.query.sqm.StrictJpaComplianceViolation;
import org.hibernate.query.sqm.TemporalUnit;
@ -108,7 +106,6 @@ import org.hibernate.query.sqm.produce.function.FunctionArgumentException;
import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers;
import org.hibernate.query.sqm.spi.ParameterDeclarationContext;
import org.hibernate.query.sqm.spi.SqmCreationContext;
import org.hibernate.query.sqm.tree.AbstractSqmDmlStatement;
import org.hibernate.query.sqm.tree.SqmJoinType;
import org.hibernate.query.sqm.tree.SqmQuery;
import org.hibernate.query.sqm.tree.SqmStatement;
@ -214,9 +211,7 @@ import org.hibernate.query.sqm.tree.select.SqmSelection;
import org.hibernate.query.sqm.tree.select.SqmSortSpecification;
import org.hibernate.query.sqm.tree.select.SqmSubQuery;
import org.hibernate.query.sqm.tree.update.SqmAssignment;
import org.hibernate.query.sqm.tree.update.SqmSetClause;
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
import org.hibernate.sql.ast.tree.cte.CteMaterialization;
import org.hibernate.sql.ast.tree.cte.CteSearchClauseKind;
@ -316,14 +311,17 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
HqlParser.StatementContext hqlParseTree,
Class<R> expectedResultType,
SqmCreationOptions creationOptions,
SqmCreationContext creationContext) {
return new SemanticQueryBuilder<>( expectedResultType, creationOptions, creationContext ).visitStatement( hqlParseTree );
SqmCreationContext creationContext,
String query) {
return new SemanticQueryBuilder<>( expectedResultType, creationOptions, creationContext, query )
.visitStatement( hqlParseTree );
}
private final Class<R> expectedResultType;
private final String expectedResultEntity;
private final SqmCreationOptions creationOptions;
private final SqmCreationContext creationContext;
private final String query;
private final Stack<DotIdentifierConsumer> dotIdentifierConsumerStack;
@ -347,26 +345,30 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
public SemanticQueryBuilder(
Class<R> expectedResultType,
SqmCreationOptions creationOptions,
SqmCreationContext creationContext) {
this( expectedResultType, null, creationOptions, creationContext );
SqmCreationContext creationContext,
String query) {
this( expectedResultType, null, creationOptions, creationContext, query );
}
public SemanticQueryBuilder(
String expectedResultEntity,
SqmCreationOptions creationOptions,
SqmCreationContext creationContext) {
this( null, expectedResultEntity, creationOptions, creationContext );
SqmCreationContext creationContext,
String query) {
this( null, expectedResultEntity, creationOptions, creationContext, query );
}
private SemanticQueryBuilder(
Class<R> expectedResultType,
String expectedResultEntity,
SqmCreationOptions creationOptions,
SqmCreationContext creationContext) {
SqmCreationContext creationContext,
String query) {
this.expectedResultType = expectedResultType;
this.expectedResultEntity = expectedResultEntity;
this.creationOptions = creationOptions;
this.creationContext = creationContext;
this.query = query;
this.dotIdentifierConsumerStack = new StandardStack<>(
DotIdentifierConsumer.class,
new BasicDotIdentifierConsumer( this )
@ -476,10 +478,9 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
final SqmRoot<R> root = visitTargetEntity( dmlTargetContext );
if ( root.getModel() instanceof SqmPolymorphicRootDescriptor<?> ) {
throw new SemanticException(
String.format(
"Target type '%s' in 'insert' statement is not an entity",
root.getModel().getHibernateEntityName()
)
"Target type '" + root.getModel().getHibernateEntityName()
+ "' in 'insert' statement is not an entity",
query
);
}
@ -587,6 +588,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
return null;
}
final SqmCreationProcessingState processingState = processingStateStack.getCurrent();
@SuppressWarnings("unchecked")
final SqmInsertStatement<R> statement = (SqmInsertStatement<R>) processingState.getProcessingQuery();
final SqmConflictClause<R> conflictClause = new SqmConflictClause<>( statement );
final HqlParser.ConflictTargetContext conflictTargetContext = ctx.conflictTarget();
@ -869,7 +871,8 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
"Cycle attribute '%s' not found in the CTE %s",
attributeName,
cteDefinition.getName()
)
),
query
);
}
cycleAttributes.add( attribute );
@ -929,7 +932,8 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
"Search attribute '%s' not found in the CTE %s",
attributeName,
cteDefinition.getName()
)
),
query
);
}
SortDirection sortOrder = SortDirection.ASCENDING;
@ -946,7 +950,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
sortOrder = SortDirection.DESCENDING;
break;
default:
throw new SemanticException( "Unrecognized sort ordering: " + sortCtx.getText() );
throw new UnsupportedOperationException( "Unrecognized sort ordering: " + sortCtx.getText() );
}
index++;
}
@ -960,7 +964,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
nullPrecedence = NullPrecedence.LAST;
break;
default:
throw new SemanticException( "Unrecognized null precedence: " + nullsPrecedenceContext.getText() );
throw new UnsupportedOperationException( "Unrecognized null precedence: " + nullsPrecedenceContext.getText() );
}
}
}
@ -1169,7 +1173,8 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
if ( processingStateStack.depth() > 1 && orderByClause == null ) {
throw new SemanticException(
"A 'limit', 'offset', or 'fetch' clause requires an 'order by' clause when used in a subquery"
"A 'limit', 'offset', or 'fetch' clause requires an 'order by' clause when used in a subquery",
query
);
}
@ -1191,7 +1196,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
sqmQueryPart.setFetchExpression( (SqmExpression<? extends Number>) visitLimitClause(limitClauseContext) );
}
else {
throw new SemanticException("The 'limit' and 'fetch' clauses may not be used together" );
throw new SemanticException("The 'limit' and 'fetch' clauses may not be used together", query );
}
}
@ -1258,16 +1263,16 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
if ( expectedResultEntity != null ) {
final EntityDomainType<R> entityDescriptor = jpaMetamodel.entity( expectedResultEntity );
if ( entityDescriptor == null ) {
throw new SemanticException("Query has no 'from' clause, and the result type '"
+ expectedResultEntity + "' is not an entity type");
throw new SemanticException( "Query has no 'from' clause, and the result type '"
+ expectedResultEntity + "' is not an entity type", query );
}
return entityDescriptor;
}
else if ( expectedResultType != null ) {
final EntityDomainType<R> entityDescriptor = jpaMetamodel.findEntityType( expectedResultType );
if ( entityDescriptor == null ) {
throw new SemanticException("Query has no 'from' clause, and the result type '"
+ expectedResultType.getSimpleName() + "' is not an entity type");
throw new SemanticException( "Query has no 'from' clause, and the result type '"
+ expectedResultType.getSimpleName() + "' is not an entity type", query );
}
return entityDescriptor;
}
@ -1286,7 +1291,8 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
if ( fromClause.getNumberOfRoots() == 0 ) {
throw new SemanticException( "query has no 'select' clause, and no root entities"
+ " (every selection query must have an explicit 'select', an explicit 'from', or an explicit entity result type)");
+ " (every selection query must have an explicit 'select', an explicit 'from', or an explicit entity result type)",
query );
}
final NodeBuilder nodeBuilder = creationContext.getNodeBuilder();
@ -1309,13 +1315,15 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
if ( fromClause.getNumberOfRoots() > 1 ) {
// multiple root entities
throw new SemanticException( "Query has no 'select' clause, and multiple root entities, but query result type is an entity class"
+ " (specify an explicit 'select' list, or a different result type, for example, 'Object[].class')");
+ " (specify an explicit 'select' list, or a different result type, for example, 'Object[].class')",
query );
}
else {
final SqmRoot<?> sqmRoot = fromClause.getRoots().get(0);
if ( sqmRoot instanceof SqmCteRoot ) {
throw new SemanticException( "Query has no 'select' clause, and the 'from' clause refers to a CTE, but query result type is an entity class"
+ " (specify an explicit 'select' list)");
+ " (specify an explicit 'select' list)",
query );
}
else {
// exactly one root entity, return it
@ -1429,7 +1437,8 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
);
}
catch (ClassLoadingException e) {
throw new SemanticException( "Could not resolve class '" + className + "' named for instantiation" );
throw new SemanticException( "Could not resolve class '" + className + "' named for instantiation",
query );
}
}
else {
@ -1456,6 +1465,12 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
dynamicInstantiation.addArgument( visitInstantiationArgument( arg ) );
}
if ( !dynamicInstantiation.checkInstantiation( creationContext.getTypeConfiguration() ) ) {
throw new SemanticException( "No matching constructor for type '"
+ dynamicInstantiation.getJavaType().getSimpleName() + "'",
query );
}
return dynamicInstantiation;
}
@ -1468,20 +1483,16 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
}
private Class<?> classForName(String className) {
return creationContext.getServiceRegistry().getService( ClassLoaderService.class ).classForName( className );
return creationContext.getServiceRegistry().requireService( ClassLoaderService.class ).classForName( className );
}
@Override
public SqmDynamicInstantiationArgument<?> visitInstantiationArgument(HqlParser.InstantiationArgumentContext ctx) {
final String alias;
if ( ctx.getChildCount() > 1 ) {
alias = extractAlias( (HqlParser.VariableContext) ctx.getChild( ctx.getChildCount() - 1 ) );
}
else {
alias = null;
}
final HqlParser.VariableContext variable = ctx.variable();
final String alias = variable == null ? null : extractAlias( variable );
final SqmSelectableNode<?> argExpression = (SqmSelectableNode<?>) ctx.getChild( 0 ).accept( this );
final SqmSelectableNode<?> argExpression =
(SqmSelectableNode<?>) ctx.instantiationArgumentExpression().accept( this );
final SqmDynamicInstantiationArgument<?> argument = new SqmDynamicInstantiationArgument<>(
argExpression,
@ -1504,7 +1515,9 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
true
);
if ( sqmFromByAlias == null ) {
throw new SemanticException( "Could not resolve alias '" + alias + "' in selection [" + ctx.getText() + "]" );
throw new SemanticException( "Could not resolve alias '" + alias
+ "' in selection [" + ctx.getText() + "]",
query );
}
return sqmFromByAlias;
}
@ -1563,7 +1576,9 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
nodeByPosition = processingState.getPathRegistry().findAliasedNodeByPosition( position );
}
if ( nodeByPosition == null ) {
throw new SemanticException( "Numeric literal '" + position + "' used in 'group by' does not match a registered select item" );
throw new SemanticException( "Numeric literal '" + position
+ "' used in 'group by' does not match a registered select item",
query );
}
return new SqmAliasedNodeRef( position, integerDomainType, nodeBuilder);
@ -1701,7 +1716,8 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
allowPositionalOrAliases
);
if ( sortExpression == null ) {
throw new SemanticException( "Could not resolve sort expression: '" + ctx.sortExpression().getText() + "'" );
throw new SemanticException( "Could not resolve sort expression: '" + ctx.sortExpression().getText() + "'",
query );
}
if ( sortExpression instanceof SqmLiteral || sortExpression instanceof SqmParameter ) {
HqlLogging.QUERY_LOGGER.debugf( "Questionable sorting by constant value : %s", sortExpression );
@ -2014,7 +2030,8 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
if ( processingStateStack.depth() > 1 ) {
throw new SemanticException(
"Implicitly-polymorphic domain path in subquery '" + entityDescriptor.getName() +"'"
"Implicitly-polymorphic domain path in subquery '" + entityDescriptor.getName() + "'",
query
);
}
}
@ -2060,7 +2077,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
);
return ((SqmCorrelation<?, ?>) correlation).getCorrelatedRoot();
}
throw new SemanticException( "Could not resolve entity or correlation path '" + name + "'" );
throw new SemanticException( "Could not resolve entity or correlation path '" + name + "'", query );
}
final SqmCteStatement<?> cteStatement = findCteStatement( name );
if ( cteStatement != null ) {
@ -2131,7 +2148,8 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
.resolveHqlEntityReference( name );
if ( entityDescriptor instanceof SqmPolymorphicRootDescriptor ) {
throw new SemanticException( "Unmapped polymorphic reference cannot be used as a target of 'cross join'" );
throw new SemanticException( "Unmapped polymorphic reference cannot be used as a target of 'cross join'",
query );
}
final SqmCrossJoin<T> join = new SqmCrossJoin<>(
entityDescriptor,
@ -2157,7 +2175,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
final boolean fetch = parserJoin.FETCH() != null;
if ( fetch && processingStateStack.depth() > 1 ) {
throw new SemanticException( "The 'from' clause of a subquery has a 'fetch'" );
throw new SemanticException( "The 'from' clause of a subquery has a 'fetch'", query );
}
dotIdentifierConsumerStack.push( new QualifiedJoinPathConsumer( sqmRoot, joinType, fetch, alias, this ) );
@ -2178,7 +2196,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
}
}
if ( joinRestrictionContext != null && attributeJoin.isFetched() ) {
throw new SemanticException( "Fetch join has a 'with' clause (use a filter instead)" );
throw new SemanticException( "Fetch join has a 'with' clause (use a filter instead)", query );
}
}
@ -2222,7 +2240,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
}
else if ( joinTargetContext instanceof HqlParser.JoinSubqueryContext ) {
if ( fetch ) {
throw new SemanticException( "The 'from' clause of a subquery has a 'fetch' join" );
throw new SemanticException( "The 'from' clause of a subquery has a 'fetch' join", query );
}
if ( getCreationOptions().useStrictJpaCompliance() ) {
throw new StrictJpaComplianceViolation(
@ -2397,7 +2415,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
);
}
else {
throw new SemanticException( "Operand of 'is empty' operator must be a plural path" );
throw new SemanticException( "Operand of 'is empty' operator must be a plural path", query );
}
}
@ -2598,7 +2616,8 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
final String escape = unquoteStringLiteral( terminalNode.getText() );
if ( escape.length() != 1 ) {
throw new SemanticException(
"Escape character literals must have exactly a single character, but found: " + escape
"Escape character literals must have exactly a single character, but found: " + escape,
query
);
}
return new SqmLiteral<>(
@ -2622,7 +2641,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
);
}
else {
throw new SemanticException( "Operand of 'member of' operator must be a plural path" );
throw new SemanticException( "Operand of 'member of' operator must be a plural path", query );
}
}
@ -2740,7 +2759,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
public SqmPredicate visitBooleanExpressionPredicate(HqlParser.BooleanExpressionPredicateContext ctx) {
final SqmExpression<?> expression = (SqmExpression<?>) ctx.expression().accept( this );
if ( expression.getJavaType() != Boolean.class ) {
throw new SemanticException( "Non-boolean expression used in predicate context: " + ctx.getText() );
throw new SemanticException( "Non-boolean expression used in predicate context: " + ctx.getText(), query );
}
@SuppressWarnings("unchecked")
final SqmExpression<Boolean> booleanExpression = (SqmExpression<Boolean>) expression;
@ -3971,20 +3990,30 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
else {
final FunctionKind functionKind = functionTemplate.getFunctionKind();
if ( ctx.filterClause() != null && functionKind == FunctionKind.NORMAL ) {
throw new SemanticException( "'FILTER' clause is illegal for non-aggregate function: " + functionName );
throw new SemanticException( "'FILTER' clause is illegal for non-aggregate function: "
+ functionName,
query );
}
if ( ctx.overClause() != null && functionKind == FunctionKind.NORMAL ) {
throw new SemanticException( "'OVER' clause is illegal for non-aggregate function: " + functionName);
throw new SemanticException( "'OVER' clause is illegal for non-aggregate function: "
+ functionName,
query );
}
if ( ctx.withinGroupClause() != null && functionKind == FunctionKind.NORMAL ) {
throw new SemanticException( "'WITHIN' GROUP clause is illegal for non-aggregate function: " + functionName);
throw new SemanticException( "'WITHIN' GROUP clause is illegal for non-aggregate function: "
+ functionName,
query );
}
if ( ctx.overClause() == null && functionKind == FunctionKind.WINDOW ) {
throw new SemanticException( "'OVER' clause is mandatory for window-only function: " + functionName );
throw new SemanticException( "'OVER' clause is mandatory for window-only function: "
+ functionName,
query );
}
if ( ctx.withinGroupClause() == null && ctx.overClause() == null
&& functionKind == FunctionKind.ORDERED_SET_AGGREGATE ) {
throw new SemanticException( "'WITHIN GROUP' or 'OVER' clause is mandatory for ordered set aggregate function: " + functionName );
throw new SemanticException( "'WITHIN GROUP' or 'OVER' clause is mandatory for ordered set aggregate function: "
+ functionName,
query );
}
if ( ctx.nullsClause() != null ) {
@ -3996,11 +4025,15 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
case "nth_value":
break;
default:
throw new SemanticException( "'RESPECT NULLS' or 'IGNORE NULLS' are illegal for function: " + functionName );
throw new SemanticException( "'RESPECT NULLS' or 'IGNORE NULLS' are illegal for function: "
+ functionName,
query );
}
}
if ( ctx.nthSideClause() != null && !"nth_value".equals( functionName ) ) {
throw new SemanticException( "'FROM FIRST' or 'FROM LAST' are illegal for function: " + functionName );
throw new SemanticException( "'FROM FIRST' or 'FROM LAST' are illegal for function: "
+ functionName,
query );
}
return functionTemplate;
}
@ -4049,7 +4082,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
final SqmFunctionDescriptor functionTemplate = getFunctionDescriptor( "listagg" );
if ( functionTemplate == null ) {
throw new SemanticException( "The listagg() function was not registered for the dialect" );
throw new SemanticException( "The listagg() function was not registered for the dialect", query );
}
return applyOverClause(
@ -4510,8 +4543,8 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
if ( !(referencedPathSource instanceof PluralPersistentAttribute ) ) {
//TODO: improve this message
throw new SemanticException( "Path is not a plural path '"
+ pluralAttributePath.getNavigablePath() + "'" );
throw new SemanticException( "Path is not a plural path '" + pluralAttributePath.getNavigablePath() + "'",
query );
}
final SqmSubQuery<?> subQuery = new SqmSubQuery<>(
processingStateStack.getCurrent().getProcessingQuery(),
@ -4768,7 +4801,9 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
final String padCharText = ctx.STRING_LITERAL().getText();
if ( padCharText.length() != 3 ) {
throw new SemanticException( "Pad character for pad() function must be single character, found '" + padCharText + "'" );
throw new SemanticException( "Pad character for pad() function must be single character, found '"
+ padCharText + "'",
query );
}
return new SqmLiteral<>(
@ -4831,7 +4866,9 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
else {
trimCharText = unquoteStringLiteral( ctx.getText() );
if ( trimCharText.length() != 1 ) {
throw new SemanticException( "Trim character for trim() function must be single character, found '" + trimCharText + "'" );
throw new SemanticException( "Trim character for trim() function must be single character, found '"
+ trimCharText + "'",
query );
}
}
}
@ -5145,7 +5182,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
final String treatTargetEntityName =
getCreationContext().getJpaMetamodel().qualifyImportableName( treatTargetName );
if ( treatTargetEntityName == null ) {
throw new SemanticException( "Could not resolve treat target type '" + treatTargetName + "'" );
throw new SemanticException( "Could not resolve treat target type '" + treatTargetName + "'", query );
}
final boolean hasContinuation = ctx.getChildCount() == 7;

View File

@ -72,7 +72,8 @@ public class StandardHqlTranslator implements HqlTranslator {
hqlParseTree,
expectedResultType,
sqmCreationOptions,
sqmCreationContext
sqmCreationContext,
query
);
// Log the SQM tree (if enabled)

View File

@ -9,8 +9,6 @@ package org.hibernate.query.sqm.tree.expression;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.hibernate.type.descriptor.java.JavaType;
/**
* @author Steve Ebersole
*/
@ -18,8 +16,8 @@ public class Compatibility {
private Compatibility() {
}
private static final Map<Class,Class> primitiveToWrapper;
private static final Map<Class,Class> wrapperToPrimitive;
private static final Map<Class<?>,Class<?>> primitiveToWrapper;
private static final Map<Class<?>,Class<?>> wrapperToPrimitive;
static {
primitiveToWrapper = new ConcurrentHashMap<>();
wrapperToPrimitive = new ConcurrentHashMap<>();
@ -33,27 +31,26 @@ public class Compatibility {
map( double.class, Double.class );
}
private static void map(Class primitive, Class wrapper) {
private static void map(Class<?> primitive, Class<?> wrapper) {
primitiveToWrapper.put( primitive, wrapper );
wrapperToPrimitive.put( wrapper, primitive );
}
public static boolean isWrapper(Class potentialWrapper) {
public static boolean isWrapper(Class<?> potentialWrapper) {
return wrapperToPrimitive.containsKey( potentialWrapper );
}
public static Class primitiveEquivalent(Class potentialWrapper) {
public static Class<?> primitiveEquivalent(Class<?> potentialWrapper) {
assert isWrapper( potentialWrapper );
return wrapperToPrimitive.get( potentialWrapper );
}
public static Class wrapperEquivalent(Class primitive) {
public static Class<?> wrapperEquivalent(Class<?> primitive) {
assert primitive.isPrimitive();
return primitiveToWrapper.get( primitive );
}
@SuppressWarnings("unchecked")
public static boolean areAssignmentCompatible(Class to, Class from) {
public static boolean areAssignmentCompatible(Class<?> to, Class<?> from) {
assert to != null;
assert from != null;
@ -79,7 +76,7 @@ public class Compatibility {
return false;
}
private static boolean areAssignmentCompatiblePrimitive(Class to, Class from) {
private static boolean areAssignmentCompatiblePrimitive(Class<?> to, Class<?> from) {
assert to != null;
assert from != null;
@ -112,7 +109,7 @@ public class Compatibility {
return false;
}
public static boolean isIntegralType(Class potentialIntegral) {
public static boolean isIntegralType(Class<?> potentialIntegral) {
if ( potentialIntegral.isPrimitive() ) {
return isIntegralTypePrimitive( potentialIntegral );
}
@ -122,7 +119,7 @@ public class Compatibility {
}
private static boolean isIntegralTypePrimitive(Class potentialIntegral) {
private static boolean isIntegralTypePrimitive(Class<?> potentialIntegral) {
assert potentialIntegral.isPrimitive();
return potentialIntegral == short.class
@ -130,7 +127,7 @@ public class Compatibility {
|| potentialIntegral == long.class;
}
private static boolean isCompatibleIntegralTypePrimitive(Class to, Class from) {
private static boolean isCompatibleIntegralTypePrimitive(Class<?> to, Class<?> from) {
assert isIntegralTypePrimitive( to );
assert from.isPrimitive();
@ -146,7 +143,7 @@ public class Compatibility {
}
}
public static boolean isFloatingType(Class potentialFloating) {
public static boolean isFloatingType(Class<?> potentialFloating) {
if ( potentialFloating.isPrimitive() ) {
return isFloatingTypePrimitive( potentialFloating );
}
@ -156,33 +153,17 @@ public class Compatibility {
}
private static boolean isFloatingTypePrimitive(Class potentialFloating) {
private static boolean isFloatingTypePrimitive(Class<?> potentialFloating) {
assert potentialFloating.isPrimitive();
return potentialFloating == float.class
|| potentialFloating == double.class;
}
private static boolean isCompatibleFloatingTypePrimitive(Class to, Class from) {
private static boolean isCompatibleFloatingTypePrimitive(Class<?> to, Class<?> from) {
assert isFloatingTypePrimitive( to );
assert from.isPrimitive();
if ( to == float.class ) {
return from == float.class;
}
else {
return isFloatingTypePrimitive( from );
}
}
public static boolean areAssignmentCompatible(
JavaType to,
JavaType from) {
// todo (6.0) - base this in the descriptor.
// `JavaType#assignableFrom` ?
// Note from Christian: I think this is a good idea to allow honoring parameterized types
return areAssignmentCompatible( to.getJavaTypeClass(), from.getJavaTypeClass() );
return to == float.class ? from == float.class : isFloatingTypePrimitive( from );
}
}

View File

@ -6,6 +6,7 @@
*/
package org.hibernate.query.sqm.tree.select;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@ -22,11 +23,14 @@ import org.hibernate.query.sqm.tree.expression.SqmExpression;
import org.hibernate.query.sqm.tree.jpa.AbstractJpaSelection;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.spi.TypeConfiguration;
import org.jboss.logging.Logger;
import static java.util.stream.Collectors.toList;
import static org.hibernate.query.sqm.DynamicInstantiationNature.CLASS;
import static org.hibernate.query.sqm.DynamicInstantiationNature.LIST;
import static org.hibernate.query.sqm.DynamicInstantiationNature.MAP;
import static org.hibernate.sql.results.graph.instantiation.internal.InstantiationHelper.isConstructorCompatible;
/**
* Represents a dynamic instantiation ({@code select new XYZ(...) ...}) as part of the SQM.
@ -111,6 +115,30 @@ public class SqmDynamicInstantiation<T>
this.arguments = arguments;
}
public boolean checkInstantiation(TypeConfiguration typeConfiguration) {
if ( getInstantiationTarget().getNature() == CLASS) {
if ( getArguments().stream().allMatch(arg -> arg.getAlias() != null ) ) {
// it's probably a bean injection-type instantiator, don't check it now
return true;
}
else {
final List<Class<?>> argTypes =
getArguments().stream()
.map(arg -> arg.getNodeJavaType().getJavaTypeClass())
.collect(toList());
for ( Constructor<?> constructor : getJavaType().getDeclaredConstructors() ) {
if ( isConstructorCompatible( constructor, argTypes, typeConfiguration ) ) {
return true;
}
}
return false;
}
}
else {
return true;
}
}
@Override
public SqmDynamicInstantiation<T> copy(SqmCopyContext context) {
final SqmDynamicInstantiation<T> existing = context.getCopy( this );

View File

@ -7,7 +7,6 @@
package org.hibernate.sql.results.graph.instantiation.internal;
import java.lang.reflect.Constructor;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashSet;
@ -23,10 +22,11 @@ import org.hibernate.sql.results.graph.FetchParentAccess;
import org.hibernate.sql.results.graph.instantiation.DynamicInstantiationResult;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.spi.TypeConfiguration;
import org.jboss.logging.Logger;
import static java.util.stream.Collectors.toList;
import static org.hibernate.query.sqm.tree.expression.Compatibility.areAssignmentCompatible;
import static org.hibernate.sql.results.graph.instantiation.internal.InstantiationHelper.isConstructorCompatible;
/**
* @author Steve Ebersole
@ -150,49 +150,30 @@ public class DynamicInstantiationResultImpl<R> implements DynamicInstantiationRe
new DynamicInstantiationAssemblerMapImpl( (JavaType<Map<?,?>>) javaType, argumentReaders );
}
else {
// find a constructor matching argument types
constructor_loop:
for ( Constructor<?> constructor : javaType.getJavaTypeClass().getDeclaredConstructors() ) {
final Type[] genericParameterTypes = constructor.getGenericParameterTypes();
if ( genericParameterTypes.length == argumentReaders.size() ) {
for ( int i = 0; i < argumentReaders.size(); i++ ) {
final Type parameterType = genericParameterTypes[i];
final ArgumentReader<?> argumentReader = argumentReaders.get( i );
final boolean assignmentCompatible;
if ( parameterType instanceof Class<?> ) {
assignmentCompatible = areAssignmentCompatible(
(Class<?>) parameterType,
argumentReader.getAssembledJavaType().getJavaTypeClass()
);
return assembler( areAllArgumentsAliased, duplicatedAliases, argumentReaders, creationState );
}
else {
final JavaType<?> argumentTypeDescriptor = creationState.getSqlAstCreationContext()
}
private DomainResultAssembler<R> assembler(
boolean areAllArgumentsAliased,
List<String> duplicatedAliases,
List<ArgumentReader<?>> argumentReaders,
AssemblerCreationState creationState) {
final List<Class<?>> argumentTypes =
argumentReaders.stream()
.map(reader -> reader.getAssembledJavaType().getJavaTypeClass())
.collect(toList());
final TypeConfiguration typeConfiguration =
creationState.getSqlAstCreationContext()
.getMappingMetamodel()
.getTypeConfiguration()
.getJavaTypeRegistry()
.resolveDescriptor( parameterType );
assignmentCompatible = areAssignmentCompatible(
argumentTypeDescriptor,
argumentReader.getAssembledJavaType()
);
}
if ( !assignmentCompatible ) {
if ( log.isDebugEnabled() ) {
log.debugf(
"Skipping constructor for dynamic-instantiation match due to argument mismatch [%s] : %s -> %s",
i,
argumentReader.getAssembledJavaType().getTypeName(),
parameterType.getTypeName()
);
}
continue constructor_loop;
}
}
.getTypeConfiguration();
// find a constructor matching argument types
for ( Constructor<?> constructor : javaType.getJavaTypeClass().getDeclaredConstructors() ) {
if ( isConstructorCompatible( constructor, argumentTypes, typeConfiguration ) ) {
constructor.setAccessible( true );
//noinspection rawtypes
return new DynamicInstantiationAssemblerConstructorImpl( constructor, javaType, argumentReaders );
@SuppressWarnings("unchecked")
final Constructor<R> construct = (Constructor<R>) constructor;
return new DynamicInstantiationAssemblerConstructorImpl<>( construct, javaType, argumentReaders );
}
}
@ -203,7 +184,7 @@ public class DynamicInstantiationResultImpl<R> implements DynamicInstantiationRe
);
}
if ( ! areAllArgumentsAliased ) {
if ( !areAllArgumentsAliased) {
throw new IllegalStateException(
"Cannot instantiate class '" + javaType.getJavaType().getTypeName() + "'"
+ " (it has no constructor with signature " + signature()
@ -215,13 +196,12 @@ public class DynamicInstantiationResultImpl<R> implements DynamicInstantiationRe
"Cannot instantiate class '" + javaType.getJavaType().getTypeName() + "'"
+ " (it has no constructor with signature " + signature()
+ ", and has arguments with duplicate aliases ["
+ StringHelper.join( ",", duplicatedAliases ) + "])"
+ StringHelper.join( ",", duplicatedAliases) + "])"
);
}
return new DynamicInstantiationAssemblerInjectionImpl<>( javaType, argumentReaders );
}
}
private List<String> signature() {
return argumentResults.stream()

View File

@ -6,12 +6,56 @@
*/
package org.hibernate.sql.results.graph.instantiation.internal;
import org.hibernate.type.spi.TypeConfiguration;
import org.jboss.logging.Logger;
import java.lang.reflect.Constructor;
import java.lang.reflect.Type;
import java.util.List;
import static org.hibernate.query.sqm.tree.expression.Compatibility.areAssignmentCompatible;
/**
* @author Steve Ebersole
* @author Gavin King
*/
public class InstantiationHelper {
private static final Logger log = Logger.getLogger( InstantiationHelper.class );
private InstantiationHelper() {
// disallow direct instantiation
}
public static boolean isConstructorCompatible(
Constructor<?> constructor,
List<Class<?>> argumentTypes,
TypeConfiguration typeConfiguration) {
final Type[] genericParameterTypes = constructor.getGenericParameterTypes();
if ( genericParameterTypes.length == argumentTypes.size() ) {
for (int i = 0; i < argumentTypes.size(); i++ ) {
final Type parameterType = genericParameterTypes[i];
final Class<?> argumentType = argumentTypes.get( i );
final Class<?> argType = parameterType instanceof Class<?>
? (Class<?>) parameterType
: typeConfiguration.getJavaTypeRegistry().resolveDescriptor( parameterType ).getJavaTypeClass();
if ( !areAssignmentCompatible( argType, argumentType ) ) {
if ( log.isDebugEnabled() ) {
log.debugf(
"Skipping constructor for dynamic-instantiation match due to argument mismatch [%s] : %s -> %s",
i,
argumentType.getTypeName(),
parameterType.getTypeName()
);
}
return false;
}
}
return true;
}
else {
return false;
}
}
}

View File

@ -115,7 +115,7 @@ public class EnumType<T extends Enum<T>>
if ( parameters.containsKey( ENUM ) ) {
final String enumClassName = (String) parameters.get( ENUM );
try {
enumClass = ReflectHelper.classForName( enumClassName, this.getClass() ).asSubclass( Enum.class );
enumClass = (Class<T>) ReflectHelper.classForName( enumClassName, this.getClass() ).asSubclass( Enum.class );
}
catch ( ClassNotFoundException exception ) {
throw new HibernateException("Enum class not found: " + enumClassName, exception);

View File

@ -83,7 +83,7 @@ public class SerializableToBlobType<T extends Serializable> implements BasicType
}
try {
@SuppressWarnings("unchecked")
Class<T> classForName = ReflectHelper.classForName(className);
Class<T> classForName = (Class<T>) ReflectHelper.classForName(className);
setJavaTypeDescriptor( new SerializableJavaType<>(classForName) );
}
catch ( ClassNotFoundException e ) {

View File

@ -3071,7 +3071,8 @@ public abstract class AbstractQueryCacheResultTransformerTest {
private Constructor getConstructor() {
Type studentNametype =
scope.getSessionFactory().getMappingMetamodel().getEntityDescriptor( Student.class.getName() )
scope.getSessionFactory().getMappingMetamodel()
.getEntityDescriptor( Student.class.getName() )
.getPropertyType( "name" );
return ReflectHelper.getConstructor(
Student.class,

View File

@ -10,7 +10,6 @@ import java.lang.reflect.Constructor;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import org.hibernate.HibernateException;
@ -42,7 +41,7 @@ public final class DialectContext {
final Constructor<? extends Dialect> constructor;
try {
@SuppressWarnings("unchecked")
final Class<? extends Dialect> dialectClass = ReflectHelper.classForName( dialectName );
final Class<? extends Dialect> dialectClass = (Class<? extends Dialect>) ReflectHelper.classForName( dialectName );
constructor = dialectClass.getConstructor( DialectResolutionInfo.class );
}
catch (ClassNotFoundException cnfe) {

View File

@ -84,7 +84,7 @@ public class Validation {
int errorOffset,
HqlParser.StatementContext statementContext) {
try {
return createSemanticQueryBuilder( returnType, factory ).visitStatement( statementContext );
return createSemanticQueryBuilder( returnType, hql, factory ).visitStatement( statementContext );
}
catch ( JdbcTypeRecommendationException ignored ) {
// just squash these for now
@ -100,15 +100,15 @@ public class Validation {
}
private static SemanticQueryBuilder<?> createSemanticQueryBuilder(
@Nullable TypeMirror returnType, SessionFactoryImplementor factory) {
@Nullable TypeMirror returnType, String hql, SessionFactoryImplementor factory) {
if ( returnType != null && returnType.getKind() == TypeKind.DECLARED ) {
final DeclaredType declaredType = (DeclaredType) returnType;
final TypeElement typeElement = (TypeElement) declaredType.asElement();
if ( isEntity( typeElement ) ) {
return new SemanticQueryBuilder<>( getEntityName( typeElement ), () -> false, factory );
return new SemanticQueryBuilder<>( getEntityName( typeElement ), () -> false, factory, hql );
}
}
return new SemanticQueryBuilder<>( Object[].class, () -> false, factory );
return new SemanticQueryBuilder<>( Object[].class, () -> false, factory, hql );
}
private static HqlParser.StatementContext parseAndCheckSyntax(String hql, Handler handler) {