eclipselink icon indicating copy to clipboard operation
eclipselink copied to clipboard

EclipseLink assumes that Query.setParameter(Parameter<T> param, T value)'s param object is an EclipseLink type

Open gregturn opened this issue 2 years ago • 0 comments

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?

gregturn avatar Nov 06 '23 18:11 gregturn