mirror of
https://github.com/hibernate/hibernate-orm
synced 2025-02-09 12:44:49 +00:00
HHH-17729 move validation of constructors in HQL instantiations to SemanticQueryBuilder
also validate injection via fields/properties
This commit is contained in:
parent
766234d281
commit
dcb2c60d4e
@ -1466,9 +1466,13 @@ public SqmDynamicInstantiation<?> visitInstantiation(HqlParser.InstantiationCont
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ( !dynamicInstantiation.checkInstantiation( creationContext.getTypeConfiguration() ) ) {
|
if ( !dynamicInstantiation.checkInstantiation( creationContext.getTypeConfiguration() ) ) {
|
||||||
throw new SemanticException( "No matching constructor for type '"
|
final String typeName = dynamicInstantiation.getJavaType().getSimpleName();
|
||||||
+ dynamicInstantiation.getJavaType().getSimpleName() + "'",
|
if ( dynamicInstantiation.isFullyAliased() ) {
|
||||||
query );
|
throw new SemanticException( "Missing constructor or attributes for injection into type '" + typeName + "'", query );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new SemanticException( "Missing constructor for type '" + typeName + "'", query );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return dynamicInstantiation;
|
return dynamicInstantiation;
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.hibernate.query.sqm.tree.select;
|
package org.hibernate.query.sqm.tree.select;
|
||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -31,6 +30,7 @@
|
|||||||
import static org.hibernate.query.sqm.DynamicInstantiationNature.LIST;
|
import static org.hibernate.query.sqm.DynamicInstantiationNature.LIST;
|
||||||
import static org.hibernate.query.sqm.DynamicInstantiationNature.MAP;
|
import static org.hibernate.query.sqm.DynamicInstantiationNature.MAP;
|
||||||
import static org.hibernate.sql.results.graph.instantiation.internal.InstantiationHelper.isConstructorCompatible;
|
import static org.hibernate.sql.results.graph.instantiation.internal.InstantiationHelper.isConstructorCompatible;
|
||||||
|
import static org.hibernate.sql.results.graph.instantiation.internal.InstantiationHelper.isInjectionCompatible;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a dynamic instantiation ({@code select new XYZ(...) ...}) as part of the SQM.
|
* Represents a dynamic instantiation ({@code select new XYZ(...) ...}) as part of the SQM.
|
||||||
@ -122,28 +122,35 @@ public boolean checkInstantiation(TypeConfiguration typeConfiguration) {
|
|||||||
// where Class objects not available during build
|
// where Class objects not available during build
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if ( getArguments().stream().allMatch(arg -> arg.getAlias() != null ) ) {
|
final List<Class<?>> argTypes = argumentTypes();
|
||||||
// it's probably a bean injection-type instantiator, don't check it now
|
if ( isFullyAliased() ) {
|
||||||
return true;
|
final List<String> aliases =
|
||||||
|
getArguments().stream()
|
||||||
|
.map(SqmDynamicInstantiationArgument::getAlias)
|
||||||
|
.collect(toList());
|
||||||
|
return isInjectionCompatible( getJavaType(), aliases, argTypes )
|
||||||
|
|| isConstructorCompatible( getJavaType(), argTypes, typeConfiguration );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
final List<Class<?>> argTypes =
|
return isConstructorCompatible( getJavaType(), argTypes, typeConfiguration );
|
||||||
getArguments().stream()
|
|
||||||
.map(arg -> arg.getNodeJavaType().getJavaTypeClass())
|
|
||||||
.collect(toList());
|
|
||||||
for ( Constructor<?> constructor : getJavaType().getDeclaredConstructors() ) {
|
|
||||||
if ( isConstructorCompatible( constructor, argTypes, typeConfiguration ) ) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
// TODO: is there anything we need to check for list/map instantiation?
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<Class<?>> argumentTypes() {
|
||||||
|
return getArguments().stream()
|
||||||
|
.map(arg -> arg.getNodeJavaType().getJavaTypeClass())
|
||||||
|
.collect(toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFullyAliased() {
|
||||||
|
return getArguments().stream().allMatch( arg -> arg.getAlias() != null );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SqmDynamicInstantiation<T> copy(SqmCopyContext context) {
|
public SqmDynamicInstantiation<T> copy(SqmCopyContext context) {
|
||||||
final SqmDynamicInstantiation<T> existing = context.getCopy( this );
|
final SqmDynamicInstantiation<T> existing = context.getCopy( this );
|
||||||
@ -323,9 +330,4 @@ public List<SqmSelectableNode<?>> getSelectionItems() {
|
|||||||
visitSubSelectableNodes( list::add );
|
visitSubSelectableNodes( list::add );
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCompoundSelection() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -6,21 +6,25 @@
|
|||||||
*/
|
*/
|
||||||
package org.hibernate.sql.results.graph.instantiation.internal;
|
package org.hibernate.sql.results.graph.instantiation.internal;
|
||||||
|
|
||||||
|
import java.beans.BeanInfo;
|
||||||
import java.beans.PropertyDescriptor;
|
import java.beans.PropertyDescriptor;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.hibernate.internal.util.beans.BeanInfoHelper;
|
import org.hibernate.internal.util.beans.BeanInfoHelper;
|
||||||
import org.hibernate.query.sqm.sql.internal.InstantiationException;
|
import org.hibernate.query.sqm.sql.internal.InstantiationException;
|
||||||
import org.hibernate.query.sqm.tree.expression.Compatibility;
|
|
||||||
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||||
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions;
|
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions;
|
||||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||||
import org.hibernate.type.descriptor.java.JavaType;
|
import org.hibernate.type.descriptor.java.JavaType;
|
||||||
|
|
||||||
|
import static org.hibernate.sql.results.graph.instantiation.internal.InstantiationHelper.findField;
|
||||||
|
import static org.hibernate.sql.results.graph.instantiation.internal.InstantiationHelper.propertyMatches;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
@ -38,52 +42,7 @@ public DynamicInstantiationAssemblerInjectionImpl(
|
|||||||
targetJavaType,
|
targetJavaType,
|
||||||
beanInfo -> {
|
beanInfo -> {
|
||||||
for ( ArgumentReader<?> argumentReader : argumentReaders ) {
|
for ( ArgumentReader<?> argumentReader : argumentReaders ) {
|
||||||
boolean found = false;
|
beanInjections.add( injection( beanInfo, argumentReader, targetJavaType ) );
|
||||||
for ( PropertyDescriptor propertyDescriptor : beanInfo.getPropertyDescriptors() ) {
|
|
||||||
if ( argumentReader.getAlias().equals( propertyDescriptor.getName() ) ) {
|
|
||||||
if ( propertyDescriptor.getWriteMethod() != null ) {
|
|
||||||
final boolean assignmentCompatible = Compatibility.areAssignmentCompatible(
|
|
||||||
propertyDescriptor.getWriteMethod().getParameterTypes()[0],
|
|
||||||
argumentReader.getAssembledJavaType().getClass()
|
|
||||||
);
|
|
||||||
if ( assignmentCompatible ) {
|
|
||||||
propertyDescriptor.getWriteMethod().setAccessible( true );
|
|
||||||
beanInjections.add(
|
|
||||||
new BeanInjection(
|
|
||||||
new BeanInjectorSetter<>( propertyDescriptor.getWriteMethod() ),
|
|
||||||
argumentReader
|
|
||||||
)
|
|
||||||
);
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( found ) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// see if we can find a Field with the given name...
|
|
||||||
final Field field = findField(
|
|
||||||
targetJavaType,
|
|
||||||
argumentReader.getAlias(),
|
|
||||||
argumentReader.getAssembledJavaType().getJavaTypeClass()
|
|
||||||
);
|
|
||||||
if ( field != null ) {
|
|
||||||
beanInjections.add(
|
|
||||||
new BeanInjection(
|
|
||||||
new BeanInjectorField<>( field ),
|
|
||||||
argumentReader
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new InstantiationException(
|
|
||||||
"Cannot set field '" + argumentReader.getAlias()
|
|
||||||
+ "' to instantiate '" + targetJavaType.getName() + "'"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -93,22 +52,29 @@ public DynamicInstantiationAssemblerInjectionImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Field findField(Class<?> declaringClass, String name, Class<?> javaType) {
|
private static BeanInjection injection(BeanInfo beanInfo, ArgumentReader<?> argument, Class<?> targetJavaType) {
|
||||||
try {
|
final Class<?> argType = argument.getAssembledJavaType().getJavaTypeClass();
|
||||||
final Field field = declaringClass.getDeclaredField( name );
|
final String alias = argument.getAlias();
|
||||||
// field should never be null
|
|
||||||
if ( Compatibility.areAssignmentCompatible( field.getType(), javaType ) ) {
|
// see if we can find a property with the given name...
|
||||||
field.setAccessible( true );
|
for ( PropertyDescriptor propertyDescriptor : beanInfo.getPropertyDescriptors() ) {
|
||||||
return field;
|
if ( propertyMatches( alias, argType, propertyDescriptor ) ) {
|
||||||
}
|
final Method setter = propertyDescriptor.getWriteMethod();
|
||||||
}
|
setter.setAccessible(true);
|
||||||
catch (NoSuchFieldException ignore) {
|
return new BeanInjection( new BeanInjectorSetter<>( setter ), argument );
|
||||||
if ( declaringClass.getSuperclass() != null ) {
|
|
||||||
return findField( declaringClass.getSuperclass(), name, javaType );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
// see if we can find a Field with the given name...
|
||||||
|
final Field field = findField( targetJavaType, alias, argType );
|
||||||
|
if ( field != null ) {
|
||||||
|
return new BeanInjection( new BeanInjectorField<>( field ), argument );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new InstantiationException(
|
||||||
|
"Cannot set field '" + alias + "' to instantiate '" + targetJavaType.getName() + "'"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -125,15 +91,14 @@ public T assemble(RowProcessingState rowProcessingState, JdbcValuesSourceProcess
|
|||||||
constructor.setAccessible( true );
|
constructor.setAccessible( true );
|
||||||
result = constructor.newInstance();
|
result = constructor.newInstance();
|
||||||
}
|
}
|
||||||
catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException | java.lang.InstantiationException e) {
|
catch ( NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException
|
||||||
|
| java.lang.InstantiationException e ) {
|
||||||
throw new InstantiationException( "Error instantiating class '"
|
throw new InstantiationException( "Error instantiating class '"
|
||||||
+ target.getJavaType().getTypeName() + "' using default constructor: " + e.getMessage(), e );
|
+ target.getJavaType().getTypeName() + "' using default constructor: " + e.getMessage(), e );
|
||||||
}
|
}
|
||||||
for ( BeanInjection beanInjection : beanInjections ) {
|
for ( BeanInjection beanInjection : beanInjections ) {
|
||||||
beanInjection.getBeanInjector().inject(
|
final Object assembled = beanInjection.getValueAssembler().assemble( rowProcessingState, options );
|
||||||
result,
|
beanInjection.getBeanInjector().inject( result, assembled );
|
||||||
beanInjection.getValueAssembler().assemble( rowProcessingState, options )
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -6,10 +6,14 @@
|
|||||||
*/
|
*/
|
||||||
package org.hibernate.sql.results.graph.instantiation.internal;
|
package org.hibernate.sql.results.graph.instantiation.internal;
|
||||||
|
|
||||||
|
import org.hibernate.internal.util.beans.BeanInfoHelper;
|
||||||
import org.hibernate.type.spi.TypeConfiguration;
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
import java.beans.BeanInfo;
|
||||||
|
import java.beans.PropertyDescriptor;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -27,6 +31,40 @@ private InstantiationHelper() {
|
|||||||
// disallow direct instantiation
|
// disallow direct instantiation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isInjectionCompatible(Class<?> targetJavaType, List<String> aliases, List<Class<?>> argTypes) {
|
||||||
|
return BeanInfoHelper.visitBeanInfo(
|
||||||
|
targetJavaType,
|
||||||
|
beanInfo -> {
|
||||||
|
for ( int i = 0; i < aliases.size(); i++ ) {
|
||||||
|
final String alias = aliases.get(i);
|
||||||
|
final Class<?> argType = argTypes.get(i);
|
||||||
|
if ( !checkArgument( targetJavaType, beanInfo, alias, argType ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean checkArgument(Class<?> targetJavaType, BeanInfo beanInfo, String alias, Class<?> argType) {
|
||||||
|
for ( PropertyDescriptor propertyDescriptor : beanInfo.getPropertyDescriptors() ) {
|
||||||
|
if ( propertyMatches( alias, argType, propertyDescriptor ) ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return findField(targetJavaType, alias, argType) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isConstructorCompatible(Class<?> javaClass, List<Class<?>> argTypes, TypeConfiguration typeConfiguration) {
|
||||||
|
for ( Constructor<?> constructor : javaClass.getDeclaredConstructors() ) {
|
||||||
|
if ( isConstructorCompatible( constructor, argTypes, typeConfiguration) ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isConstructorCompatible(
|
public static boolean isConstructorCompatible(
|
||||||
Constructor<?> constructor,
|
Constructor<?> constructor,
|
||||||
List<Class<?>> argumentTypes,
|
List<Class<?>> argumentTypes,
|
||||||
@ -58,4 +96,28 @@ public static boolean isConstructorCompatible(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Field findField(Class<?> declaringClass, String name, Class<?> javaType) {
|
||||||
|
try {
|
||||||
|
final Field field = declaringClass.getDeclaredField( name );
|
||||||
|
// field should never be null
|
||||||
|
if ( areAssignmentCompatible( field.getType(), javaType ) ) {
|
||||||
|
field.setAccessible( true );
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (NoSuchFieldException ignore) {
|
||||||
|
if ( declaringClass.getSuperclass() != null ) {
|
||||||
|
return findField( declaringClass.getSuperclass(), name, javaType );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean propertyMatches(String alias, Class<?> argType, PropertyDescriptor propertyDescriptor) {
|
||||||
|
return alias.equals(propertyDescriptor.getName())
|
||||||
|
&& propertyDescriptor.getWriteMethod() != null
|
||||||
|
&& areAssignmentCompatible( propertyDescriptor.getWriteMethod().getParameterTypes()[0], argType );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user