JSON Infinite when using @ManyToMany GET

0

I have two classes, which has a @ManyToMany relationship generating the third table described in the code below:

Card


package br.com.rpgnext.deck.critical.model;

import com.fasterxml.jackson.annotation.*;
import javax.persistence.*;
import java.io.Serializable;
import java.util.*;

@Entity
@Table(name = "cards")
public class Card implements Serializable{
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @JoinColumn(name = "id")
    private Long id;
    private String image;

    @ManyToMany(cascade = CascadeType.DETACH, fetch = FetchType.LAZY)
    @JoinTable(name = "cards_items", joinColumns = @JoinColumn(name = "card_id"), inverseJoinColumns = @JoinColumn(name = "item_id"))
    private Set items;

    @Temporal(TemporalType.DATE)
    @JsonFormat(pattern = "YYYY-MM-dd")
    private Date onCreate;

    @PrePersist
    public void prePersist(){
        this.onCreate = new Date();
    }

    public Card() {
    }

    public Card(String image) {
        this.image = image;
    }

    public Card(String image, Set items) {
        this.image = image;
        this.items = items;
    }

    public Card(String image, Set items, Date onCreate) {
        this.image = image;
        this.items = items;
        this.onCreate = onCreate;
    }

    public Card(Long id, String image, Set items, Date onCreate) {
        this.image = image;
        this.items = items;
        this.onCreate = onCreate;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getImage() {
        return image;
    }

    public void setImage(String image) {
        this.image = image;
    }

    public Set getItems() {
        return items;
    }

    public void setItems(Set items) {
        this.items = items;
    }

    public Date getOnCreate() {
        return onCreate;
    }

    public void setOnCreate(Date onCreate) {
        this.onCreate = onCreate;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Card card = (Card) o;
        return Objects.equals(id, card.id) &&
                Objects.equals(image, card.image) &&
                Objects.equals(items, card.items) &&
                Objects.equals(onCreate, card.onCreate);
    }

    @Override
    public int hashCode() {

        return Objects.hash(id, image, items, onCreate);
    }
}

Item


package br.com.rpgnext.deck.critical.model;

import com.fasterxml.jackson.annotation.*;

import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;
import java.util.Set;
import java.util.Objects;

@Entity
@Table(name = "items")
public class Item implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @JoinColumn(name = "id")
    private Long id;
    private String title;
    private String description;

    @OneToOne
    @JoinColumn(name="type_id", referencedColumnName = "id")
    private Type type;

    @ManyToMany(fetch = FetchType.EAGER, mappedBy = "items" )
    private Set cards;

    @Temporal(TemporalType.DATE)
    @JsonFormat(pattern = "YYYY-MM-dd")
    private Date onCreate;

    @PrePersist
    void prePersist(){
        this.onCreate = new Date();
    }

    public Item() {
    }

    public Item(Long id){
        this.id = id;
    }

    public Item(Long id, String title, String description, Type type, Set cards, Date onCreate) {
        this.title = title;
        this.description = description;
        this.type = type;
        this.cards = cards;
        this.onCreate = onCreate;
    }

    public Item(String title, String description, Type type, Set cards) {
        this.title = title;
        this.description = description;
        this.type = type;
        this.cards = cards;
    }

    public Item(Set cards) {
        this.cards = cards;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public Type getType() {
        return type;
    }

    public void setType(Type type) {
        this.type = type;
    }

    public Set getCards() {
        return cards;
    }

    public void setCards(Set cards) {
        this.cards = cards;
    }

    public Date getOnCreate() {
        return onCreate;
    }

    public void setOnCreate(Date onCreate) {
        this.onCreate = onCreate;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Item item = (Item) o;
        return Objects.equals(id, item.id) &&
                Objects.equals(title, item.title) &&
                Objects.equals(description, item.description) &&
                Objects.equals(type, item.type) &&
                Objects.equals(cards, item.cards) &&
                Objects.equals(onCreate, item.onCreate);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, title, description, type, cards, onCreate);
    }
}

Type


package br.com.rpgnext.deck.critical.model;

import com.fasterxml.jackson.annotation.JsonFormat;

import java.io.Serializable;
import java.util.Date;
import java.util.Objects;

import javax.persistence.*;

@Entity
@Table(name = "types")
public class Type implements Serializable{
    private static final long serialVersionUID = 1L;

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

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

    @Temporal(value = TemporalType.DATE)
    @JsonFormat(pattern = "YYYY-MM-dd")
    private Date onCreate;

    @PrePersist
    void prePersists(){
        this.onCreate = new Date();
    }

    public Type() {
    }

    public Type(String text) {
        this.text = text;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    public String getImage() {
        return image;
    }

    public void setImage(String image) {
        this.image = image;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Type type = (Type) o;
        return Objects.equals(id, type.id) &&
                Objects.equals(text, type.text) &&
                Objects.equals(image, type.image) &&
                Objects.equals(onCreate, type.onCreate);
    }

    @Override
    public int hashCode() {

        return Objects.hash(id, text, image, onCreate);
    }
}

It has been tried several ways to get the Infonite Loop from JSON, since both classes have a List, but without success.

@JsonIgnore does not resolve because it will not show the content I need.

Inserted JSON sample


{
  "image": "Carta-Critica-001.jpg",
  "items": [
    {"id": 1},
    {"id": 2},
    {"id": 3},
    {"id": 4}
  ]
}

How would you like me to come back


{
  "image": "Carta-Critica-01.jpg",
  "items": [
    {
      "id": 1,
      "title": "traqueia esmagada",
      "type": {
        "id": 1,
        "text": "contusão",
        "image": "contution.png"
      },
      "description": "Danos Crítico, e o alvo fica incapacitadoe não pode falar e respirar até receber tratamento."
    },
    {
      "id": 2,
      "title": "mão perfurada",
      "type": {
        "id": 2,
        "text": "perfurante",
        "image": "piercing.png"
      },
      "description": "Danos Crítico, e o alvo solta o que estiver segurando (1 item) e não pode usar aquele membro até o início de seu próximo turno."
    },
    {
      "id": 3,
      "title": "testa cortada",
      "type": {
        "id": 3,
        "text": "cortante",
        "image": "sharp.png"
      },
      "description": "Danos Crítico, e o alvo fica cego até o início de seu próximo turno."
    },
    {
      "id": 4,
      "title": "vulnerabilidade mágica",
      "type": {
        "id": 4,
        "text": "mágico",
        "image": "magic.png"
      },
      "description": "Danos Crítico, e o alvo tem vulnerabilidade a dano mágico até o início de seu próximo turno."
    }
  ]
}
}

Controller


package br.com.rpgnext.deck.critical.controller;

import java.beans.Transient;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import br.com.rpgnext.deck.critical.service.ItemService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import br.com.rpgnext.deck.critical.model.Card;
import br.com.rpgnext.deck.critical.model.Item;
import br.com.rpgnext.deck.critical.model.Type;
import br.com.rpgnext.deck.critical.repository.CardRespository;
import br.com.rpgnext.deck.critical.repository.ItemRepository;
import br.com.rpgnext.deck.critical.repository.TypeRepository;
import br.com.rpgnext.deck.critical.service.CardService;

@RestController
public class CardController {
    @Autowired
    private CardRespository cardRespository;

    @Autowired
    private TypeRepository typeRepository;

    @Autowired
    private ItemRepository itemRepository;

    @Autowired
    private CardService cardService;

    @Autowired
    private ItemService itemService;

    @RequestMapping(value = "card/save", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_UTF8_VALUE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE )
    @ResponseBody
    public ResponseEntity save(@RequestBody Card card){
        Card cardSaved = cardRespository.save(card);
        return new ResponseEntity(cardSaved, HttpStatus.CREATED);
    }

    @RequestMapping(value = "cards/save", method = RequestMethod.POST)
    @ResponseBody
    public ResponseEntity> saveAll(@RequestBody List cards){
        return new ResponseEntity((List) cardRespository.findAll(), HttpStatus.FOUND);
    }

    @RequestMapping(value = "cards", method = RequestMethod.GET)
    @ResponseBody
    public ResponseEntity> findAll(){
        return new ResponseEntity((List) cardRespository.findAll(), HttpStatus.FOUND);
    }

    @RequestMapping(value = "card/{id}", method = RequestMethod.GET)
    @ResponseBody
    public ResponseEntity findById(@PathVariable Long id){
        return new ResponseEntity((cardRespository.findById(id).get()), HttpStatus.FOUND);
    }

    @RequestMapping(value = "card/random", method = RequestMethod.GET)
    @ResponseBody
    public ResponseEntity randomCard(){
        List cards = (List) cardRespository.findAll();
        Card card = cardService.randomCard(cards);
        return new ResponseEntity(card, HttpStatus.OK);
    }

    @RequestMapping(value = "card/new", method = RequestMethod.GET)
    @ResponseBody
    public ResponseEntity randomNewCard(){
        Card card = new Card();

        for(Long i = 1L; i  allItemsByType = itemRepository.findAllByType(type);
            Item item = itemService.randomItem(allItemsByType);
            //item.setCard(card);
            //card.addItem(item);
        }

        Card cardSaved = cardRespository.save(card);
        return new ResponseEntity(cardSaved, HttpStatus.CREATED);
    }
}
    
asked by anonymous 10.04.2018 / 16:16

2 answers

1

Add reference back in

    @Entity
    @Table(name = "items")
    public class Item implements Serializable {
        /**
         * 
         */
        @JsonBackReference("items")
        @ManyToMany(fetch = FetchType.EAGER, mappedBy = "items" )
        private Set cards;

    }

On both sides

    @Entity
    @Table(name = "cards")
    public class Card implements Serializable{

        @JsonBackReference("cards")
        @ManyToMany(cascade = CascadeType.DETACH, fetch = FetchType.LAZY)
        @JoinTable(name = "cards_items", joinColumns = @JoinColumn(name = "card_id"), inverseJoinColumns = @JoinColumn(name = "item_id"))
        private Set items;

    }
    
11.04.2018 / 16:58
1

Use the annotations JsonManagedReference and JsonBackReference . They are used to indicate that such properties are part of a bidirectional relationship. The first should be used in the "parent" class, and the second in the "daughter" class.

In your class Card add the annotation JsonManagedReference :

//Algum motivo para não parametrizar seu Set (Set<Item>)?
@JsonManagedReference
private Set items;

In your class Item add the annotation JsonBackReference :

//Algum motivo para não parametrizar seu Set (Set<Card>)?
@JsonBackReference
private Set items;
    
10.04.2018 / 17:18