HHH-8643 corrected TemporalType parameter validation

This commit is contained in:
Brett Meyer 2013-10-28 07:06:11 -04:00
parent 4fad9a4a8f
commit cd55718f71
2 changed files with 139 additions and 98 deletions

View File

@ -23,19 +23,31 @@
*/ */
package org.hibernate.ejb; package org.hibernate.ejb;
import static org.hibernate.ejb.QueryHints.HINT_CACHEABLE;
import static org.hibernate.ejb.QueryHints.HINT_CACHE_MODE;
import static org.hibernate.ejb.QueryHints.HINT_CACHE_REGION;
import static org.hibernate.ejb.QueryHints.HINT_COMMENT;
import static org.hibernate.ejb.QueryHints.HINT_FETCH_SIZE;
import static org.hibernate.ejb.QueryHints.HINT_FLUSH_MODE;
import static org.hibernate.ejb.QueryHints.HINT_READONLY;
import static org.hibernate.ejb.QueryHints.HINT_TIMEOUT;
import static org.hibernate.ejb.QueryHints.SPEC_HINT_TIMEOUT;
import java.util.Calendar;
import java.util.Collection; import java.util.Collection;
import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import javax.persistence.CacheRetrieveMode; import javax.persistence.CacheRetrieveMode;
import javax.persistence.CacheStoreMode; import javax.persistence.CacheStoreMode;
import javax.persistence.FlushModeType; import javax.persistence.FlushModeType;
import javax.persistence.Parameter; import javax.persistence.Parameter;
import javax.persistence.TemporalType;
import javax.persistence.TransactionRequiredException; import javax.persistence.TransactionRequiredException;
import javax.persistence.TypedQuery; import javax.persistence.TypedQuery;
import org.jboss.logging.Logger;
import org.hibernate.CacheMode; import org.hibernate.CacheMode;
import org.hibernate.FlushMode; import org.hibernate.FlushMode;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
@ -46,16 +58,7 @@ import org.hibernate.ejb.util.CacheModeHelper;
import org.hibernate.ejb.util.ConfigurationHelper; import org.hibernate.ejb.util.ConfigurationHelper;
import org.hibernate.ejb.util.LockModeTypeHelper; import org.hibernate.ejb.util.LockModeTypeHelper;
import org.hibernate.hql.internal.QueryExecutionRequestException; import org.hibernate.hql.internal.QueryExecutionRequestException;
import org.jboss.logging.Logger;
import static org.hibernate.ejb.QueryHints.HINT_CACHEABLE;
import static org.hibernate.ejb.QueryHints.HINT_CACHE_MODE;
import static org.hibernate.ejb.QueryHints.HINT_CACHE_REGION;
import static org.hibernate.ejb.QueryHints.HINT_COMMENT;
import static org.hibernate.ejb.QueryHints.HINT_FETCH_SIZE;
import static org.hibernate.ejb.QueryHints.HINT_FLUSH_MODE;
import static org.hibernate.ejb.QueryHints.HINT_READONLY;
import static org.hibernate.ejb.QueryHints.HINT_TIMEOUT;
import static org.hibernate.ejb.QueryHints.SPEC_HINT_TIMEOUT;
/** /**
* Intended as a base class providing convenience in implementing both {@link javax.persistence.Query} and * Intended as a base class providing convenience in implementing both {@link javax.persistence.Query} and
@ -341,11 +344,16 @@ public abstract class AbstractQueryImpl<X> implements TypedQuery<X> {
@SuppressWarnings( {"unchecked"}) @SuppressWarnings( {"unchecked"})
protected void registerParameterBinding(Parameter parameter, Object value) { protected void registerParameterBinding(Parameter parameter, Object value) {
registerParameterBinding( parameter, value, null );
}
@SuppressWarnings( {"unchecked"})
protected void registerParameterBinding(Parameter parameter, Object value, TemporalType temporalType) {
if ( parameter == null ) { if ( parameter == null ) {
throw new IllegalArgumentException( "parameter cannot be null" ); throw new IllegalArgumentException( "parameter cannot be null" );
} }
validateParameterBinding( parameter, value ); validateBinding( parameter.getParameterType(), value, temporalType );
if ( parameterBindings == null ) { if ( parameterBindings == null ) {
parameterBindings = new HashMap(); parameterBindings = new HashMap();
@ -353,70 +361,83 @@ public abstract class AbstractQueryImpl<X> implements TypedQuery<X> {
parameterBindings.put( parameter, value ); parameterBindings.put( parameter, value );
} }
private void validateParameterBinding(Parameter parameter, Object value) { private void validateBinding(Class parameterType, Object bind, TemporalType temporalType) {
if ( value == null || parameter.getParameterType() == null ) { if ( bind == null || parameterType == null ) {
// nothing we can check // nothing we can check
return; return;
} }
if ( Collection.class.isInstance( value ) if ( Collection.class.isInstance( bind ) && ! Collection.class.isAssignableFrom( parameterType ) ) {
&& ! Collection.class.isAssignableFrom( parameter.getParameterType() ) ) {
// we have a collection passed in where we are expecting a non-collection. // we have a collection passed in where we are expecting a non-collection.
// NOTE : this can happen in Hibernate's notion of "parameter list" binding // NOTE : this can happen in Hibernate's notion of "parameter list" binding
// NOTE2 : the case of a collection value and an expected collection (if that can even happen) // NOTE2 : the case of a collection value and an expected collection (if that can even happen)
// will fall through to the main check. // will fall through to the main check.
validateCollectionValuedParameterMultiBinding( parameter, (Collection) value ); validateCollectionValuedParameterBinding( parameterType, (Collection) bind, temporalType );
} }
else if ( value.getClass().isArray() ) { else if ( bind.getClass().isArray() ) {
validateArrayValuedParameterBinding( parameter, value ); validateArrayValuedParameterBinding( parameterType, bind, temporalType );
} }
else { else {
if ( ! parameter.getParameterType().isInstance( value ) ) { if ( ! isValidBindValue( parameterType, bind, temporalType ) ) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
String.format( String.format(
"Parameter value [%s] did not match expected type [%s]", "Parameter value [%s] did not match expected type [%s (%s)]",
value, bind,
parameter.getParameterType().getName() parameterType.getName(),
extractName( temporalType )
) )
); );
} }
} }
} }
private void validateCollectionValuedParameterMultiBinding(Parameter parameter, Collection value) { private String extractName(TemporalType temporalType) {
return temporalType == null ? "n/a" : temporalType.name();
}
private void validateCollectionValuedParameterBinding(
Class parameterType,
Collection value,
TemporalType temporalType) {
// validate the elements... // validate the elements...
for ( Object element : value ) { for ( Object element : value ) {
if ( ! parameter.getParameterType().isInstance( element ) ) { if ( ! isValidBindValue( parameterType, element, temporalType ) ) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
String.format( String.format(
"Parameter value element [%s] did not match expected type [%s]", "Parameter value element [%s] did not match expected type [%s (%s)]",
element, element,
parameter.getParameterType().getName() parameterType.getName(),
extractName( temporalType )
) )
); );
} }
} }
} }
private void validateArrayValuedParameterBinding(Parameter parameter, Object value) { private void validateArrayValuedParameterBinding(
if ( ! parameter.getParameterType().isArray() ) { Class parameterType,
Object value,
TemporalType temporalType) {
if ( ! parameterType.isArray() ) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
String.format( String.format(
"Encountered array-valued parameter binding, but was expecting [%s]", "Encountered array-valued parameter binding, but was expecting [%s (%s)]",
parameter.getParameterType().getName() parameterType.getName(),
extractName( temporalType )
) )
); );
} }
if ( value.getClass().getComponentType().isPrimitive() ) { if ( value.getClass().getComponentType().isPrimitive() ) {
// we have a primitive array. we validate that the actual array has the component type (type odf elements) // we have a primitive array. we validate that the actual array has the component type (type of elements)
// we expect based on the component type of the parameter specification // we expect based on the component type of the parameter specification
if ( ! parameter.getParameterType().getComponentType().isAssignableFrom( value.getClass().getComponentType() ) ) { if ( ! parameterType.getComponentType().isAssignableFrom( value.getClass().getComponentType() ) ) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
String.format( String.format(
"Primitive array-valued parameter bind value type [%s] did not match expected type [%s]", "Primitive array-valued parameter bind value type [%s] did not match expected type [%s (%s)]",
value.getClass().getComponentType().getName(), value.getClass().getComponentType().getName(),
parameter.getParameterType().getName() parameterType.getName(),
extractName( temporalType )
) )
); );
} }
@ -426,12 +447,13 @@ public abstract class AbstractQueryImpl<X> implements TypedQuery<X> {
// the type we expect based on the component type of the parameter specification // the type we expect based on the component type of the parameter specification
final Object[] array = (Object[]) value; final Object[] array = (Object[]) value;
for ( Object element : array ) { for ( Object element : array ) {
if ( ! parameter.getParameterType().getComponentType().isInstance( element ) ) { if ( ! isValidBindValue( parameterType.getComponentType(), element, temporalType ) ) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
String.format( String.format(
"Array-valued parameter value element [%s] did not match expected type [%s]", "Array-valued parameter value element [%s] did not match expected type [%s (%s)]",
element, element,
parameter.getParameterType().getName() parameterType.getName(),
extractName( temporalType )
) )
); );
} }
@ -439,6 +461,25 @@ public abstract class AbstractQueryImpl<X> implements TypedQuery<X> {
} }
} }
private boolean isValidBindValue(Class expectedType, Object value, TemporalType temporalType) {
if ( expectedType.isInstance( value ) ) {
return true;
}
if ( temporalType != null ) {
final boolean parameterDeclarationIsTemporal = Date.class.isAssignableFrom( expectedType )
|| Calendar.class.isAssignableFrom( expectedType );
final boolean bindIsTemporal = Date.class.isInstance( value )
|| Calendar.class.isInstance( value );
if ( parameterDeclarationIsTemporal && bindIsTemporal ) {
return true;
}
}
return false;
}
@Override @Override
public boolean isBound(Parameter<?> param) { public boolean isBound(Parameter<?> param) {
return parameterBindings != null && parameterBindings.containsKey( param ); return parameterBindings != null && parameterBindings.containsKey( param );

View File

@ -395,7 +395,7 @@ public class QueryImpl<X> extends org.hibernate.ejb.AbstractQueryImpl<X> impleme
else if ( temporalType == TIMESTAMP ) { else if ( temporalType == TIMESTAMP ) {
query.setTimestamp( name, value ); query.setTimestamp( name, value );
} }
registerParameterBinding( getParameter( name ), value ); registerParameterBinding( getParameter( name ), value, temporalType );
return this; return this;
} }
catch (QueryParameterException e) { catch (QueryParameterException e) {
@ -420,7 +420,7 @@ public class QueryImpl<X> extends org.hibernate.ejb.AbstractQueryImpl<X> impleme
else if ( temporalType == TIMESTAMP ) { else if ( temporalType == TIMESTAMP ) {
query.setCalendar( name, value ); query.setCalendar( name, value );
} }
registerParameterBinding( getParameter(name), value ); registerParameterBinding( getParameter(name), value, temporalType );
return this; return this;
} }
catch (QueryParameterException e) { catch (QueryParameterException e) {
@ -476,7 +476,7 @@ public class QueryImpl<X> extends org.hibernate.ejb.AbstractQueryImpl<X> impleme
else if ( temporalType == TIMESTAMP ) { else if ( temporalType == TIMESTAMP ) {
query.setTimestamp( position - 1, value ); query.setTimestamp( position - 1, value );
} }
registerParameterBinding( getParameter( position ), value ); registerParameterBinding( getParameter( position ), value, temporalType );
} }
return this; return this;
} }
@ -507,7 +507,7 @@ public class QueryImpl<X> extends org.hibernate.ejb.AbstractQueryImpl<X> impleme
else if ( temporalType == TIMESTAMP ) { else if ( temporalType == TIMESTAMP ) {
query.setCalendar( position - 1, value ); query.setCalendar( position - 1, value );
} }
registerParameterBinding( getParameter( position ), value ); registerParameterBinding( getParameter( position ), value, temporalType );
} }
return this; return this;
} }