JPA 在 Spring Boot 中导致 java.lang.NullPointerException

Posted

技术标签:

【中文标题】JPA 在 Spring Boot 中导致 java.lang.NullPointerException【英文标题】:JPA Causing java.lang.NullPointerException in Spring Boot 【发布时间】:2018-05-29 09:04:13 【问题描述】:

我正在尝试在我的 Spring Boot 应用程序中运行一些单元测试并收到以下错误:

我不知道这是因为它无法连接到我在 Heroku (ClearDB) 中的数据库,还是我的代码中有一些注释错误。

我在运行时将环境变量加载到我的application-dev.properties 文件中,所有这些信息都可以在底部看到。

错误

java.lang.NullPointerException
    at com.algoq.algoq.services.AlgorithmService.getSubscribers(AlgorithmService.java:26)
    at com.algoq.algoq.ExampleTest.subscriberListNull(ExampleTest.java:42)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    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.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
    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:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

测试类

package com.algoq.algoq;
import com.algoq.algoq.models.Subscriber;
import com.algoq.algoq.respositories.SubscriberRepository;
import com.algoq.algoq.services.AlgorithmService;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Bean;
import org.springframework.test.context.junit4.SpringRunner;


import java.util.ArrayList;

import static org.assertj.core.api.Assertions.assertThat;

@RunWith(SpringRunner.class)
@TestConfiguration
@SpringBootTest(classes = 
        AlgoQApplication.class,
        )
public class ExampleTest extends AlgoQApplicationTests 

    @Autowired
    private AlgorithmService aService;

    @MockBean
    private SubscriberRepository employeeRepository;

    @Bean
    public AlgorithmService aService() 
        return new AlgorithmService();
    


    @Test
    public void subscriberListNull() throws Exception 
        ArrayList<Subscriber> subs = aService.getSubscribers();
        assertThat(subs).isEmpty();
    


服务类

package com.algoq.algoq.services;

import com.algoq.algoq.models.Subscriber;
import com.algoq.algoq.respositories.SubscriberRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;

@Service
public class AlgorithmService 

    @Autowired
    private SubscriberRepository subRep;

    /**
     * Gets a list of subscribers to return to the API
     * @return
     */
    public ArrayList<Subscriber> getSubscribers() 
        ArrayList<Subscriber> subscribers = new ArrayList<>();
        subRep.findAll()
                .forEach(subscribers::add);
        return subscribers;
    

    /**
     * Adds a new subscriber to the database
     * @param sub
     * @return
     */
    public void addSubscriber(Subscriber sub) 
        subRep.save(sub);
    

    /**
     * Finds a single user id
     * @param email
     * @return
     */
    public List<Subscriber> getSubscriber(String email) 
        return subRep.findByEmailAddress(email);
    

POM

http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0

<groupId>com.algoQ</groupId>
<artifactId>algo-q</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>algo-q</name>
<description>An algorithm a day keeps the brain up to date</description>

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.9.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.9</java.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-rest</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-core</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-mail</artifactId>
    </dependency>
    <dependency>
        <groupId>com.itextpdf</groupId>
        <artifactId>itextpdf</artifactId>
        <version>5.5.12</version>
    </dependency>
    <dependency>
        <groupId>com.itextpdf.tool</groupId>
        <artifactId>xmlworker</artifactId>
        <version>5.5.12</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <dependency>
        <groupId>org.python</groupId>
        <artifactId>jython-standalone</artifactId>
        <version>2.5.3</version>
    </dependency>
    <dependency>
        <groupId>javax.xml.bind</groupId>
        <artifactId>jaxb-api</artifactId>
        <version>2.3.0</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.h2database/h2 -->
    <!--<dependency>-->
        <!--<groupId>com.h2database</groupId>-->
        <!--<artifactId>h2</artifactId>-->
        <!--<version>1.4.196</version>-->
        <!--<scope>test</scope>-->
    <!--</dependency>-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
        <version>1.5.2.RELEASE</version>
    </dependency>
    <!--<dependency>-->
        <!--<groupId>com.h2database</groupId>-->
        <!--<artifactId>h2</artifactId>-->
        <!--<version>1.4.194</version>-->
    <!--</dependency>-->
    <dependency>
        <groupId>org.pygments</groupId>
        <artifactId>pygments</artifactId>
        <version>1.5</version>
        <scope>runtime</scope>
    </dependency>
</dependencies>
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

应用属性

server.port = 5600

spring.mail.host=smtp.gmail.com
spring.mail.port=587
spring.mail.username=$MAIL_USER
spring.mail.password=$MAIL_PASS

#mail properties
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true

spring.thymeleaf.cache=false
spring.thymeleaf.mode=html5

spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url = $MYSQL_HOST
spring.datasource.username = $MYSQL_USERNAME
spring.datasource.password = $MYSQL_PASSWORD
spring.datasource.tomcat.max-active=20
spring.datasource.maxIdle=2
spring.datasource.tomcat.remove-abandoned=true
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQLDialect

spring.jpa.hibernate.ddl-auto=update

【问题讨论】:

您正在使用模拟,模拟的默认行为是返回null。对findAll 的调用会返回null,因此forEach 上的NullPointerException。也停止混合来自不同版本的 Spring Boot jar 1.5.91.5.2 等待发生的麻烦。 谢谢@M.Deinum 你能详细说明版本控制问题吗?我该如何解决这个问题 spring-data-jpa-starter依赖中删除版本,你可以删除hibernate-core依赖。 谢谢!我现在有这个工作,感谢它 @M.Deinum 您能否发表此评论作为答案?否则,问题将显示为未回答。谢谢。 【参考方案1】:

您正在使用@MockBean,它在下面使用Mockito.mock 来创建您的bean 的模拟。模拟的默认行为是返回默认值,在这种情况下,它将返回 null 作为默认值。因此它将在forEach 上中断

您需要做的是告诉您的模拟在特定方法调用上要做什么。在您的情况下,您可能希望返回一个空列表。在您的测试方法中,您需要在这些方面添加一些内容。

@Test
public void subscriberListNull() throws Exception 
    Mockito.when(employeeService.findAll)).thenReturn(new ArrayList());
    ArrayList<Subscriber> subs = aService.getSubscribers();
    assertThat(subs).isEmpty();

这也与 Spring Data JPA 的默认值内联,因为它永远不会从集合返回方法返回 null。如果没有结果,您将获得一个空集合。

【讨论】:

以上是关于JPA 在 Spring Boot 中导致 java.lang.NullPointerException的主要内容,如果未能解决你的问题,请参考以下文章

Spring <form:select> 标签在 Spring mvc + Hibernate 应用程序中导致错误

SpringBoot2(thymeleaf模板jsp页面和jpa)

Spring Boot(17)——使用Spring Data JPA

致Spring Boot初学者

Spring Boot:在Spring Boot中使用Mysql和JPA

Spring Boot中使用Spring Data JPA示例