IllegalArgumentException:Type不能为null

我遇到了一个问题,似乎很多人遇到了它,可能无法解决它。

我有以下MYSQL存储过程。 这只是一个示例,看看是否一切正常,然后我会开始添加inactive参数。

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

映射resource_types表和命名存储过程查询的实体。

 @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 ... } 

这是我的JpaRepository ,我从中调用存储过程

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

驻留在@ServicegetAll()方法

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

当我尝试运行它时,我得到以下堆栈跟踪:

 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.(AbstractParameterRegistrationImpl.java:131) at org.hibernate.procedure.internal.AbstractParameterRegistrationImpl.(AbstractParameterRegistrationImpl.java:140) at org.hibernate.procedure.internal.AbstractParameterRegistrationImpl.(AbstractParameterRegistrationImpl.java:77) at org.hibernate.procedure.internal.PositionalParameterRegistration.(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 

任何想法为什么会发生这种情况以及如何解决它?

InvalidDataAccessApiUsageException的文档说:

不正确使用API​​时会抛出exception,例如无法在执行前“编译”需要编译的查询对象。 这代表了我们的Java数据访问框架中的问题,而不是底层数据访问基础架构。

更新1

如果我将存储库更改为以下代码,它将起作用,这意味着名称过程查询存在问题。 我仍然需要使存储过程的方式工作。

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

更新2

我没有使用EclipseLink(或者至少我不知道他的存在)

更新3

即使我通过代码调试,我也找不到这个问题的原因。 但我找到了一些可能有助于找到问题的东西。

我在org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java找到了这段代码,如果您注意到在最后一行有一个函数调用,它会获取参数总数并添加一个额外的一个。 这可能是问题,额外的参数。 尽管如此,这可能不是问题,因为我认为它可以在不同的情况下沿着另一条路走。

 /** * 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); } 

从1.11.1开始的Spring Data JPA不支持返回结果集的SP。 我向Spring Data提交了相应的缺陷 。

解决方案是降低API级别并使用JPA。 这是我编写的与MS SQL SP一起使用的generics类。

 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  class of row instances converted per JPA * @return the entire result set */ public  List queryViaStoredProc(String procedureName, Class 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  class of row instances converted per JPA * @return the entire result set */ public  T queryTopRowViaStoredProc(String procedureName, Class resultClass, Object... spArgs) { return queryViaStoredProc(procedureName, resultClass, spArgs).get(0); } } 

对于MS SQL SP,附加要求是对执行多个查询的所有SP具有SET NOCOUNT ON 。 这可以通过以下三种方式之一设置:

  1. 在使用JPA的通用Java包装器中(参见下面的代码)。 此方法仅适用于jTDS JDBC驱动程序。 已经向MS JDBC驱动程序项目提交了相应的问题 。
  2. 在每个SP的开头。
  3. 全局在您的数据库中 。

她是#1的代码:相同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  class of row instances converted per JPA * @return the entire result set */ public  List queryViaMsSqlStoredProc(String procedureName, Class 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  class of row instances converted per JPA * @return the entire result set */ public  T queryTopRowViaMsSqlStoredProc(String procedureName, Class resultClass, Object... spArgs) { return queryViaMsSqlStoredProc(procedureName, resultClass, spArgs).get(0); } 

在映射结果时似乎存在问题(无法输入结果实体)。

尝试改变:

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

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

并删除NamedStoredProcedureQuery中的resultClasses。

希望有所帮助。