Hello,
I am using Hibernate-core 3.5.1-Final. I have a named query defined in my entity class. Later in my Generic DAO logic, I use the Query API to dynamically look up the named query from the session and then set the parameters on the query.
Code:
@SuppressWarnings("unchecked")
private Map<String, Object> getNamedQueryParameters(final String namedQueryName, Object[] queryArgs) {
System.out.println("GenericDaoHibernateJpa.getNamedQueryParameters(): namedQueryName: " + namedQueryName);
StringBuffer queryArgsString = new StringBuffer();
int qaIndex = 0;
for(Object qa: queryArgs){
queryArgsString.append("GenericDaoHibernateJpa.getNamedQueryParameters(): queryArgs("+(qaIndex++)+"):" + qa.toString() + "\n");
}
System.out.println("GenericDaoHibernateJpa.getNamedQueryParameters(): Query Arguements: " + queryArgsString.toString());
Map<String, Object> namedQueryParams = Collections.synchronizedMap(new HashMap<String, Object>());
final QueryImpl<?> namedQuery = (QueryImpl<?>) getJpaTemplate().execute(new JpaCallback(){
public Object doInJpa(EntityManager em) {
return em.createNamedQuery(namedQueryName);
}
});
System.out.println("GenericDaoHibernateJpa.getNamedQueryParameters(): assigning parameters.");
int i = 0;
for(Object arguement: queryArgs){
namedQuery.setParameter(i++, arguement);
}
for(String namedQueryParamString: namedQuery.getHibernateQuery().getNamedParameters()){
System.out.println("GenericDaoHibernateJpa.getNamedQueryParameters(): " + namedQueryParamString + "=" + namedQuery.getParameterValue(namedQueryParamString));
namedQueryParams.put(namedQueryParamString, namedQuery.getParameterValue(namedQueryParamString));
}
System.out.println("GenericDaoHibernateJpa.getNamedQueryParameters(): end result: " + namedQueryParams);
return namedQueryParams;
}
I get the following error message:
Code:
java.lang.IllegalArgumentException: org.hibernate.QueryParameterException: Position beyond number of declared ordinal parameters. Remember that ordinal parameters are 1-based! Position: 0
at org.hibernate.ejb.QueryImpl.setParameter(QueryImpl.java:435)
at org.generic.dao.aop.persistence.core.dao.GenericDaoHibernateJpa.getNamedQueryParameters(GenericDaoHibernateJpa.java:245)
at org.generic.dao.aop.persistence.core.dao.GenericDaoHibernateJpa.executeFinder(GenericDaoHibernateJpa.java:187)
at org.generic.dao.aop.persistence.core.dao.finder.FinderIntroductionInterceptor.invoke(FinderIntroductionInterceptor.java:32)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
at $Proxy30.findProfileWithExpiredPassword(Unknown Source)
at com.aviall.pw.manager.core.dao.TestPasswordProfileDAO.testFindingExpiredPassword(TestPasswordProfileDAO.java:67)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:82)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:240)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:44)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:180)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:41)
at org.junit.runners.ParentRunner$1.evaluate(ParentRunner.java:173)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:220)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:180)
at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:62)
at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.executeTestSet(AbstractDirectoryTestSuite.java:140)
at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.execute(AbstractDirectoryTestSuite.java:127)
at org.apache.maven.surefire.Surefire.run(Surefire.java:177)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.maven.surefire.booter.SurefireBooter.runSuitesInProcess(SurefireBooter.java:338)
at org.apache.maven.surefire.booter.SurefireBooter.main(SurefireBooter.java:997)
Caused by: org.hibernate.QueryParameterException: Position beyond number of declared ordinal parameters. Remember that ordinal parameters are 1-based! Position: 0
at org.hibernate.engine.query.ParameterMetadata.getOrdinalParameterDescriptor(ParameterMetadata.java:81)
at org.hibernate.engine.query.ParameterMetadata.getOrdinalParameterExpectedType(ParameterMetadata.java:87)
at org.hibernate.impl.AbstractQueryImpl.determineType(AbstractQueryImpl.java:445)
at org.hibernate.impl.AbstractQueryImpl.setParameter(AbstractQueryImpl.java:417)
at org.hibernate.ejb.QueryImpl.setParameter(QueryImpl.java:429)
... 41 more
I've been looking at the JPA code from Hibernate and there seem to a problem with the QueryImpl class.
Code:
public TypedQuery<X> setParameter(int position, Object value) {
try {
if ( isJpaPositionalParameter( position ) ) {
this.setParameter( Integer.toString( position ), value );
}
else {
query.setParameter( position - 1, value );
registerParameterBinding( getParameter( position ), value );
}
return this;
}
catch (QueryParameterException e) {
throw new IllegalArgumentException( e );
}
catch (HibernateException he) {
throw getEntityManager().convert( he );
}
}
Which calls the AbstractQueryImpl.setParameter(int position, Object val) method:
Code:
public Query setParameter(int position, Object val) throws HibernateException {
if (val == null) {
setParameter( position, val, Hibernate.SERIALIZABLE );
}
else {
setParameter( position, val, determineType( position, val ) );
}
return this;
}
Which calls the AbstractQueryImpl.determineType(int paramPosition, Object paramValue) method:
Code:
protected Type determineType(int paramPosition, Object paramValue) throws HibernateException {
Type type = parameterMetadata.getOrdinalParameterExpectedType( paramPosition + 1 );
if ( type == null ) {
type = guessType( paramValue );
}
return type;
}
Which uses the Ordinal to determine the ordinal expected type. But instead of return null, this method throws the above exception. See ParameterMetadata.getOrdinalParameterExpectedType(int position) method.
Code:
public OrdinalParameterDescriptor getOrdinalParameterDescriptor(int position) {
if ( position < 1 || position > ordinalDescriptors.length ) {
String error = "Position beyond number of declared ordinal parameters. " +
"Remember that ordinal parameters are 1-based! Position: " + position;
throw new QueryParameterException( error );
}
return ordinalDescriptors[position - 1];
}
public Type getOrdinalParameterExpectedType(int position) {
return getOrdinalParameterDescriptor( position ).getExpectedType();
}
Lastly, I found that the QueryTranslatorImpl.getWalker() returns a walker with a positionalParameterCount of 0 (zero) and parameterCount of 3 (three); which seem to be the root of the problem. Because when the HQLQueryPlan build the parameter meta data via the buildParameterMetaData() method the ordinalParamCount is 0 (zero). However the recognizer.getNamedParameterDescriptionMap() return 3 (three) entries for the 3 (three) parameters. It seems to me the ordinalParamCount should be overridden to include these 3 (three) entries from the recognizer.getNamedParameterDescriptionMap().
Code:
private ParameterMetadata buildParameterMetadata(ParameterTranslations parameterTranslations, String hql) {
long start = System.currentTimeMillis();
ParamLocationRecognizer recognizer = ParamLocationRecognizer.parseLocations( hql );
long end = System.currentTimeMillis();
if ( log.isTraceEnabled() ) {
log.trace( "HQL param location recognition took " + (end - start) + " mills (" + hql + ")" );
}
int ordinalParamCount = parameterTranslations.getOrdinalParameterCount();
int[] locations = ArrayHelper.toIntArray( recognizer.getOrdinalParameterLocationList() );
if ( parameterTranslations.supportsOrdinalParameterMetadata() && locations.length != ordinalParamCount ) {
throw new HibernateException( "ordinal parameter mismatch" );
}
ordinalParamCount = locations.length;
OrdinalParameterDescriptor[] ordinalParamDescriptors = new OrdinalParameterDescriptor[ordinalParamCount];
for ( int i = 1; i <= ordinalParamCount; i++ ) {
ordinalParamDescriptors[ i - 1 ] = new OrdinalParameterDescriptor(
i,
parameterTranslations.supportsOrdinalParameterMetadata()
? parameterTranslations.getOrdinalParameterExpectedType( i )
: null,
locations[ i - 1 ]
);
}
Iterator itr = recognizer.getNamedParameterDescriptionMap().entrySet().iterator();
Map namedParamDescriptorMap = new HashMap();
while( itr.hasNext() ) {
final Map.Entry entry = ( Map.Entry ) itr.next();
final String name = ( String ) entry.getKey();
final ParamLocationRecognizer.NamedParameterDescription description =
( ParamLocationRecognizer.NamedParameterDescription ) entry.getValue();
namedParamDescriptorMap.put(
name,
new NamedParameterDescriptor(
name,
parameterTranslations.getNamedParameterExpectedType( name ),
description.buildPositionsArray(),
description.isJpaStyle()
)
);
}
return new ParameterMetadata( ordinalParamDescriptors, namedParamDescriptorMap );
}
So is this a bug?
How do I ensure the ParameterMetadata is set correctly with the appropriate values; so the ParameterMetadata.getOrdinalParameterExpectedType(int position) method can stop throwing the above exception?
Thanks!
Tonté