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)
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 albumEncontrado
and 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.