I have an object Schools and another object Students. Schools has a OneToMany relationship with Students so that way a School can have a set of many Students. I am trying to access a list of Schools being given to the page from the controller using Javascript and Thymeleaf. When I try this, I am given an error in the console mentioning:
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:774) ~[jackson-databind-2.13.3.jar:2.13.3]
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178) ~[jackson-databind-2.13.3.jar:2.13.3]
at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serializeContents(CollectionSerializer.java:145) ~[jackson-databind-2.13.3.jar:2.13.3]
at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serialize(CollectionSerializer.java:107) ~[jackson-databind-2.13.3.jar:2.13.3]
at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serialize(CollectionSerializer.java:25) ~[jackson-databind-2.13.3.jar:2.13.3]
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:728) ~[jackson-databind-2.13.3.jar:2.13.3]
I have determined that this error only occurs when I try to access the object via the Javascript Thymeleaf. If I access the object using Thymeleaf in the html it loads the data exactly as it should.
Here is my School Object:
@Entity
public class Schools {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@NonNull
private String schoolName;
@NonNull
private String type;
@NonNull
private String schoolGrade;
@NonNull
private int studentCount;
@NonNull
private int startHour;
@NonNull
private int startMin;
@NonNull
private String startAmPm;
@NonNull
private int endHour;
@NonNull
private int endMin;
@NonNull
private String endAmPm;
@NonNull
private Date startDate;
@NonNull
private Date endDate;
@NonNull
private int schoolDays;
@NonNull
private String address;
@NonNull
private String city;
@NonNull
private String state;
@NonNull
private int zip;
@NonNull
private String phone;
@NonNull
private boolean isActive;
//one-to-one relationship so each location point is associated with one school
@OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinColumn(name = "location_id", referencedColumnName = "location_id")
private LocationPoint location;
//mapping students one to many from schools
//@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval=true)
@OneToMany(fetch = FetchType.EAGER, cascade = {CascadeType.MERGE, CascadeType.REMOVE, CascadeType.REFRESH, CascadeType.DETACH}, orphanRemoval=true)
@JoinColumn(name = "school_id")
private Set<Students> students = new HashSet<>();
Here is my Student Object:
@Entity
public class Students {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int studentId;
@NonNull private int studId;
@NonNull private boolean graduated;
@NonNull private String firstName;
@NonNull private String midName;
@NonNull private String lastName;
@NonNull private String suffix;
@NonNull private float grade;
@NonNull private String address1;
@Column(columnDefinition = "TEXT")
@NonNull private String address2;
@NonNull private String city;
@NonNull private String state;
@NonNull private int zipCode;
@NonNull private String gender;
@NonNull private Date birthDate;
@NonNull private String phoneNum;
@NonNull private String cellPhoneNum;
@NonNull private String emergencyPhoneNum;
@NonNull private String parentName;
@NonNull private String schoolName;
@NonNull private Date enter;
@NonNull private boolean isEnter;
@NonNull private Date Withdrawl;
@NonNull private boolean isWithdrawl;
@NonNull private boolean hazardousRoad;
@NonNull private String travelMode;
@NonNull private String driverNote;
@NonNull private String misc;
@NonNull private String journal;
@NonNull private String medical;
@NonNull private boolean isCustody;
@Column(columnDefinition = "TEXT") private String alt1Name;
@Column(columnDefinition = "TEXT") private String alt1Relationship;
@Column(columnDefinition = "TEXT") private String alt1Address1;
@Column(columnDefinition = "TEXT") private String alt1Address2;
@Column(columnDefinition = "TEXT") private String alt1City;
@Column(columnDefinition = "TEXT") private String alt1State;
@Column(columnDefinition = "TEXT") private long alt1ZipCode;
@Column(columnDefinition = "TEXT") private String alt1Phone;
@Column(columnDefinition = "TEXT") private String alt1CellPhone;
@Column(columnDefinition = "TEXT") private String alt2Name;
@Column(columnDefinition = "TEXT") private String alt2Relationship;
@Column(columnDefinition = "TEXT") private String alt2Address1;
@Column(columnDefinition = "TEXT") private String alt2Address2;
@Column(columnDefinition = "TEXT") private String alt2City;
@Column(columnDefinition = "TEXT") private String alt2State;
@Column(columnDefinition = "TEXT") private long alt2ZipCode;
@Column(columnDefinition = "TEXT") private String alt2Phone;
@Column(columnDefinition = "TEXT") private String alt2CellPhone;
@Column(columnDefinition = "TEXT") private String alt3Name;
@Column(columnDefinition = "TEXT") private String alt3Relationship;
@Column(columnDefinition = "TEXT") private String alt3Address1;
@Column(columnDefinition = "TEXT") private String alt3Address2;
@Column(columnDefinition = "TEXT") private String alt3City;
@Column(columnDefinition = "TEXT") private String alt3State;
@Column(columnDefinition = "TEXT") private long alt3ZipCode;
@Column(columnDefinition = "TEXT") private String alt3Phone;
@Column(columnDefinition = "TEXT") private String alt3CellPhone;
@NonNull private String studentType1;
@NonNull private String studentType2;
@NonNull private String studentType3;
@NonNull private String pickUpLocation;
@NonNull private int pickUpStopNo;
@NonNull private String busRoute1;
@NonNull private String pickUpTime;
@NonNull private String deliveryLocation;
@NonNull private int deliveryStopNo;
@NonNull private String busRoute2;
@NonNull private String deliveryTime;
//many-to-one relationship so multiple students are associated with one school
@ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private Schools school;
//many-to-one relationship so multiple students are associated with one pickupDropoff
@ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private PickupDropoff2 pickupDropoff;
//one-to-one mapping so each student is associated with one location point
@OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private LocationPoint location;
Here is my controller:
//create pickupDropoff point display page
@RequestMapping({"/createPickupDropoff"})
public String createPickupDropoff(PickupDropoff2 pickupDropoff, Model model) {
model.addAttribute("schools", schoolRepo.findAll());
model.addAttribute("students", studentRepo.findAll());
return "create-pickupDropoff-point";
}
And here is my Javascript:
<script type="text/javascript" th:inline="javascript">
var schools = /*[[${schools}]]*/ 'false';
</script>
I figured out the issue was with the bi-directional relationship that School and Students has. This caused a recursive loop and made it so the data couldn't be serialized in the JSON object. I fixed it by adding:
@JsonIdentityInfo( generator = ObjectIdGenerators.PropertyGenerator.class, property = "studentId")
And
@JsonIdentityInfo( generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
This caused both of the domains to only load 1 level deep, and stopping the recursive loop.