NullPointerException trying retrieve @ManyToOne Parent Field - Spring Boot

Danik_Help :

i'm trying to get a field from parent entity in a @ManyToOne relationship, but it throws a NullPointerException during the Integration Test when i try to print the parent field, the code is below for both my entities including the Test.

Album Entity - Parent


import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.validation.constraints.NotBlank;

import org.hibernate.annotations.GenericGenerator;

@Entity
@Table(name = "albuns")
public class Album {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO, generator = "native")
    @GenericGenerator(name = "native", strategy = "native")
    private Long codigo;

    @NotBlank(message = "Erro: Certifique-se de digitar o nome do album.")
    @Column(nullable = false)
    private String nome;

    @OneToMany(mappedBy = "album", cascade = { CascadeType.PERSIST, CascadeType.REMOVE }, fetch = FetchType.EAGER)
    private List<Imagem> imagens = new ArrayList<>();

    @Column(name = "data_criacao", nullable = false)
    private LocalDate dataCriacao;

    @Column(unique = true, nullable = false)
    private String capa;

    protected Album() {
    }

    public Album(String nome, LocalDate dataCriacao, String capa) {
        this.nome = nome;
        this.dataCriacao = dataCriacao;
        this.capa = capa;
    }

    public Long getCodigo() {
        return codigo;
    }

    public void setCodigo(Long codigo) {
        this.codigo = codigo;
    }

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    public LocalDate getDataCriacao() {
        return dataCriacao;
    }

    public void setDataCriacao(LocalDate dataCriacao) {
        this.dataCriacao = dataCriacao;
    }

    public String getCapa() {
        return capa;
    }

    public void setCapa(String capa) {
        this.capa = capa;
    }

    public List<Imagem> getImagens() {
        return imagens;
    }

    public void setImagens(List<Imagem> imagens) {
        this.imagens = imagens;
    }

}

Imagem Entity - Child


import java.time.LocalDate;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

import org.hibernate.annotations.GenericGenerator;

@Entity
@Table(name = "imagens")
public class Imagem {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO, generator = "native")
    @GenericGenerator(name = "native", strategy = "native")
    private long codigo;

    @Column(nullable = false, unique = true)
    private String url;

    @Column(name = "data_carregamento", nullable = false)
    private LocalDate dataCarregamento;

    @ManyToOne(targetEntity = Album.class, fetch = FetchType.LAZY)
    @JoinColumn(name = "cod_album")
    private Album album;

    public Imagem() {
    }

    public Imagem(String url, LocalDate dataCarregamento) {
        this.url = url;
        this.dataCarregamento = dataCarregamento;

    }

    public long getCodigo() {
        return codigo;
    }

    public void setCodigo(long codigo) {
        this.codigo = codigo;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public LocalDate getDataCarregamento() {
        return dataCarregamento;
    }

    public void setDataCarregamento(LocalDate dataCarregamento) {
        this.dataCarregamento = dataCarregamento;
    }

    public Album getAlbum() {
        return album;
    }

    public Long getAlbumCodigo() {
        return album.getCodigo();
    }

    public void setAlbum(Album album) {
        this.album = album;
    }

}

Album Repository


import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.servidor.multiplatform.entidades.Album;

@Repository
public interface AlbumRepositorio extends JpaRepository<Album, Long> {

    public Album findByNome(String nome);

}

Integration Test


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

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import org.springframework.test.context.junit4.SpringRunner;

import com.servidor.multiplatform.entidades.Album;
import com.servidor.multiplatform.entidades.Imagem;
import com.servidor.multiplatform.repositorios.AlbumRepositorio;

@RunWith(SpringRunner.class)
@DataJpaTest
public class TesteDeIntegracaoAlbumRepositorio {

    @Autowired
    private TestEntityManager entityManager;

    @Autowired
    private AlbumRepositorio albumRepositorio;

    @Test
    public void TesteDeInsercaoDoAlbumAsSuasImagensEPesquisaDaSuaInformacao() {

        // Album

        Album album1 = new Album("Album 1", LocalDate.now(), "Capa 1");

        // List for Test
        List<Imagem> imagensLocal = new ArrayList<>();

        // Filling the Lists with new images
        for (int i = 0; i < 4; i++) {
            Imagem imagem = new Imagem("url" + i, LocalDate.now());
            imagensLocal.add(imagem);
            album1.getImagens().add(imagem);
        }

        // Persisting the Album and the Pictures
        entityManager.persist(album1);
        entityManager.flush();

        // Getting the Album and the Pictures

        Album albumEncontrado = albumRepositorio.findByNome("Album 1");
        List<Imagem> imagensPesistidas = albumEncontrado.getImagens();

        Album album = entityManager.find(Album.class, 1L);

        System.out.println(album.getCodigo());

        for (Imagem imagem : album.getImagens()) {
            // The problem is here in this code
            System.out.println(imagem.getAlbumCodigo());
        }

        // Testing the Album
        assertThat(album1.getNome()).isEqualTo(albumEncontrado.getNome());

        // Testing the Lists
        Assert.assertEquals(imagensLocal, imagensPesistidas);

    }

}

The Stack Trace

TesteDeIntegracaoAlbumRepositorio.TesteDeInsercaoDoAlbumAsSuasImagensEPesquisaDaSuaInformacao
com.servidor.multiplatform.TesteDeIntegracaoAlbumRepositorio
TesteDeInsercaoDoAlbumAsSuasImagensEPesquisaDaSuaInformacao(com.servidor.multiplatform.TesteDeIntegracaoAlbumRepositorio)
java.lang.NullPointerException

    at com.servidor.multiplatform.entidades.Imagem.getAlbumCodigo(Imagem.java:75)

    at com.servidor.multiplatform.TesteDeIntegracaoAlbumRepositorio.TesteDeInsercaoDoAlbumAsSuasImagensEPesquisaDaSuaInformacao(TesteDeIntegracaoAlbumRepositorio.java:64)

    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)

    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)

    at java.lang.reflect.Method.invoke(Unknown Source)

    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.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74)

    at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84)

    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:251)

    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)

    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:190)

    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)

    at org.junit.runner.JUnitCore.run(JUnitCore.java:115)

    at org.junit.vintage.engine.execution.RunnerExecutor.execute(RunnerExecutor.java:40)

    at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(Unknown Source)

    at java.util.stream.ReferencePipeline$3$1.accept(Unknown Source)

    at java.util.Iterator.forEachRemaining(Unknown Source)

    at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Unknown Source)

    at java.util.stream.AbstractPipeline.copyInto(Unknown Source)

    at java.util.stream.AbstractPipeline.wrapAndCopyInto(Unknown Source)

    at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(Unknown Source)

    at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(Unknown Source)

    at java.util.stream.AbstractPipeline.evaluate(Unknown Source)

    at java.util.stream.ReferencePipeline.forEach(Unknown Source)

    at org.junit.vintage.engine.VintageTestEngine.executeAllChildren(VintageTestEngine.java:80)

    at org.junit.vintage.engine.VintageTestEngine.execute(VintageTestEngine.java:71)

    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:229)

    at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:197)

    at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:211)

    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:191)

    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:137)

    at org.eclipse.jdt.internal.junit5.runner.JUnit5TestReference.run(JUnit5TestReference.java:89)

    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)

    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:541)

    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:763)

    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:463)

    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:209)





Mikko Maunu :

Imagem.album remains null because code given in unit test never writes to that field. Only one side of the relation is set via album1.getImagens().add(imagem).

//Imagem.album is not set in this loop:
for (int i = 0; i < 4; i++) {
    Imagem imagem = new Imagem("url" + i, LocalDate.now());
    imagensLocal.add(imagem);
    album1.getImagens().add(imagem);
}

As told in specification, one should keep relationships consistent:

Note that it is the application that bears responsibility for maintaining the consistency of run-time relationships—for example, for insuring that the “one” and the “many” sides of a bidi-rectional relationship are consistent with one another when the application updates therelationship at runtime

It will remain null also for albumEncontradoand album, because they are refences to same object as album1. It is so, because persistence context is not cleared in any point.

Right way to fix the issue is to keep relationships consistent by setting both sides of relationship:

for (int i = 0; i < 4; i++) {
    Imagem imagem = new Imagem("url" + i, LocalDate.now());
    imagensLocal.add(imagem);
    album1.getImagens().add(imagem);
    imagem.setAlbum(album1);
}

NullPointerException could also be avoided by clearing persistence context via EntityManager.clear() before querying database. That would break the assertion for list equality, because it depends from identity of elements in list. Reason is that Imagem does not implement equals. Even after adding equals it still has assumption about order of elements, which is not that deterministic.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=417625&siteId=1