【问题标题】:Spring Boot CRUD REST API + Spring Data JPA + H2 DB: Getting NPE on tests but not on PostmanSpring Boot CRUD REST API + Spring Data JPA + H2 DB:在测试中获取 NPE 而不是 Postman
【发布时间】:2021-10-12 08:09:16
【问题描述】:

我正在做一些练习来熟悉 Spring 框架并使用 REST API。我确实通过了练习,但没有通过我最初的实现。我想知道为什么我在测试中获得 NPE 而在 Postman 中却没有。

在尝试解决此测验时,测试抛出了 NPE:

{
  "title": "Math4",
  "text": "Which of the following is equal to 4?",
  "options": ["2*3", "5*8", "8*0", "1*5"],
  "answer": null
}

当我使用 Postman 将此测验发布到 dB 并解决时,一切正常。

但是当我通过测试运行它时,我收到以下信息:

堆栈跟踪:

java.lang.NullPointerException: null
at engine.presentation.controllers.QuizController.solveQuiz(QuizController.java:44) ~[main/:na]
at jdk.internal.reflect.GeneratedMethodAccessor90.invoke(Unknown Source) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:888) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:660) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at jdk.internal.reflect.GeneratedMethodAccessor64.invoke(Unknown Source) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:282) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:279) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at java.base/java.security.AccessController.doPrivileged(Native Method) ~[na:na]
at java.base/javax.security.auth.Subject.doAsPrivileged(Subject.java:550) ~[na:na]
at org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:314) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:170) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:225) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.access$000(ApplicationFilterChain.java:47) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:149) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:145) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at java.base/java.security.AccessController.doPrivileged(Native Method) ~[na:na]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:144) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.29.jar:9.0.29]
at jdk.internal.reflect.GeneratedMethodAccessor47.invoke(Unknown Source) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:282) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:279) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at java.base/java.security.AccessController.doPrivileged(Native Method) ~[na:na]
at java.base/javax.security.auth.Subject.doAsPrivileged(Subject.java:550) ~[na:na]
at org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:314) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:253) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:191) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.access$000(ApplicationFilterChain.java:47) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:149) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:145) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at java.base/java.security.AccessController.doPrivileged(Native Method) ~[na:na]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:144) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at jdk.internal.reflect.GeneratedMethodAccessor47.invoke(Unknown Source) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:282) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:279) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at java.base/java.security.AccessController.doPrivileged(Native Method) ~[na:na]
at java.base/javax.security.auth.Subject.doAsPrivileged(Subject.java:550) ~[na:na]
at org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:314) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:253) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:191) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.access$000(ApplicationFilterChain.java:47) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:149) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:145) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at java.base/java.security.AccessController.doPrivileged(Native Method) ~[na:na]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:144) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at jdk.internal.reflect.GeneratedMethodAccessor47.invoke(Unknown Source) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:282) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:279) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at java.base/java.security.AccessController.doPrivileged(Native Method) ~[na:na]
at java.base/javax.security.auth.Subject.doAsPrivileged(Subject.java:550) ~[na:na]
at org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:314) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:253) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:191) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.access$000(ApplicationFilterChain.java:47) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:149) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:145) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at java.base/java.security.AccessController.doPrivileged(Native Method) ~[na:na]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:144) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:108) ~[spring-boot-actuator-2.2.2.RELEASE.jar:2.2.2.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at jdk.internal.reflect.GeneratedMethodAccessor47.invoke(Unknown Source) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:282) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:279) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at java.base/java.security.AccessController.doPrivileged(Native Method) ~[na:na]
at java.base/javax.security.auth.Subject.doAsPrivileged(Subject.java:550) ~[na:na]
at org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:314) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:253) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:191) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.access$000(ApplicationFilterChain.java:47) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:149) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:145) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at java.base/java.security.AccessController.doPrivileged(Native Method) ~[na:na]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:144) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at jdk.internal.reflect.GeneratedMethodAccessor47.invoke(Unknown Source) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:282) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:279) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at java.base/java.security.AccessController.doPrivileged(Native Method) ~[na:na]
at java.base/javax.security.auth.Subject.doAsPrivileged(Subject.java:550) ~[na:na]
at org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:314) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:253) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:191) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.access$000(ApplicationFilterChain.java:47) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:149) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:145) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at java.base/java.security.AccessController.doPrivileged(Native Method) ~[na:na]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:144) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:526) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:367) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:860) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1591) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at java.base/java.lang.Thread.run(Thread.java:829) ~[na:na]

build.gradle

plugins {
    id 'org.springframework.boot' version '2.2.2.RELEASE'
    id 'java'
    id "io.freefair.lombok" version "6.1.0-m3"
}

apply plugin: 'io.spring.dependency-management'

sourceCompatibility = 11

repositories {
    mavenCentral()
}

sourceSets.main.resources.srcDirs = ["src/resources"]

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter'
    implementation 'org.springframework.boot:spring-boot-starter-actuator'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    runtimeOnly 'com.h2database:h2'
}

应用程序设置

server.port=8889
management.endpoints.web.exposure.include=*
management.endpoint.shutdown.enabled=true

# H2 data source setup
spring.datasource.url=jdbc:h2:file:../quizdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect


# Automatically update tables when persistence objects have changed
spring.jpa.hibernate.ddl-auto=update

# Setup for the H2 console, used for viewing data in the database
spring.h2.console.enabled=true
spring.h2.console.settings.trace=false
spring.h2.console.settings.web-allow-others=false

# Show SQL statements generated by Spring ORM
spring.jpa.show-sql=true

测验.java:

@Entity
@Table(name = "quizzes")
@NoArgsConstructor
@Getter
@Setter
@ToString
@JsonPropertyOrder({
        "id",
        "title",
        "text",
        "options"
})
public class Quiz {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @JsonProperty(access = JsonProperty.Access.READ_ONLY)
    private int id;

    @NotBlank(message = "Title is required")
    private String title;

    @NotBlank(message = "Text is required")
    private String text;

    @NotEmpty(message = "There must be at least 2 options")
    @Size(min = 2, message = "There must be at least 2 options")
    private LinkedHashSet<String> options;

    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    private HashSet<Integer> answer;

    public void setAnswer(HashSet<Integer> answer) {
        this.answer = Optional.ofNullable(answer).orElseGet(HashSet::new);
    }
}

QuizController.java

@RestController
@RequestMapping("/api")
public class QuizController {

    private final QuizService quizService;

    @Autowired
    public QuizController(QuizService quizService) {
        this.quizService = quizService;
    }

    @GetMapping("/quizzes")
    public Iterable<Quiz> getQuizzes() {
        return quizService.findAll();
    }

    @GetMapping("/quizzes/{id}")
    public Quiz getQuiz(@PathVariable @Min(0) int id) {
        return quizService.findById(id);
    }

    @PostMapping("/quizzes")
    public Quiz saveQuiz(@RequestBody @Valid Quiz quiz) {
        return quizService.save(quiz);
    }

    @PostMapping("/quizzes/{id}/solve")
    public Response solveQuiz(@RequestBody @Valid AnswerDto answer,
                          @PathVariable @Min(0) int id) {
        return Response.builder()
                .success(quizService.findById(id).getAnswer().equals(answer.getAnswer()))
                .build();
    }
}

QuizService.java:

    @Service
public class QuizService {
    private final QuizRepository repository;

    @Autowired
    public QuizService(QuizRepository repository) {
        this.repository = repository;
    }

    public Iterable<Quiz> findAll() {
        return repository.findAll();
    }

    public Quiz findById(int id) {
        Optional<Quiz> quiz = repository.findById(id);

        if (quiz.isEmpty()) {
            throw new QuizNotFoundException(id);
        }

        return quiz.get();
    }

    public Quiz save(Quiz quiz) {
        return repository.save(quiz);
    }
}

我不明白的是,如果 setAnswer 方法从未将字段设置为空,那么如何使用空答案字段创建测验。根据我的理解,如果我弄错了,请纠正我。当使用@RequestBody 将JSON 反序列化为对象时,它使用默认(无参数)构造函数和定义的setter 方法来实例化对象。另外,为什么我无法在 Postman 上复制问题?

这仅用于教育目的。即使我解决了问题,我也想了解我从哪里开始出错。任何帮助都非常感谢。

编辑:为澄清起见,如果在创建 Quiz 对象时传递了一个空值作为答案,则会创建一个空的 HashSet。这应该反序列化为一个空数组,如上面的邮递员图像所示。当我发布测验的答案时,我收到了成功响应,因为答案是 [];

【问题讨论】:

  • 请同时发布测试。似乎QuizService 没有自动连接到QuizController
  • 测试是从第 3 方生成的,除了在调用方法时将一些语句打印到控制台之外,我无权访问正在运行的测试。我确实编辑了我的帖子并添加了 QuizService 类。我有通过构造函数自动装配的存储库,以及通过 QuizController 类的构造函数自动装配的服务。有错吗?
  • 构造函数注入非常好。我想说,与现场注入相比,它甚至是更先进的方法。从堆栈跟踪中不清楚哪个变量是null。如果它在本地运行但不在测试中,我的第一个猜测是quizService。但也可能是第一个quizService.findById(id).getAnswer()的返回值。没有看到测试设置,很难说。
  • @hpoettker 方法返回测验或抛出异常public Quiz findById(int id) { Optional&lt;Quiz&gt; quiz = repository.findById(id); if (quiz.isEmpty()) { throw new QuizNotFoundException(id); } return quiz.get(); }
  • @hpoeeker 我在solveQuiz 上打印到控制台Quiz 对象。 Quiz(id=6, title=Math4, text=Which of the following is equal to 4?, options=[2*3, 5*8, 8*0, 1*5], answer=null) 2021-08-08 10:26:48.784 ERROR 4840 --- [io-8889-exec-10] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause

标签: spring-boot rest jpa nullpointerexception crud


【解决方案1】:
问题是
quizService.findById(id).getAnswer().equals(answer.getAnswer())

当第一个getAnswer() 返回null 时失败,equals 不能被调用。

您可以改用 null-safe Objects::equals

Objects.equals(quizService.findById(id).getAnswer(), answer.getAnswer())

问题在于测试发送的测验 json 没有明确的 answer 字段。在这种情况下,杰克逊不会调用您的设置器。要强制执行当前在 setter 中的逻辑,带有 @JsonCreator 的构造函数应该会有所帮助。

【讨论】:

  • 是的,我明白这也是问题所在,问题是如果我的 setter 方法从不将答案设置为 null,为什么 getAnswer 返回 null。
  • public void setAnswer(HashSet&lt;Integer&gt; answer) { this.answer = Optional.ofNullable(answer).orElseGet(HashSet::new); }
  • 这就是test和postman的区别:
  • Quiz(id=6, title=Math4, text=Which of the following is equal to 4?, options=[2*3, 5*8, 8*0, 1*5], answer=null) AnswerDto(answer=[])
  • 因为测试发送的 json 没有 answer 字段。使用邮递员,您可以将其明确设置为null。这与 setter 不同。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-05
  • 2017-02-03
  • 2023-03-25
  • 2021-08-28
  • 2019-01-23
  • 2018-03-20
相关资源
最近更新 更多