[ https://issues.apache.org/jira/browse/OPENJPA-2785?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]
Mark Struberg resolved OPENJPA-2785. ------------------------------------ Resolution: Fixed Assignee: Mark Struberg Fix Version/s: 3.1.0 Thanks for the report! I've just comitted a fix for this issue. Can you please give it a try and report back if it works or not? txs! > Queries invoked by Spring data that have parameters fail > -------------------------------------------------------- > > Key: OPENJPA-2785 > URL: https://issues.apache.org/jira/browse/OPENJPA-2785 > Project: OpenJPA > Issue Type: Bug > Components: criteria > Affects Versions: 3.1.0 > Reporter: Michael Wiles > Assignee: Mark Struberg > Priority: Major > Fix For: 3.1.0 > > > When you invoke a query provided/built by spring data when it contains a > parameter this fails. > Only affects version 3.1.0 > > See [https://github.com/michaelwiles/openjpa-spring-data-bug] for demo > project. > Here is the stack trace: > org.springframework.dao.InvalidDataAccessApiUsageException: Cannot execute > query; declared parameters "ParameterExpression<String>" were not given > values. You must supply a value for each of the following parameters, in the > given order: [ParameterExpression<String>]; nested exception is > <openjpa-3.1.0-re2160a11145bd3b2a03d61a8752fb52febcec4cc nonfatal user error> > org.apache.openjpa.persistence.ArgumentException: Cannot execute query; > declared parameters "ParameterExpression<String>" were not given values. You > must supply a value for each of the following parameters, in the given order: > [ParameterExpression<String>] > at > org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:373) > at > org.springframework.orm.jpa.DefaultJpaDialect.translateExceptionIfPossible(DefaultJpaDialect.java:127) > at > org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:527) > at > org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61) > at > org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242) > at > org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:153) > at > org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) > at > org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:138) > at > org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) > at > org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93) > at > org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) > at > org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:61) > at > org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) > at > org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) > at com.sun.proxy.$Proxy64.findByName(Unknown Source) > at > com.afrozaar.bug.openjpa.OpenjpaSpringDataJpaApplicationTests.MemberByName(OpenjpaSpringDataJpaApplicationTests.java:30) > at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) > at > sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) > at > sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) > at java.lang.reflect.Method.invoke(Method.java:498) > at > org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) > at > org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) > at > org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) > at > org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) > at > org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74) > at > org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84) > at > org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75) > at > org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86) > at > org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84) > at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) > at > org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251) > at > org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97) > at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) > at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) > at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) > at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) > at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) > at > org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) > at > org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) > at org.junit.runners.ParentRunner.run(ParentRunner.java:363) > at > org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190) > at > org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:89) > at > org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41) > at > org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:541) > at > org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:763) > at > org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:463) > at > org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:209) > Caused by: <openjpa-3.1.0-re2160a11145bd3b2a03d61a8752fb52febcec4cc nonfatal > user error> org.apache.openjpa.persistence.ArgumentException: Cannot execute > query; declared parameters "ParameterExpression<String>" were not given > values. You must supply a value for each of the following parameters, in the > given order: [ParameterExpression<String>] > at org.apache.openjpa.kernel.QueryImpl.assertParameters(QueryImpl.java:1849) > at org.apache.openjpa.kernel.QueryImpl.execute(QueryImpl.java:905) > at org.apache.openjpa.kernel.QueryImpl.execute(QueryImpl.java:843) > at > org.apache.openjpa.kernel.DelegatingQuery.execute(DelegatingQuery.java:601) > at org.apache.openjpa.persistence.QueryImpl.execute(QueryImpl.java:297) > at org.apache.openjpa.persistence.QueryImpl.getResultList(QueryImpl.java:314) > at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) > at > sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) > at > sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) > at java.lang.reflect.Method.invoke(Method.java:498) > at > org.springframework.orm.jpa.SharedEntityManagerCreator$DeferredQueryInvocationHandler.invoke(SharedEntityManagerCreator.java:402) > at com.sun.proxy.$Proxy77.getResultList(Unknown Source) > at > org.springframework.data.jpa.repository.query.JpaQueryExecution$CollectionExecution.doExecute(JpaQueryExecution.java:129) > at > org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:91) > at > org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:136) > at > org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:125) > at > org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:605) > at > org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.lambda$invoke$3(RepositoryFactorySupport.java:595) > at > org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:595) > at > org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) > at > org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:59) > at > org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) > at > org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:294) > at > org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98) > at > org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) > at > org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139) > ... 41 more > And some of my comments (originally from a reply to the mailing list:) > I can't upgrade the 3.1 until this issue is fixed as basically, as far as I > can tell, any parameterised call via spring data does not work. > > Not sure it's the right place to discuss this but the way I see it the > ParameterExpressionImpl > ([https://github.com/apache/openjpa/blob/master/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/ParameterExpressionImpl.java]) > has acquired a hashCode and equals with this release - > [https://github.com/apache/openjpa/commit/0e4ec5b392b978c4515b26c60e485f2b610de94f#diff-e357856846fb8b88f15c08e60891cc35] > and this is the code of the problem with spring data. > > Now what's happening is that the compile is called - and this is called > before the parameter expression has a value. All hashcode calcs are done and > stuff is added to a set. > > Then later on the value for the parameter is set. This causes changes to the > hashCode and equals, resulting in the problem that I'm seeing. > > Now I apologise if I'm completely out of line but I'm wondering why the > value is included in the hashCode and equals of a Parameter as surely a value > is a "runtime" concept and it not necessarily available at compile time. > > Now the hashCode and equals were added for good reason I assume, and > furthermore, the value is included in the hashCode/equals also for good > reason. But we arguably need a mechanism to view the parameter purely from a > metadata point of view (which is I think what we need here) as well as from a > metadata+value point of view. > > But I do wonder why the ParamterExpressionImpl does include the value in the > hashCode and equals. My gut feel is that it's not necessary. > > Relevant stack traces: first time hashCode is called - at this point the > value is not specified in the ParameterExpressionImpl. Notice that the > CriteriaQueryImpl.compile kicks this off. > > > org.apache.openjpa.persistence.criteria.ParameterExpressionImpl<T>.hashCode() > line: 154 > java.util.HashMap<K,V>.hash(java.lang.Object) line: 338 > > java.util.LinkedHashMap<K,V>(java.util.HashMap<K,V>).containsKey(java.lang.Object) > line: 595 > org.apache.openjpa.lib.util.OrderedMap<K,V>.containsKey(java.lang.Object) > line: 70 > > org.apache.openjpa.persistence.criteria.CriteriaQueryImpl<T>.registerParameter(org.apache.openjpa.persistence.criteria.ParameterExpressionImpl<?>) > line: 227 > > org.apache.openjpa.persistence.criteria.CriteriaExpressionVisitor$ParameterVisitor.enter(org.apache.openjpa.persistence.criteria.CriteriaExpression) > line: 106 > > org.apache.openjpa.persistence.criteria.Expressions.acceptVisit(org.apache.openjpa.persistence.criteria.CriteriaExpressionVisitor, > org.apache.openjpa.persistence.criteria.CriteriaExpression, > javax.persistence.criteria.Expression<?>...) line: 106 > > org.apache.openjpa.persistence.criteria.ParameterExpressionImpl<T>(org.apache.openjpa.persistence.criteria.SelectionImpl<X>).acceptVisit(org.apache.openjpa.persistence.criteria.CriteriaExpressionVisitor) > line: 156 > > org.apache.openjpa.persistence.criteria.Expressions.visitChildren(org.apache.openjpa.persistence.criteria.CriteriaExpressionVisitor, > javax.persistence.criteria.Expression<?>...) line: 121 > > org.apache.openjpa.persistence.criteria.Expressions.acceptVisit(org.apache.openjpa.persistence.criteria.CriteriaExpressionVisitor, > org.apache.openjpa.persistence.criteria.CriteriaExpression, > javax.persistence.criteria.Expression<?>...) line: 108 > > org.apache.openjpa.persistence.criteria.Expressions$Equal(org.apache.openjpa.persistence.criteria.Expressions$BinaryLogicalExpression).acceptVisit(org.apache.openjpa.persistence.criteria.CriteriaExpressionVisitor) > line: 278 > > org.apache.openjpa.persistence.criteria.CriteriaQueryImpl<T>.collectParameters(org.apache.openjpa.persistence.criteria.CriteriaExpressionVisitor) > line: 681 > *org.apache.openjpa.persistence.criteria.CriteriaQueryImpl<T>.compile() > line: 672* > > org.apache.openjpa.persistence.EntityManagerImpl.createQuery(javax.persistence.criteria.CriteriaQuery<T>) > line: 1898 > > Then the error I get, occurs here - in org.apache.openjpa.kernel.QueryImpl > > protected void assertParameters(StoreQuery q, StoreQuery.Executor ex, Map > params) { > if (!q.requiresParameterDeclarations()) > return; > > OrderedMap<Object,Class<?>> paramTypes = > ex.getOrderedParameterTypes(q); > *for (Object actual : params.keySet()) {* > *if (!paramTypes.containsKey(actual))* > throw new UserException(_loc.get("unbound-params", > actual, paramTypes.keySet())); > } > for (Object expected : paramTypes.keySet()) > { if (!params.containsKey(expected)) throw new > UserException(_loc.get("unbound-params", expected, > paramTypes.keySet())); } > > for (Entry<Object, Class<?>> entry : paramTypes.entrySet()) > { if (entry.getValue().isPrimitive() && > params.get(entry.getKey()) == null) throw new > UserException(_loc.get("null-primitive-param", entry.getKey())); } > } > > The error occurs in the bold stuff. > > And the fundamental reason as far as I can tell is that the paramtypes map > was populated when the value was set and then the *actual* reference in this > code has the value set... > > Iow, getOrderedParameterTypes returns the map created before the value was > set and the params.keySet has parameterExpressionImpls that have their values > set. > > And you know what happens when you use a hashMap and you change the hashCode > after you've populated the hashmap. > > Error stack trace: > at org.apache.openjpa.kernel.QueryImpl.assertParameters(QueryImpl.java:1849) > at org.apache.openjpa.kernel.QueryImpl.execute(QueryImpl.java:905) > at org.apache.openjpa.kernel.QueryImpl.execute(QueryImpl.java:843) > at > org.apache.openjpa.kernel.DelegatingQuery.execute(DelegatingQuery.java:601) > at org.apache.openjpa.persistence.QueryImpl.execute(QueryImpl.java:297) > at org.apache.openjpa.persistence.QueryImpl.getResultList(QueryImpl.java:314) > at > org.apache.openjpa.persistence.QueryImpl.getSingleResult(QueryImpl.java:343) > at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) > at > sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) > at > sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) > at java.lang.reflect.Method.invoke(Method.java:498) > at org.springframework.orm.jpa.SharedEntityManagerCreat -- This message was sent by Atlassian JIRA (v7.6.3#76005)