Solucionado (ver solução)
Solucionado
(ver solução)
3
respostas

O ultimo pedido se repete no json até dar StackOverflowError

Vi alguns alunos com o mesmo problema, mas a anotação @JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) não funcionou pra mim.

No Browser ele cria o json dos pedidos, no caso tem 2 salvos no banco de dados com o status aguardando, o segundo ele repete até dar StackOverflowError.

Classe do usuario

@Entity
@Table(name="users")

public class User {
    @Id
    private String username;
    private String password;
    private Boolean enabled;
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "user", fetch = FetchType.LAZY)
    private List<Pedido> pedidos;

Classe dos pedidos

@Entity
public class Pedido {

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

    private String nomeProduto;
    private BigDecimal valorNegociado;
    private LocalDate dataDaEntrega;
    private String urlProduto;
    private String urlImagem;
    private String descricao;
    @Enumerated(EnumType.STRING)
    private StatusPedido status;
    @ManyToOne
    private User user;

O erro ocorre durante essa requisição

@RestController
@RequestMapping("/api/pedidos")
public class PedidosApi {

    @Autowired
    private PedidoRepository repository;

    @GetMapping("aguardando")
    public List<Pedido> getPedidoAguardandoOfertas(){
        Sort sort = Sort.by("nomeProduto").descending();
        PageRequest pageRequest = PageRequest.of(0, 10, sort);
        return repository.findByStatus(StatusPedido.AGUARDANDO, pageRequest);
    }

}
2021-05-11 15:34:17.096 ERROR 9664 --- [nio-8080-exec-5] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Infinite recursion (StackOverflowError); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: br.com.alura.mvc.mudi.model.User["pedidos"]->org.hibernate.collection.internal.PersistentBag[0]->br.com.alura.mvc.mudi.model.Pedido["user"]->br.com.alura.mvc.mudi.model.User["pedidos"]->org.hibernate.collection.internal.PersistentBag[0]->br.com.alura.mvc.mudi.model.Pedido["user"]->br.com.alura.mvc.mudi.model.User["pedidos"]->org.hibernate.collection.internal.PersistentBag[0]- <----- essa linha se repete varias vezes, umas dezenas de vezes... e dps da esse erro:

java.lang.StackOverflowError: null
    at java.base/java.lang.ClassLoader.defineClass1(Native Method) ~[na:na]
    at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1016) ~[na:na]
    at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:151) ~[na:na]
    at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:825) ~[na:na]
    at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:723) ~[na:na]
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:646) ~[na:na]
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:604) ~[na:na]
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:168) ~[na:na]
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522) ~[na:na]
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:773) ~[jackson-databind-2.11.4.jar:2.11.4]
tem muito mais coisa aqui...
3 respostas

Eu estava com um problema parecido e consegui resolver. Olha este tópico com a solução: https://cursos.alura.com.br/forum/topico-vazamento-de-memoria-ao-colocar-response-data-156284

Me livrei do erro, mas usando a sua solução Dyane a querry ta carregando dados que n deveriam, ta muito estranho, ele carrega os 2 pedidos AGUARDANDO, até ai tudo bem, mas ele traz junto todos os pedidos que esse usuário possui e até mesmo os dados desse usuário, como password. Ta muito confuso.

Esse é o resultado no browser:

[
  {
    "id": 3,
    "nomeProduto": "Xiaomi Mi 10T Pro",
    "valorNegociado": 6000.00,
    "dataDaEntrega": "2021-08-18",
    "urlProduto": "https://www.kabum.com.br/produto/131880/smartphone-xiaomi-mi-10t-pro-5g-tela-6-67-8gb-256gb-prata-cx303pra",
    "urlImagem": "https://images0.kabum.com.br/produtos/fotos/131880/smartphone-xiaomi-mi-10t-pro-5g-tela-6-67-8gb-256gb-prata-cx303pra_1605722410_gg.jpg",
    "descricao": "Smartphone Xiaomi Mi 10T Pro, 5G Tela 6,67´ 8GB/256GB, prata - CX303PRA",
    "status": "AGUARDANDO",
    "user": {
      "username": "123",
      "password": "$2a$10$Q.LBQ11A96U.ASNfwxE/rupN/jc23r9pv5qxzYxfy5PoNjttkea3e",
      "enabled": true,
      "pedidos": [
        {
          "id": 1,
          "nomeProduto": "RX 6900 XT",
          "valorNegociado": null,
          "dataDaEntrega": null,
          "urlProduto": "https://www.kabum.com.br/produto/134513/placa-de-v-deo-asus-rog-strix-lc-radeon-rx-6900-xt-16-gbps-16gb-gddr6-rog-strix-lc-rx6900xt-o16g-gaming",
          "urlImagem": "https://images3.kabum.com.br/produtos/fotos/134513/placa-de-video-asus-rog-strix-lc-radeon-rx-6900-xt-16-gbps-16gb-gddr6-rog-strix-lc-rx6900xt-o16g-gaming_1608727276_gg.jpg",
          "descricao": "Placa de Vídeo ASUS ROG Strix LC Radeon RX 6900 XT, 16 Gbps, 16GB, GDDR6 -  ROG-STRIX-LC-RX6900XT-O16G-GAMING",
          "status": "APROVADO",
          "user": "123"
        },
        3
      ]
    }
  },
  {
    "id": 5,
    "nomeProduto": "Monitor Asus LED TUF Gaming 31.5",
    "valorNegociado": null,
    "dataDaEntrega": null,
    "urlProduto": "https://www.kabum.com.br/cgi-local/site/produtos/descricao_ofertas.cgi?codigo=115238",
    "urlImagem": "https://images8.kabum.com.br/produtos/fotos/115238/monitor-gamer-asus-led-tuf-gaming-31-5-wqhd-hdmi-displayport-freesync-165hz-1ms-vg32vq1b_1597350761_gg.jpg",
    "descricao": "Monitor doidera",
    "status": "AGUARDANDO",
    "user": {
      "username": "rod",
      "password": "$2a$10$kKWdGaWW.Us5kpJ6g99BhuJv882QCcZhqxEPN5yVCv20GVHJsWDK6",
      "enabled": true,
      "pedidos": [
        {
          "id": 2,
          "nomeProduto": "teste1",
          "valorNegociado": 300.00,
          "dataDaEntrega": "2021-08-13",
          "urlProduto": "teste",
          "urlImagem": "https://www.animeunited.com.br/oomtumtu/2021/04/EfFUquRXgAEcI15-1024x576.jpg",
          "descricao": "solo leveling é lecal",
          "status": "ENTREGUE",
          "user": "rod"
        },
        5,
        {
          "id": 6,
          "nomeProduto": "Teste Entrega",
          "valorNegociado": 9999.67,
          "dataDaEntrega": "2021-08-31",
          "urlProduto": "https://tenor.com/view/wazatoz-rick-roll-never-gonna-give-you-up-meme-gif-18110697",
          "urlImagem": "https://media.tenor.com/images/323393ed1e06a5850cdc5fd66684f59d/tenor.gif",
          "descricao": "descricao entregada",
          "status": "ENTREGUE",
          "user": "rod"
        }
      ]
    }
  }
]
solução!

Consegui achar uma solução, vou deixar registrado aqui: Qnd a gente quer exibir apenas alguns dados a gente usa os DTO(data transfer object) a gente ja viu o instrutor usando isso. Criei a classe PedidoJsonDto

public class PedidoJsonDto {

    private Long id;

    private String nomeProduto;
    private String urlProduto;
    private String urlImagem;
    private String descricao;
    @Enumerated(EnumType.STRING)
    private StatusPedido status;

Minha classe PedidosApi ficou assim:

@RestController
@RequestMapping("/api/pedidos")
public class PedidosApi {

    @Autowired
    private PedidoRepository repository;

    @Autowired
    private ModelMapper modelMapper;

    @GetMapping("aguardando")
    public List<PedidoJsonDto> getPedidoAguardandoOfertas(){
        Sort sort = Sort.by("nomeProduto").descending();
        PageRequest pageRequest = PageRequest.of(0, 10, sort);
        return repository.findByStatus(StatusPedido.AGUARDANDO, pageRequest)
                .stream().map(this::toPedidoJsonDto).collect(Collectors.toList());
    }

    private PedidoJsonDto toPedidoJsonDto(Pedido pedido){
        return modelMapper.map(pedido, PedidoJsonDto.class);
    }
}

Eu usei uma biblioteca chamada ModelMapper que auxilia a gente na conversão de modelos, por exemplo: transformar um Pedido pra PedidoJsonDto. Note que o Spring n vai fazer a injeção de dependência automaticamente com o @Autowired.

É necessário criar uma classe de configuraçao que eu chamei de ModelMapperConfig

@Configuration
public class ModelMapperConfig {

    @Bean
    public ModelMapper modelMapper(){
        return new ModelMapper();
    }
}