Unable to evaluate the expression Method threw 'java.lang.StackOverflowError' exception. with nested relations between entities
Asked Answered
L

3

13

I am fighting trough one day with one problem. So Lets get started. I have 3 classes like tags, offers and animals

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "animals")
public class Animal {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "animal_id")
private Long id;

@Column(name = "name")
@NotNull
private String name;

@Column(name = "description")
@NotNull
private String text;

@Column(name = "quantity")
@NotNull
private int count;


@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "user_id", nullable = false)
private User user;

@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "tag_id", nullable = false)
private Tag tags;


@OneToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "offer_id", nullable = true)
private Offer offer;

Offers

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "offer")
public class    Offer {


@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "offer_id")
private Long id;

@Column(name = "title")
@NotNull
private String title;

@Column(name = "description")
@NotNull
private String text;


@Column(name = "price")
@NotNull
private int price;

@Column(name = "contact")
@NotNull
private int contact;

@Temporal(TemporalType.TIMESTAMP)
@Column(name = "create_date", nullable = false, updatable = false)
@CreationTimestamp
private java.util.Date createDate;



@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "user_id", nullable = false)
private User user;


@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "tag_id", nullable = false)
private Tag tags;


@OneToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "animal_id", nullable = false)
private Animal animal;

And tags.

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "tags")
public class Tag {


@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "tag_id")
private long id;

@Column(name = "name")
@Email(message = "*Please provide a valid tag")
@NotEmpty(message = "*Please provide an tag")
private String name;


@OneToMany(mappedBy = "tags")
private Set<Offer> offers;

@OneToMany(mappedBy = "tags")
private Set<Animal> animals;

OneToOne relation is because I want to have one offer for one animal.

I have also controller like showed to add new offer.

@RequestMapping(value = "/postForm", method = RequestMethod.GET)
public String newOffer(Principal principal,Model model) {

  String e_mail = principal.getName();
  System.out.println(e_mail);
  User user = userService.findUserByEmail(e_mail);

  Offer offer = new Offer();
  offer.setUser(user);
    Long id = user.getId();

    List<Tag> tags =  tagService.findAlltags();


   List<Animal> animal = animalRepository.findAnimalByUser(user);

    model.addAttribute("tags", tags);
    model.addAttribute("animals",animal);
    model.addAttribute("offer", offer);

        return "/postForm";
}

I am passing lists like animal, and tags to pick tag, and animal assigned to the user. My animal repository.

public interface AnimalRepository extends JpaRepository<Animal, Long> {


    List<Animal> findAnimalByUser(User user);


}

And actually I have the problem with showing animals correctly. Tags are showing correctly, but animals lists cannot evaluate tags inside class of animals. To be more specific let me show You screen of traceback from Inteliji

link for imgur enter link description here Probably there is something wrong with relations but what exactly ? Thanks for all answers. :)

Lichenin answered 18/1, 2019 at 12:39 Comment(1)
don't post a small image, especially if you have no idea of our display settings. post the stacktrace in your question.Mouthpiece
H
13

toString() method in Animal class causes this problem. Method builds return string alternately from Animal and Tag classes both have reference to each other which produces infinite process.

Get rid of @Data annotation (which includes @ToString annotation), and make your own implementation of toString() method.

Hauteloire answered 18/1, 2019 at 12:56 Comment(2)
Okay,I understand the idea. But how to do this exactly ? My first attempt to make own toString() was something like this " String toStrint(){ return get.Name()+","+getSomething().Lichenin
Correct answer for me. The explanation with about the infinite processing is also correct.Inhalant
L
4

If you are using lombok just add;

@EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = false)
Lilllie answered 4/7, 2020 at 11:43 Comment(0)
M
0

When there is cross-referencing between multiple classes, the StackOverflowError exception happens as too many methods are initialised and called without returning to superclasses, namely the @ToString method (see the documentation):

Animal -> User, Tag, Offer
Tag -> Animal, Offer
Offer -> Animal, Tag

Other than @Kamil W's method to get rid of the @Data Lombok annotation, if you still want the benefits of using automated toString() methods, you can use @ToString.Exclude annotation on the cross-references, for example, with Animal Class:

@Data
// ...other annotations
public class Animal {
  // ...other fields

  @ManyToOne(fetch = FetchType.EAGER)
  @JoinColumn(name = "user_id", nullable = false)
  @ToString.Exclude
  private User user;

  @ManyToOne(fetch = FetchType.EAGER)
  @JoinColumn(name = "tag_id", nullable = false)
  @ToString.Exclude
  private Tag tags;
  
  @OneToOne(fetch = FetchType.EAGER)
  @JoinColumn(name = "offer_id", nullable = true)
  @ToString.Exclude
  private Offer offer;
}
Manlike answered 7/8, 2024 at 6:43 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.