it-roy-ru.com

Ошибка в NamedStoredProcedureQuery в Spring JPA - "Найден именованный параметр хранимой процедуры, связанный с позиционными параметрами"

Я пытаюсь вызвать хранимую процедуру, написанную в Postgresql, используя NamedStoredProcedureQuery, предоставленный Spring JPA. Ниже приведены фрагменты кода.

EntityMovement.Java

@Entity
@Table(name = "entity_movement")
@NamedStoredProcedureQueries({
    @NamedStoredProcedureQuery(name = "near_by_entities", 
                               procedureName = "near_by_entities",
                               parameters = {
                                     @StoredProcedureParameter(mode = ParameterMode.IN, name = "location", type = String.class),
                                     @StoredProcedureParameter(mode = ParameterMode.IN, name = "radius", type = Double.class),
                                     @StoredProcedureParameter(mode = ParameterMode.REF_CURSOR, type = void.class)
                               })
})
public class EntityMovement implements Serializable{

//Fields

//Getters and Setters

}

EntityMovementRepository

@Repository
public interface EntityMovementRepository extends JpaRepository<EntityMovement, Entity>{
    @Procedure(name = "near_by_entities")
    public List<EntityMovement> nearByEntities(@Param("location")String location,@Param("radius")double radius);

}

Звонок  

List<EntityMovement> entityMovements= entityMovementRepository.nearByEntities(location, radius);

Stored Procedure

Запрос упрощен

CREATE OR REPLACE FUNCTION public.near_by_entities(
location character varying,
radius double precision)
RETURNS refcursor
LANGUAGE 'plpgsql'
AS $BODY$
DECLARE ref refcursor;
BEGIN
OPEN ref FOR SELECT * FROM public.entity_movement;
RETURN ref;
END
$BODY$;

Трассировки стека

org.springframework.dao.InvalidDataAccessApiUsageException: Found named stored procedure parameter associated with positional parameters; nested exception is Java.lang.IllegalStateException: Found named stored procedure parameter associated with positional parameters
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.Java:381) ~[spring-orm-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.Java:246) ~[spring-orm-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.Java:488) ~[spring-orm-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.Java:59) ~[spring-tx-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.Java:213) ~[spring-tx-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.Java:147) ~[spring-tx-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.Java:179) ~[spring-aop-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.Java:133) ~[spring-data-jpa-1.11.7.RELEASE.jar:na]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.Java:179) ~[spring-aop-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.Java:92) ~[spring-aop-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.Java:179) ~[spring-aop-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.Java:57) ~[spring-data-commons-1.13.7.RELEASE.jar:na]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.Java:179) ~[spring-aop-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.Java:213) ~[spring-aop-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at com.Sun.proxy.$Proxy210.nearByEntities(Unknown Source) ~[na:na]
at com.onwards.LocationEngine.business.EntityMovementBusinessImpl.findNearByEntities(EntityMovementBusinessImpl.Java:38) ~[classes/:0.0.1-SNAPSHOT]
at com.onwards.LocationEngine.business.EntityMovementBusinessImpl$$FastClassBySpringCGLIB$$99567b2c.invoke(<generated>) ~[classes/:0.0.1-SNAPSHOT]
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.Java:204) ~[spring-core-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.Java:738) ~[spring-aop-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.Java:157) ~[spring-aop-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.Java:99) ~[spring-tx-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.Java:282) ~[spring-tx-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.Java:96) ~[spring-tx-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.Java:179) ~[spring-aop-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.Java:673) ~[spring-aop-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at com.onwards.LocationEngine.business.EntityMovementBusinessImpl$$EnhancerBySpringCGLIB$$b7870dee.findNearByEntities(<generated>) ~[classes/:0.0.1-SNAPSHOT]
at com.onwards.LocationEngine.controller.EntityMovementController.findNearByEntities(EntityMovementController.Java:37) ~[classes/:0.0.1-SNAPSHOT]
at Sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_144]
at Sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.Java:62) ~[na:1.8.0_144]
at Sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.Java:43) ~[na:1.8.0_144]
at Java.lang.reflect.Method.invoke(Method.Java:498) ~[na:1.8.0_144]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.Java:205) ~[spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.Java:133) ~[spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.Java:97) ~[spring-webmvc-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.Java:827) ~[spring-webmvc-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.Java:738) ~[spring-webmvc-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.Java:85) ~[spring-webmvc-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.Java:967) ~[spring-webmvc-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.Java:901) ~[spring-webmvc-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.Java:970) ~[spring-webmvc-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.Java:861) ~[spring-webmvc-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.Java:635) ~[servlet-api.jar:na]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.Java:846) ~[spring-webmvc-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.Java:742) ~[servlet-api.jar:na]
at org.Apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.Java:231) [catalina.jar:8.5.23]
at org.Apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.Java:166) [catalina.jar:8.5.23]
at org.Apache.Tomcat.websocket.server.WsFilter.doFilter(WsFilter.Java:52) ~[Tomcat-websocket.jar:8.5.23]
at org.Apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.Java:193) [catalina.jar:8.5.23]
at org.Apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.Java:166) [catalina.jar:8.5.23]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.Java:99) ~[spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.Java:107) [spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.Apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.Java:193) [catalina.jar:8.5.23]
at org.Apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.Java:166) [catalina.jar:8.5.23]
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.Java:108) ~[spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.Java:107) [spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.Apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.Java:193) [catalina.jar:8.5.23]
at org.Apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.Java:166) [catalina.jar:8.5.23]
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.Java:81) ~[spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.Java:107) [spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.Apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.Java:193) [catalina.jar:8.5.23]
at org.Apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.Java:166) [catalina.jar:8.5.23]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.Java:197) ~[spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.Java:107) [spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.Apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.Java:193) [catalina.jar:8.5.23]
at org.Apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.Java:166) [catalina.jar:8.5.23]
at org.springframework.boot.web.support.ErrorPageFilter.doFilter(ErrorPageFilter.Java:115) [spring-boot-1.5.7.RELEASE.jar:1.5.7.RELEASE]
at org.springframework.boot.web.support.ErrorPageFilter.access$000(ErrorPageFilter.Java:59) [spring-boot-1.5.7.RELEASE.jar:1.5.7.RELEASE]
at org.springframework.boot.web.support.ErrorPageFilter$1.doFilterInternal(ErrorPageFilter.Java:90) [spring-boot-1.5.7.RELEASE.jar:1.5.7.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.Java:107) [spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.boot.web.support.ErrorPageFilter.doFilter(ErrorPageFilter.Java:108) [spring-boot-1.5.7.RELEASE.jar:1.5.7.RELEASE]
at org.Apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.Java:193) [catalina.jar:8.5.23]
at org.Apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.Java:166) [catalina.jar:8.5.23]
at org.Apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.Java:199) [catalina.jar:8.5.23]
at org.Apache.catalina.core.StandardContextValve.invoke(StandardContextValve.Java:96) [catalina.jar:8.5.23]
at org.Apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.Java:478) [catalina.jar:8.5.23]
at org.Apache.catalina.core.StandardHostValve.invoke(StandardHostValve.Java:140) [catalina.jar:8.5.23]
at org.Apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.Java:81) [catalina.jar:8.5.23]
at org.Apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.Java:650) [catalina.jar:8.5.23]
at org.Apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.Java:87) [catalina.jar:8.5.23]
at org.Apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.Java:342) [catalina.jar:8.5.23]
at org.Apache.coyote.http11.Http11Processor.service(Http11Processor.Java:803) [Tomcat-coyote.jar:8.5.23]
at org.Apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.Java:66) [Tomcat-coyote.jar:8.5.23]
at org.Apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.Java:868) [Tomcat-coyote.jar:8.5.23]
at org.Apache.Tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.Java:1459) [Tomcat-coyote.jar:8.5.23]
at org.Apache.Tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.Java:49) [Tomcat-coyote.jar:8.5.23]
at Java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.Java:1149) [na:1.8.0_144]
at Java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.Java:624) [na:1.8.0_144]
at org.Apache.Tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.Java:61) [Tomcat-util.jar:8.5.23]
at Java.lang.Thread.run(Thread.Java:748) [na:1.8.0_144]

Я новичок в Spring JPA, и это аннотации. Имя параметра четко упоминается в @StoredProcedureParameter, и то же самое используется с функцией @param в репозитории. Это выглядит как прямое сообщение об ошибке, так как в нем говорится, что я использую именованные параметры вместо позиционных параметров, и мне не хватает чего-то очень очевидного. Но я не могу найти решение ни на одном из форумов .... Любая помощь будет оценена. Спасибо!!

РЕДАКТИРОВАТЬ - Добавление структуры таблицы

CREATE TABLE public.entity_movement ( entity bigint NOT NULL, location geography NOT NULL, movement_time timestamp with time zone NOT NULL, CONSTRAINT pk_entity PRIMARY KEY (entity), CONSTRAINT fk2sd7ux7x1atbbpdl4y0lwc9la FOREIGN KEY (entity) REFERENCES public.entity (id) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION, CONSTRAINT fk_entity FOREIGN KEY (entity) REFERENCES public.entity (id) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE )

12
PriyaAnil

Корень проблемы в том, что вы смешиваете именованные и позиционные параметры. Спецификация JPA 2.1 в разделе 3.10.17.1 Именованные хранимые процедуры Queries утверждает, что это использование приводит к неопределенному поведению:

Если используются имена параметров, имя параметра используется для привязки значения параметра и извлечения выходного значения (если параметр является параметром INOUT или OUT). Если имена параметров не указаны, предполагается, что используются позиционные параметры. Смешивание именованных и позиционных параметров не определено.

Это также может быть причиной того, что Hibernate - при определении стратегии параметра - проверяет только первый параметр хранимой процедуры в ParameterDefinition#L156 .

Сообщение об ошибке «Найден именованный параметр хранимой процедуры, связанный с позиционными параметрами» немного вводит в заблуждение, потому что в ProcedureCallImpl#L423 то же сообщение об ошибке используется, когда указан параметр стратегии, но параметр позиционный и наоборот. В вашем случае сообщение об ошибке должно быть таким: «Найден параметр позиционной хранимой процедуры, связанный с именованными параметрами» (поскольку в вашем случае стратегия была определена как именованная, но ваш последний параметр REF_CURSOR является позиционным).

Чтобы это исправить, мы можем добавить имя к параметру REF_CURSOR:

@StoredProcedureParameter(mode = ParameterMode.REF_CURSOR, name = "out", type = void.class)

Но, к сожалению, это приведет к другому (вводящему в заблуждение) сообщению об ошибке:

org.springframework.orm.jpa.JpaSystemException: PostgreSQL supports only one REF_CURSOR parameter, but multiple were registered

Несмотря на то, что зарегистрирован только один параметр REF_CURSOR, мы получаем сообщение об ошибке при регистрации более одного. Исключение вызывается PostgresCallableStatementSupport#L66 , и фактически его метод renderCallableStatement() содержит несколько полезных сведений о требованиях, когда определен параметр REF_CURSOR:

  • Это должен быть первый параметр
  • Стратегия параметра должна быть позиционной

А также в комментарии к методу renderCallableStatement() явно указывается, что смешивание именованных и позиционных параметров недопустимо.

Поэтому мы должны удалить предоставленные имена:

@StoredProcedureParameter(mode = ParameterMode.REF_CURSOR, type = void.class),
@StoredProcedureParameter(mode = ParameterMode.IN, type = String.class),
@StoredProcedureParameter(mode = ParameterMode.IN, type = Double.class)

Поскольку в настоящее время Spring Data не поддерживает переопределение позиционного сопоставления параметров (только именованное сопоставление параметров ) и наш первый параметр - REF_CURSOR, мы получаем следующее сообщение об ошибке, когда Spring Data пытается сопоставить REF_CURSOR с первым параметром метода, определенным в интерфейс репозитория:

InvalidDataAccessApiUsageException: Parameter value [location] did not match expected type [void (n/a)]

Поэтому @Procedure больше нельзя использовать, но в качестве обходного пути мы можем создать и реализовать отдельный интерфейс EntityMovementRepositoryWithProcedure и выполнить сопоставление вручную:

public interface EntityMovementRepositoryWithProcedure {
    List<EntityMovement> nearByEntities(String location, double radius);
}

@Repository
public interface EntityMovementRepository extends JpaRepository<EntityMovement, Integer>, EntityMovementRepositoryWithProcedure { }

public class EntityMovementRepositoryImpl implements EntityMovementRepositoryWithProcedure {

    @PersistenceContext
    private EntityManager em;

    @Override
    public List<EntityMovement> nearByEntities(String location, double radius) {
        StoredProcedureQuery nearByEntities em.createNamedStoredProcedureQuery("near_by_entities");
        nearByEntities.setParameter(2, location);
        nearByEntities.setParameter(3, radius);
        return nearByEntities.getResultList();
    }
}

Также autocommit должен быть отключен при использовании PostgreSQL REF_CURSOR в противном случае при вызове хранимой процедуры будет выдано следующее исключение:

PSQLException: ERROR: cursor "<unnamed portal 1>" does not exist

Полностью рабочий пример доступен здесь: https://github.com/sandor-balazs/example/tree/master/spring-data-postgresql-refcursor .

4
Sandor Balazs

Можете ли вы попробовать добавить имя к параметру out:

@StoredProcedureParameter(mode = ParameterMode.REF_CURSOR, name = "out", type = void.class)
0
Simon Martinelli

Согласно примеру jpa-данных для jpa2.1 для использования хранимой процедуры

spring-data-examples

Существует две различные интерпретации вызова метода репозитория: одна явно отображает метаданные аннотации, а другая получает метаданные процедуры из репозитория.

Вызов UserRepository.plus1BackedByOtherNamedStoredProcedure (…) выполнит хранимую процедуру plus1inout с использованием метаданных, объявленных в классе домена пользователя.

UserRepository.plus1inout (…) будет извлекать метаданные хранимой процедуры из репозитория и по умолчанию будет привязывать позиционный параметр и ожидать единственного выходного параметра резервной хранимой процедуры.

Здесь, это может быть в том случае, если вызов nearByEntities разрешается производным от репо, и это позиционно?

Чтобы попробовать, мы можем обновить имя внутри аннотации

 @NamedStoredProcedureQuery(name = "near_by_entities", 

К

 @NamedStoredProcedureQuery(name = "EntityMovement.nearByEntities", 

Вместе с 

@Procedure(name = "near_by_entities")
public List<EntityMovement> nearByEntities(@Param("location")String location,@Param("radius")double radius);

К 

@Procedure(name = "EntityMovement.nearByEntities")
public List<EntityMovement> nearByEntitiesNamed(@Param("location")String location,@Param("radius")double radius);

Звонить будет 

List<EntityMovement> entityMovements= entityMovementRepository.nearByEntitiesNamed(location, radius);
0
Rizwan