0

I'm learning spring boot. I have a GYMS table and HOURS table. I declared a @OneToMany mapping on the gym entity and @ManyToOne mapping on the hours entity. However, when I attempt to save a new gym and its hours in the database I get a stackoverflow error. Any help would be appreciated. Thanks in advance!

CREATE TABLE gyms (
  id int NOT NULL AUTO_INCREMENT,
  name varchar(60) NOT NULL,
  address varchar(255),
  city varchar(255),
  province varchar(3),
  postal_code char (7),
  phone_number varchar(20),
  PRIMARY KEY (id)
);

CREATE TABLE hours (
  id int NOT NULL AUTO_INCREMENT,
  gym_id int NOT NULL,
  day_of_week INT,
  opening_time TIME,
  closing_time TIME,
  PRIMARY KEY (id),
  FOREIGN KEY (gym_id) REFERENCES gyms(id)
);
@Entity
@Table (name = "gyms")
@NoArgsConstructor
@Getter
@Setter
public class Gym {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    private String name;
    private String address;
    private String city;
    private String province;
    private String postalCode;
    private String phoneNumber;

    @OneToMany(mappedBy = "gym", cascade = CascadeType.ALL)
    private List<Hours> hours;

    public Gym(String name, String address, String city, String province, String postalCode, String phoneNumber) {
        this.name = name;
        this.address = address;
        this.city = city;
        this.province = province;
        this.postalCode = postalCode;
        this.phoneNumber = phoneNumber;
    }
}
@Entity
@Table (name = "hours")
@NoArgsConstructor
@Getter
@Setter
public class Hours {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    // private int gymId;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "gym_id")
    private Gym gym;

    private int dayOfWeek;
    private Time openingTime;
    private Time closingTime;
    
    public Hours(int dayOfWeek, String openingTime, String closingTime) {
        this.dayOfWeek = dayOfWeek;
        this.openingTime = Time.valueOf(openingTime);
        this.closingTime = Time.valueOf(closingTime);
    }
}
@Service
public class GymService {
    private final GymRepository gymRepository;
    private final HoursRepository hoursRepository;
    
    @Autowired
    public GymService(GymRepository gymRepository, HoursRepository hoursRepository) {
        this.gymRepository = gymRepository;
        this.hoursRepository = hoursRepository;
    }

    public Gym addNewGym(Gym gym, List<Hours> hours) {
        gym.setHours(hours);
        for (Hours hour : hours) {
            hour.setGym(gym);
        }
        return gymRepository.save(gym);
    }

}

2 Answers2

0

I am little confused about your approach. If you want to add new Gym object with child you can do it simply by doing it

First in Gym class put

@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "gym_id")
private List<Hours> hours;

then from Hours class remove this thing

@ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "gym_id")
    private Gym gym;

then write your service like

public Gym addNewGym(Gym gym, List<Hours> hours) {
        gym.setHours(hours);
        return gymRepository.save(gym);
    }
Avijit Barua
  • 2,950
  • 5
  • 14
  • 35
0

Without trying it, but having been there before, my guess is its related to Lombok. Now I can't see your classes have the Lombok annotation for generating it but maybe this is configured elsewhere... I'm not a huge fan of Lombok.

That said, I suspect it is auto-generating the equals/hashcode/toString methods and then they are getting executed as part of the code execution.

Unfortunately, because hibernate forces us to create circular dependencies (i.e. your Gym object has an Hours field and your Hours object has a Gym field) when the equals/hashcode/toString method is executed, it will infinitely call each class' method.

e.g.. Gym calls equals on Hours which calls equals on Gym, which calls equals on Hours, which calls equals on Gym etc, etc.