EclipseLink assumes that Query.setParameter(Parameter<T> param, T value)'s param object is an EclipseLink type
Spring Data JPA creates a JPA-compliant Parameter instance and supplies it through Query.setParameter(Parameter<T> param, T value). This ends up inside EJBQueryImpl, where it breaks.
@Override
public <T> TypedQuery setParameter(Parameter<T> param, T value) {
if (param == null) {
throw new IllegalArgumentException(ExceptionLocalization.buildMessage("NULL_PARAMETER_PASSED_TO_SET_PARAMETER"));
}
//bug 402686: type validation
String position = getParameterId(param); // <----- this is where it fails
ParameterExpressionImpl parameter = (ParameterExpressionImpl) this.getInternalParameters().get(position);
if (parameter == null ) {
throw new IllegalArgumentException(ExceptionLocalization.buildMessage("NO_PARAMETER_WITH_NAME", new Object[] { param.toString(), this.databaseQuery }));
}
if (!parameter.getParameterType().equals(param.getParameterType())) {
throw new IllegalArgumentException(ExceptionLocalization.buildMessage("INCORRECT_PARAMETER_TYPE", new Object[] { position, param.getParameterType() }));
}
return this.setParameter(position, value);
}
When it invokes getParameterId against the Parameter<T>, EL is assuming that this is an internal type instead of a JPA type, and attempts a cast operation, which in our situation fails.
public static String getParameterId(Parameter param){
Integer id= param.getPosition();
if (id == null ){
return String.valueOf(((ParameterExpressionImpl)param).getInternalName()); // <--- perhaps param.getName() instead?
}
return String.valueOf(id);
}
Because we are supplying a name-based parameter, a JPA Parameter.getName() operation, which would have worked, is traded for a downcast and then an invocation for an internal name operation.
Perhaps consider FIRST checking if there is a param.getName() result and deferring to that?
You're clearly using the getPosition() call, so why not stick to the interface operations?