简体   繁体   中英

How to implement optional parameter of list type in native query using Spring Data JPA

In my use case I need to pass a parameter of type List<> , but this parameter needs to be optional and I'm having trouble implementing it, can you help me?

Endpoint:

http://localhost:8080/api/v1/census-population/find?state=1,2

it's work: required parameter

@Query(value = "SELECT s.id, s.code, s.name, s.uf, " +
            "SUM(cp.population) AS statePopulation, " +
            "COUNT(cp.id) AS countCities " +
            "FROM census_population cp " +
            "INNER JOIN cities c ON c.id = cp.city_id " +
            "INNER JOIN states s ON s.id = c.state_id " +
            "WHERE s.id IN (:state) " +
            "GROUP BY s.code, s.name, s.uf"
            , nativeQuery = true)
    List<CensusStateStats> censusByStateList(@Param("state") List<Long> state);

Result:

[
    {
        "id": 1,
        "code": 11,
        "name": "Rondônia",
        "statePopulation": 1815278,
        "countCities": 52
    },
    {
        "id": 2,
        "code": 12,
        "name": "Acre",
        "statePopulation": 906876,
        "countCities": 22
    }
]

it's doesn't work: optional parameter

@Query(value = "SELECT s.id, s.code, s.name, s.uf, " +
            "SUM(cp.population) AS statePopulation, " +
            "COUNT(cp.id) AS countCities " +
            "FROM census_population cp " +
            "INNER JOIN cities c ON c.id = cp.city_id " +
            "INNER JOIN states s ON s.id = c.state_id " +
            "WHERE (:state IS NULL OR s.id IN (:state)) " +
            "GROUP BY s.code, s.name, s.uf"
            , nativeQuery = true)
    List<CensusStateStats> censusByStateList(@Param("state") List<Long> state);

Result:

{
    "timestamp": "2022-02-24T19:49:28.895+00:00",
    "status": 500,
    "error": "Internal Server Error",
    "trace": "org.springframework.dao.DataIntegrityViolationException: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.DataException: could not extract ResultSet\r\n\tat org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:280)\r\n\tat org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:233)\r\n\tat org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:551)\r\n\tat org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)\r\n\tat org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242)\r\n\tat org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:152)\r\n\tat org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)\r\n\tat org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:145)\r\n\tat org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)\r\n\tat org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)\r\n\tat org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)\r\n\tat org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215)\r\n\tat com.sun.proxy.$Proxy109.censusByStateList(Unknown Source)\r\n\tat com.example.demo.service.CensusPopulationService.censusByStateList(CensusPopulationService.java:27)\r\n\tat com.example.demo.controller.CensusPopulationController.censusByStateList(CensusPopulationController.java:38)\r\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\r\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\r\n\tat java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\r\n\tat java.base/java.lang.reflect.Method.invoke(Method.java:566)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)\r\n\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1067)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963)\r\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)\r\n\tat org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)\r\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:655)\r\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)\r\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:764)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\r\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\r\n\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\r\n\tat org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\r\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\r\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197)\r\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)\r\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:540)\r\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135)\r\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)\r\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)\r\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357)\r\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:382)\r\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)\r\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:895)\r\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1722)\r\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\r\n\tat org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)\r\n\tat org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)\r\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\r\n\tat java.base/java.lang.Thread.run(Thread.java:829)\r\nCaused by: org.hibernate.exception.DataException: could not extract ResultSet\r\n\tat org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:52)\r\n\tat org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:37)\r\n\tat org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:113)\r\n\tat org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:99)\r\n\tat org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:67)\r\n\tat org.hibernate.loader.Loader.getResultSet(Loader.java:2322)\r\n\tat org.hibernate.loader.Loader.executeQueryStatement(Loader.java:2075)\r\n\tat org.hibernate.loader.Loader.executeQueryStatement(Loader.java:2037)\r\n\tat org.hibernate.loader.Loader.doQuery(Loader.java:956)\r\n\tat org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:357)\r\n\tat org.hibernate.loader.Loader.doList(Loader.java:2868)\r\n\tat org.hibernate.loader.Loader.doList(Loader.java:2850)\r\n\tat org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2682)\r\n\tat org.hibernate.loader.Loader.list(Loader.java:2677)\r\n\tat org.hibernate.loader.custom.CustomLoader.list(CustomLoader.java:338)\r\n\tat org.hibernate.internal.SessionImpl.listCustomQuery(SessionImpl.java:2195)\r\n\tat org.hibernate.internal.AbstractSharedSessionContract.list(AbstractSharedSessionContract.java:1190)\r\n\tat org.hibernate.query.internal.NativeQueryImpl.doList(NativeQueryImpl.java:177)\r\n\tat org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:1617)\r\n\tat org.hibernate.query.Query.getResultList(Query.java:165)\r\n\tat org.springframework.data.jpa.repository.query.JpaQueryExecution$CollectionExecution.doExecute(JpaQueryExecution.java:128)\r\n\tat org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:90)\r\n\tat org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:155)\r\n\tat org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:143)\r\n\tat org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:137)\r\n\tat org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:121)\r\n\tat org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:159)\r\n\tat org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:138)\r\n\tat org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)\r\n\tat org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:80)\r\n\tat org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)\r\n\tat org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123)\r\n\tat org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388)\r\n\tat org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)\r\n\tat org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)\r\n\tat org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137)\r\n\t... 59 more\r\nCaused by: org.h2.jdbc.JdbcSQLDataException: Data conversion error converting \"ROW (1, TRUE)\"; SQL statement:\nSELECT s.id, s.code, s.name, s.uf, SUM(cp.population) AS statePopulation, COUNT(cp.id) AS countCities, FROM census_population cp INNER JOIN cities c ON c.id = cp.city_id INNER JOIN states s ON s.id = c.state_id WHERE (?, ? IS NULL OR s.id IN (?, ?)) GROUP BY s.code, s.name, s.uf [22018-200]\r\n\tat org.h2.message.DbException.getJdbcSQLException(DbException.java:457)\r\n\tat org.h2.message.DbException.getJdbcSQLException(DbException.java:429)\r\n\tat org.h2.message.DbException.get(DbException.java:194)\r\n\tat org.h2.value.Value.convertTo(Value.java:875)\r\n\tat org.h2.value.Value.convertTo(Value.java:737)\r\n\tat org.h2.value.Value.getBoolean(Value.java:561)\r\n\tat org.h2.expression.condition.ConditionAndOr.getValue(ConditionAndOr.java:101)\r\n\tat org.h2.expression.Expression.getBooleanValue(Expression.java:258)\r\n\tat org.h2.command.dml.Select.isConditionMet(Select.java:448)\r\n\tat org.h2.command.dml.Select.gatherGroup(Select.java:522)\r\n\tat org.h2.command.dml.Select.queryGroup(Select.java:487)\r\n\tat org.h2.command.dml.Select.queryWithoutCache(Select.java:839)\r\n\tat org.h2.command.dml.Query.queryWithoutCacheLazyCheck(Query.java:201)\r\n\tat org.h2.command.dml.Query.query(Query.java:489)\r\n\tat org.h2.command.dml.Query.query(Query.java:451)\r\n\tat org.h2.command.CommandContainer.query(CommandContainer.java:285)\r\n\tat org.h2.command.Command.executeQuery(Command.java:195)\r\n\tat org.h2.jdbc.JdbcPreparedStatement.executeQuery(JdbcPreparedStatement.java:114)\r\n\tat com.zaxxer.hikari.pool.ProxyPreparedStatement.executeQuery(ProxyPreparedStatement.java:52)\r\n\tat com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeQuery(HikariProxyPreparedStatement.java)\r\n\tat org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:57)\r\n\t... 90 more\r\nCaused by: java.lang.NumberFormatException: Character R is neither a decimal digit number, decimal point, nor \"e\" notation exponential mark.\r\n\tat java.base/java.math.BigDecimal.<init>(BigDecimal.java:518)\r\n\tat java.base/java.math.BigDecimal.<init>(BigDecimal.java:401)\r\n\tat java.base/java.math.BigDecimal.<init>(BigDecimal.java:834)\r\n\tat org.h2.value.Value.convertToBoolean(Value.java:908)\r\n\tat org.h2.value.Value.convertTo(Value.java:800)\r\n\t... 107 more\r\n",
    "message": "could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.DataException: could not extract ResultSet",
    "path": "/api/v1/census-population/find"
}

My classes:

MODELS

State

package com.example.demo.model;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;

@Entity
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "states")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class State {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private Integer code;
    private String name;

    @Column(length = 2)
    private String uf;

}

City

package com.example.demo.model;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;

@Entity
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "cities")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class City {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "state_id", nullable = false, foreignKey = @ForeignKey(name = "fk_cities_states1"))
    private State state;
    private String  code;
    private String name;

}

Cencus

package com.example.demo.model;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;

@Entity
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "census")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Census {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

}

CensusPopulation

package com.example.demo.model;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;

@Entity
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "census")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Census {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

}

DTO

CensusStateStats

package com.example.demo.dto;

public interface CensusStateStats {
    Long getId();
    Integer getCode();
    String getName();
    Long getStatePopulation();
    Long getCountCities();
}

REPOSITORY

CensusPopulationRep

package com.example.demo.repository;

import com.example.demo.dto.CensusStateCitiesStats;
import com.example.demo.dto.CensusStateStats;
import com.example.demo.model.CensusPopulation;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.List;

public interface CensusPopulationRep extends JpaRepository<CensusPopulation, Long> {

    @Query(value = "SELECT s.id, s.code, s.name, s.uf, " +
            "SUM(cp.population) AS statePopulation, " +
            "COUNT(cp.id) AS countCities " +
            "FROM census_population cp " +
            "INNER JOIN cities c ON c.id = cp.city_id " +
            "INNER JOIN states s ON s.id = c.state_id " +
            "WHERE (:state IS NULL OR s.id IN (:state)) " +
            "GROUP BY s.code, s.name, s.uf"
            , nativeQuery = true)
    List<CensusStateStats> censusByStateList(@Param("state") List<Long> state);

}

SERVICE

CensusPopulationService

package com.example.demo.service;

import com.example.demo.dto.CensusStateCitiesStats;
import com.example.demo.dto.CensusStateStats;
import com.example.demo.model.CensusPopulation;
import com.example.demo.repository.CensusPopulationRep;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
@RequiredArgsConstructor
public class CensusPopulationService {

    private final CensusPopulationRep censusPopulationRep;    

    public List<CensusStateStats> censusByStateList(List<Long> state) {
        return censusPopulationRep.censusByStateList(state);
    }

}

CONTROLLER

CensusPopulationController

package com.example.demo.controller;

import com.example.demo.dto.*;
import com.example.demo.model.CensusPopulation;
import com.example.demo.service.CensusPopulationService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@RestController
@RequestMapping("api/v1/census-population")
@RequiredArgsConstructor
public class CensusPopulationController {

    private final CensusPopulationService censusPopulationService;

  

    @GetMapping("/find")
    public ResponseEntity<List<CensusStateStatsDto>> censusByStateList(@RequestParam(required = false) List<Long> state) {
        return ResponseEntity.ok(censusPopulationService.censusByStateList(state).stream()
                .map(CensusStateStatsDto::new)
                .collect(Collectors.toList()));
    }

}

Assuming the values (1,2) are going in your (:state), it would look like this: WHERE (1,2 IS NULL OR s.id IN(1,2))

Please review this code which should resolve your issue.

Ex: WHERE COALESCE( null, :state ) is null or s.id in (:state)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM