IllegalArgumentException: Type cannot be null.

I ran into a problem, and it seems that many people ran into it and probably could not solve it.

I have the following MYSQL stored procedure. This is just a sample to see if everything is working fine, then I would add parameters like inactive .

 CREATE DEFINER=`root`@`localhost` PROCEDURE `get_resource_types`() BEGIN SELECT * FROM resource_types WHERE inactive = 0; END 

An object that displays the resource_types table and the named request of the stored procedure.

 @NamedStoredProcedureQuery( name="getResourceTypes", procedureName="get_resource_types", resultClasses = ResourceType.class, parameters = {} ) @Entity @Table(name = "resource_types") public class ResourceType { ... fields with annotations used for validation + getters and setters ... } 

And here is my JpaRepository from which I am making a stored procedure call

 @Repository public interface ResourceTypeRepository extends JpaRepository<ResourceType, Long> { @Procedure("ResourceType.getResourceTypes") List<ResourceType> getResourceTypes(); } 

getAll() method, which is in @Service

 public List<ResourceType> getAll(){ final List<ResourceType> resourceTypes = resourceTypeRepository.getResourceTypes(); return resourceTypes; } 

When I try to run this, I get the following stack trace:

 org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: Type cannot be null; nested exception is java.lang.IllegalArgumentException: Type cannot be null at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:978) at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:857) at javax.servlet.http.HttpServlet.service(HttpServlet.java:618) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842) at org.springframework.test.web.servlet.TestDispatcherServlet.service(TestDispatcherServlet.java:65) at javax.servlet.http.HttpServlet.service(HttpServlet.java:725) at org.springframework.mock.web.MockFilterChain$ServletFilterProxy.doFilter(MockFilterChain.java:167) at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134) at org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:144) at com.test.ihbs.controller.ResourceTypeControllerTest.test_getAll(ResourceTypeControllerTest.java:111) 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:497) 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.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26) at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:73) at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:82) at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:73) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:224) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:83) 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:68) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:163) at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.runTestClass(JUnitTestClassExecuter.java:86) at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.execute(JUnitTestClassExecuter.java:49) at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessor.processTestClass(JUnitTestClassProcessor.java:64) at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:50) 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:497) at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35) at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) at org.gradle.messaging.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32) at org.gradle.messaging.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93) at com.sun.proxy.$Proxy2.processTestClass(Unknown Source) at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:106) 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:497) at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35) at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) at org.gradle.messaging.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:360) at org.gradle.internal.concurrent.DefaultExecutorFactory$StoppableExecutorImpl$1.run(DefaultExecutorFactory.java:64) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) Caused by: org.springframework.dao.InvalidDataAccessApiUsageException: Type cannot be null; nested exception is java.lang.IllegalArgumentException: Type cannot be null at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:381) at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:223) at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:417) at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:59) at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213) at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:147) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodIntercceptor.invoke(CrudMethodMetadataPostProcessor.java:122) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207) at com.sun.proxy.$Proxy87.getResourceTypes(Unknown Source) at com.ihbs.service.ResourceTypeService.getAll(ResourceTypeService.java:34) at com.ihbs.controller.ResourceTypeController.getAllResourceTypes(ResourceTypeController.java:44) 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:497) at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221) at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:137) at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:776) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:705) at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966) ... 58 more Caused by: java.lang.IllegalArgumentException: Type cannot be null at org.hibernate.procedure.internal.AbstractParameterRegistrationImpl.setHibernateType(AbstractParameterRegistrationImpl.java:182) at org.hibernate.procedure.internal.AbstractParameterRegistrationImpl.<init>(AbstractParameterRegistrationImpl.java:131) at org.hibernate.procedure.internal.AbstractParameterRegistrationImpl.<init>(AbstractParameterRegistrationImpl.java:140) at org.hibernate.procedure.internal.AbstractParameterRegistrationImpl.<init>(AbstractParameterRegistrationImpl.java:77) at org.hibernate.procedure.internal.PositionalParameterRegistration.<init>(PositionalParameterRegistration.java:41) at org.hibernate.procedure.internal.ProcedureCallImpl.registerParameter(ProcedureCallImpl.java:275) at org.hibernate.jpa.internal.StoredProcedureQueryImpl.registerStoredProcedureParameter(StoredProcedureQueryImpl.java:128) at org.springframework.data.jpa.repository.query.StoredProcedureJpaQuery.newAdhocStoredProcedureQuery(StoredProcedureJpaQuery.java:147) at org.springframework.data.jpa.repository.query.StoredProcedureJpaQuery.createStoredProcedure(StoredProcedureJpaQuery.java:110) at org.springframework.data.jpa.repository.query.StoredProcedureJpaQuery.doCreateQuery(StoredProcedureJpaQuery.java:68) at org.springframework.data.jpa.repository.query.StoredProcedureJpaQuery.createQuery(StoredProcedureJpaQuery.java:58) at org.springframework.data.jpa.repository.query.JpaQueryExecution$ProcedureExecution.doExecute(JpaQueryExecution.java:295) at org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:74) at org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:97) at org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:88) at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:395) at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:373) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.data.repository.core.support.RepositoryFactorySupport$DefaultMethodInvokingMethodInterceptor.invoke(RepositoryFactorySupport.java:486) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136) ... 80 more 

Any ideas why this is happening and how to fix it?

The documentation for InvalidDataAccessApiUsageException says:

An exception caused by improper use of the API, for example, non-compliance with the "compile" request object, which requires compilation before execution. This is a problem in our Java data access infrastructure, not in the underlying data access infrastructure.

UPDATE 1

If I changed the repository to the following code, it will work, which means that there are problems with requesting a name procedure. I still need to make the stored procedure method work.

 @Repository public interface ResourceTypeRepository extends JpaRepository<ResourceType, Long> { @Query("SELECT rt FROM ResourceType rt WHERE rt.inactive = 0") List<ResourceType> getResourceTypes(); } 

UPDATE 2

I do not use EclipseLink (or at least I do not know about its existence)

UPDATE 3

I could not find the cause of this problem, even if I went through the code while debugging. But I found something that could help in detecting the problem.

I found this piece of code in org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java , and if you notice that in the last line there is a function call where it gets the total number of parameters and adds an extra one. There may be a problem, an additional parameter. However, this may not be a problem, because I believe that he can follow a different path in different circumstances.

 /** * Extracts the output value from the given {@link StoredProcedureQuery}. * * @param storedProcedureQuery must not be {@literal null}. * @return */ Object extractOutputValue(StoredProcedureQuery storedProcedureQuery) { Assert.notNull(storedProcedureQuery, "StoredProcedureQuery must not be null!"); if (!procedureAttributes.hasReturnValue()) { return null; } if (StringUtils.hasText(procedureAttributes.getOutputParameterName())) { return storedProcedureQuery.getOutputParameterValue(procedureAttributes.getOutputParameterName()); } return storedProcedureQuery.getOutputParameterValue(getQueryMethod().getParameters().getNumberOfParameters() + 1); } 
+6
source share
2 answers

Spring JPA data from 1.11.1 does not support SPs that return result sets. I registered a corresponding defect with Spring data.

The solution is to go down from the API level and just use JPA. Here is a generic class that I wrote that works with MS SQL SP.

 import com.google.common.base.Strings; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.ParameterMode; import javax.persistence.Query; import javax.persistence.StoredProcedureQuery; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service @RequiredArgsConstructor(onConstructor = @__(@Autowired)) public class StoredProcRepository { //region Injected beans (via a RequiredArgsConstructor) private final EntityManager em; //endregion /** * Calls a stored procedure via JPA and retrieves a single implicit result set (in DBs that * support them eg MS SQL or MySQL). The call is not dependent on a DB dialect. Be * aware that large result sets should be paginated and not entirely read to memory. Recreates * StoredProcedureQuery instance and its parameters on each call. * To execute MS SQL SPs performing multiple queries, SET NOCOUNT ON. * * @param procedureName stored procedure name, optionally qualified per DB syntax * @param resultClass converts (maps) each result set row into instances of resultClass via JPA * @param spArgs stored procedure arguments, supplied positionally (optional SP arguments at the * end of the list could be omitted) * @param <T> class of row instances converted per JPA * @return the entire result set */ public <T> List<T> queryViaStoredProc(String procedureName, Class<T> resultClass, Object... spArgs) { StoredProcedureQuery spq = em.createStoredProcedureQuery(procedureName, resultClass); int pos = 0; for (Object arg : spArgs) { spq.registerStoredProcedureParameter(++pos, arg.getClass(), ParameterMode.IN); spq.setParameter(pos, arg); } return spq.getResultList(); } /** * Calls a stored procedure via JPA and retrieves only the top row of a single implicit result * set (in DBs that support them eg MS SQL or MySQL). * Assumes that result set has at least one row. * The call is not dependent on a DB dialect. * Be aware that large result sets should be paginated and not entirely read to memory. * Recreates StoredProcedureQuery instance and its parameters on each call. * To execute MS SQL SPs performing multiple queries, SET NOCOUNT ON. * * @param procedureName stored procedure name, optionally qualified per DB syntax * @param resultClass converts (maps) each result set row into instances of resultClass via JPA * @param spArgs stored procedure arguments, supplied positionally (optional SP arguments at the * end of the list could be omitted) * @param <T> class of row instances converted per JPA * @return the entire result set */ public <T> T queryTopRowViaStoredProc(String procedureName, Class<T> resultClass, Object... spArgs) { return queryViaStoredProc(procedureName, resultClass, spArgs).get(0); } } 

For MS SQL SPs, an additional requirement is to have SET NOCOUNT ON for all SPs that execute more than one query. This can be set in one of three ways:

Its code is for # 1: the corresponding methods for the same class, StoredProcRepository .

  /** * Calls an MS SQL stored procedure via JPA and retrieves a single implicit result set. * Protects against lack of SET NOCOUNT in stored procedures. * This works with jTDS JDBC driver, but not with MS JDBC driver. * Be aware that large result sets should be paginated and not entirely read to memory. * * @param procedureName stored procedure name, optionally qualified per DB syntax * @param resultClass converts (maps) each result set row into instances of resultClass via JPA * @param spArgs stored procedure arguments, supplied positionally (optional SP arguments at the * end of the list could be omitted) * @param <T> class of row instances converted per JPA * @return the entire result set */ public <T> List<T> queryViaMsSqlStoredProc(String procedureName, Class<T> resultClass, Object... spArgs) { String spBindParams = (spArgs.length == 0) ? "" : "?" + Strings.repeat(",?", spArgs.length - 1); // The following works with jTDS driver, but not with MS driver String spQuery = String.format("EXEC %s %s", procedureName, spBindParams); // The following works with jTDS driver, but not with MS driver /* String spQuery = String.format("{call %s(%s)}", procedureName, spBindParams); Query q = em.createNativeQuery("SET NOCOUNT ON; " + spQuery, resultClass) .setHint("org.hibernate.readOnly", true); */ Query q = em.createNativeQuery(spQuery, resultClass); int pos = 0; for (Object arg : spArgs) { q.setParameter(++pos, arg); } return q.getResultList(); } /** * Calls an MS SQL stored procedure via JPA and retrieves only the top row of a single implicit * result set. * Assumes that result set has at least one row. * The call sets the "NOCOUNT ON" MS SQL batch option. * Be aware that large result sets should be paginated and not entirely read to memory. * * @param procedureName stored procedure name, optionally qualified per DB syntax * @param resultClass converts (maps) each result set row into instances of resultClass via JPA * @param spArgs stored procedure arguments, supplied positionally (optional SP arguments at the * end of the list could be omitted) * @param <T> class of row instances converted per JPA * @return the entire result set */ public <T> T queryTopRowViaMsSqlStoredProc(String procedureName, Class<T> resultClass, Object... spArgs) { return queryViaMsSqlStoredProc(procedureName, resultClass, spArgs).get(0); } 
+1
source

It seems that the problem is with the display of your results (the resulting object cannot be printed).

Try changing:

 @Procedure("ResourceType.getResourceTypes") List<ResourceType> getResourceTypes(); 

to

 @Procedure("ResourceType.getResourceTypes") Object[] getResourceTypes(); 

and remove resultClasses in your NamedStoredProcedureQuery.

Hope this helps.

0
source

Source: https://habr.com/ru/post/989879/


All Articles