本文介绍了IllegalArgumentException:类型不能为空的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



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



<$程序`get_resource_types`()
BEGIN
SELECT *
FROM resource_types
WHERE inactive = 0;
END

映射 resource_types
$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $' $ b procedureName =get_resource_types,
resultClasses = ResourceType.class,
parameters = {}

@Entity
@Table(name =resource_types)
public class ResourceType {
...带有用于验证的注释字段+ getters和setters ...
}

这里是我调用存储过程的 JpaRepository

  @Repository 
public interface ResourceTypeRepository扩展JpaRepository< ResourceType,Long> {
@Procedure(ResourceType.getResourceTypes)
List< ResourceType> getResourceTypes();

$ b

getAll() code>方法驻留在 @Service

  public列表与LT;&的ResourceType GT; getAll(){
final List< ResourceType> resourceTypes = resourceTypeRepository.getResourceTypes();
返回resourceTypes;
}

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

  org.springframework.web.util.NestedServletException:请求处理失败;嵌套异常是org.springframework.dao.InvalidDataAccessApiUsageException:类型不能为空;嵌套异常是java.lang.IllegalArgumentException:类型不能为空
在org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:978)
在org.springframework.web.servlet.FrameworkServlet .doGet(FrameworkServlet.java:857)
在javax.servlet.http.HttpServlet.service(HttpServlet.java:618)
在org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java :842)在org.springframework.test.web.servlet.TestDispatcherServlet.service(TestDispatcherServlet.java:65

在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(ResourceTypeC在sun.reflect.NativeMethodAccessorImpl.invoke ontrollerTest.java:111)
在sun.reflect.NativeMethodAccessorImpl.invoke0(本机方法)
(NativeMethodAccessorImpl.java:62)
。在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(RunBeforeT (org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:68)
at org.junit.runners.ParentRunner.run(ParentRunner。的java:在org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter在org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run 363)
(SpringJUnit4ClassRunner.java:163)
。 runTestClass(JUnitTestClassExecuter.java:86)
在org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.execute(JUnitTestClassExecuter.java:49)
。在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(本地方法)
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。在org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
处使用gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
。 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(De legatingMethodAccessorImpl.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)
在java.util.concurrent.ThreadPoolExecutor $ Worker.run(ThreadPoolExecutor.java:617)$ b $在java.lang.Thread.run(Thread.java:745)
导致:org。 springframework.dao.InvalidDataAccessApiUsageException:类型不能为空;嵌套异常是java.lang.IllegalArgumentException:类型不能为空
在org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:381)
在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)
在org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvoca tion.java:179)维持在org.springframework.aop.framework.ReflectiveMethodInvocation org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor $ CrudMethodMetadataPopulatingMethodIntercceptor.invoke(CrudMethodMetadataPostProcessor.java:122)

。继续(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation。
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy。$ Proxy87.getResourceTypes(Unknown Source)
在com.ihbs.service.ResourceTypeService.getAll(ResourceTypeService.java:34)
在com.ihbs.controller.ResourceTypeController.getAllResourceTypes(ResourceTypeController.java:44)
在sun.reflect.NativeMethodAccessorImpl.invoke0 (本地方法)
在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.AbstractHa ndlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959)
at org.springframework.web.servlet.DispatcherServlet.doService( DispatcherServlet.java:893)
在org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966)
... 58 more
引起:java.lang.IllegalArgumentException :类型不能为空
在org.hibernate.procedure.internal.AbstractParameterRegistrationImpl.setHibernateType(AbstractParameterRegistrationImpl.java:182)
在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.j ava:77)
at org.hibernate.procedure.internal.PositionalParameterRegistration。< init>(PositionalParameterRegistration.java:41)
at org.hibernate.procedure.internal.ProcedureCallImpl.registerParameter(ProcedureCallImpl.java :275)在org.hibernate.jpa.internal.StoredProcedureQueryImpl.registerStoredProcedureParameter(StoredProcedureQueryImpl.java:128

在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 $参考程序dureExecution.doExecute(JpaQueryExecution.java:295)
在org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:74)
在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)$在org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)b $ b。在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 说明:

UPDATE 1



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

  @Repository 
public interface ResourceTypeRepository扩展JpaRepository< ResourceType,Long> {

@Query(SELECT rt FROM ResourceType rt WHERE rt.inactive = 0)
List< ResourceType> getResourceTypes();

$ b b

$ p $ UPDATE 2



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



更新3

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



我在 org / springframework / data / jpa / repository / query / StoredProcedureJpaQuery.java ,如果您可能注意到在最后一行有一个函数调用,它获取参数总数并添加一个额外的参数。这可能是问题,额外的参数。不过,这可能不是问题,因为我认为它可以在不同情况下遵循另一条路径。

  / ** 
*从给定的{@link StoredProcedureQuery}中提取输出值。
*
* @param storedProcedureQuery不得为{@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());
}

返回storedProcedureQuery.getOutputParameterValue(getQueryMethod()。getParameters()。getNumberOfParameters()+ 1);

$ / code>


解决方案

Spring Data JPA从1.11开始.1不支持SP返回结果集。我向Spring Data提交了。



解决方案是降低API级别并仅使用JPA。这是我写的一个与MS SQL SPs一起工作的泛型类。

  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))
公共类StoredProcRepository {

//区域注入的bean(通过RequiredArgsConstructor)
private final EntityManager em;
// endregion
$ b $ ** ** b $ b *通过JPA调用存储过程并检索单个隐式结果集(在
*支持它们的数据库中,例如MS SQL或MySQL)。该调用不依赖于DB方言。是
*意识到大的结果集应该分页并且不能完全读入内存。在每次调用中重新创建
* StoredProcedureQuery实例及其参数。
*要执行MS SQL SP执行多个查询,请设置NOCOUNT ON。
*
* @param procedureName存储过程名称,可选地按照数据库语法限定
* @param resultClass通过JPA将每个结果集行转换(映射)到resultClass的实例
* @ param spArgs存储过程参数,位置提供(可以省略列表
*末尾的可选SP参数)
* @param< T>每个JPA
*返回的行实例类返回整个结果集
* /
public< T>列表与LT; 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();

$ b $ **
*通过JPA调用一个存储过程,并只检索单个隐式结果的顶行
* set(在支持它们的DB中如MS SQL或MySQL)。
*假设结果集至少有一行。
*通话不依赖于DB方言。
*请注意,较大的结果集应分页并且不能完全读入内存。
*在每次调用时重新创建StoredProcedureQuery实例及其参数。
*要执行MS SQL SP执行多个查询,请设置NOCOUNT ON。
*
* @param procedureName存储过程名称,可选地按照数据库语法限定
* @param resultClass通过JPA将每个结果集行转换(映射)到resultClass的实例
* @ param spArgs存储过程参数,位置提供(可以省略列表
*末尾的可选SP参数)
* @param< T>每个JPA
*返回的行实例类返回整个结果集
* /
public< T> T queryTopRowViaStoredProc(String procedureName,Class< T> resultClass,
Object ... spArgs){
return queryViaStoredProc(procedureName,resultClass,spArgs).get(0);


$ / code>

对于MS SQL SP,额外的要求是有对于执行多个查询的所有SP, SET NOCOUNT ON >。这可以通过至少三种方式之一来设置:


  1. 使用JPA的通用Java包装器(请参阅下面的代码)。此方法仅适用于jTDS JDBC驱动程序。 已提交给MS JDBC驱动程序项目。

  2. 在每个SP的开头。


是#1的代码:对于相同的 StoredProcRepository 类的相应方法。

  / ** 
*通过JPA调用MS SQL存储过程并检索单个隐式结果集。
*防止存储过程中缺少SET NOCOUNT。
*这适用于jTDS JDBC驱动程序,但不适用于MS JDBC驱动程序。
*请注意,较大的结果集应分页并且不能完全读入内存。
*
* @param procedureName存储过程名称,可选地按照数据库语法限定
* @param resultClass通过JPA将每个结果集行转换(映射)到resultClass的实例
* @ param spArgs存储过程参数,位置提供(可以省略列表
*末尾的可选SP参数)
* @param< T>每个JPA
*返回的行实例类返回整个结果集
* /
public< T>列表与LT; T> queryViaMsSqlStoredProc(String procedureName,Class< T> resultClass,
Object ... spArgs){
String spBindParams =(spArgs.length == 0)? :? + Strings.repeat(,?,spArgs.length - 1);

//以下内容适用于jTDS驱动程序,但不适用于MS驱动程序
String spQuery = String.format(EXEC%s%s,procedureName,spBindParams);

//以下内容适用于jTDS驱动程序,但不适用于MS驱动程序
/ *
String spQuery = String.format({call%s(%s)} ,procedureName,spBindParams);
Query q = em.createNativeQuery(SET NOCOUNT ON;+ spQuery,resultClass)
.setHint(org.hibernate.readOnly,true);
* /

查询q = em.createNativeQuery(spQuery,resultClass);
int pos = 0;
for(Object arg:spArgs){
q.setParameter(++ pos,arg);
}
return q.getResultList();
}

/ **
*通过JPA调用MS SQL存储过程,并仅检索单个隐式
*结果集的首行。
*假设结果集至少有一行。
*该调用设置NOCOUNT ONMS SQL批处理选项。
*请注意,较大的结果集应分页并且不能完全读入内存。
*
* @param procedureName存储过程名称,可选地按照数据库语法限定
* @param resultClass通过JPA将每个结果集行转换(映射)到resultClass的实例
* @ param spArgs存储过程参数,位置提供(可以省略列表
*末尾的可选SP参数)
* @param< T>每个JPA
*返回的行实例类返回整个结果集
* /
public< T> T queryTopRowViaMsSqlStoredProc(String procedureName,Class< T> resultClass,
Object ... spArgs){
return queryViaMsSqlStoredProc(procedureName,resultClass,spArgs).get(0);
}


I am confronting with an issue and it seems that many people encountered it and probably couldn't solve it.

I have the following MYSQL stored procedure. This is just a sample to see if everything works fine, then I would've start adding parameters like inactive.

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

The entity which maps the resource_types table and the named stored procedure query.

@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 make the call to the stored procedure

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

}

The getAll() method which resides in a @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 is this happening and how to fix it?

The documentation for InvalidDataAccessApiUsageException says:

UPDATE 1

If I change the repository into the following code, it will work, which means there are issues with name procedure query. I still need to make the stored procedure way to 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 am not using EclipseLink (or at least I don't know about his existence)

UPDATE 3

I could not found the cause of this problem even if I went through the code, debugging. But I found something that may help in finding the issue.

I found this piece of code in org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java and if you may notice that on the last line there is a call to a function where it gets the total number of parameters and adds an extra one. Here may be the problem, the extra parameter. Still, this may not be the issue because I suppose it can follow another path under 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);
}

Spring Data JPA as of 1.11.1 does not support SPs returning result sets. I filed a corresponding defect with Spring Data.

The solution is to descend an API level and just use JPA. Here is a generic class that I wrote that works with MS SQL SPs.

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 e.g. 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 e.g. 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, the additional requirement is to have SET NOCOUNT ON for all SPs executing more than one query. This could be set in one of at least three ways:

  1. In a generic Java wrapper using JPA (see code below). This approach works only with jTDS JDBC driver. A corresponding issue has been filed with MS JDBC driver project.
  2. In the beginning of each SP.
  3. Globally in your database.

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

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

这篇关于IllegalArgumentException:类型不能为空的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-30 22:19