问题
I have the following sample entities:
Institution
@Data
@Entity
@NoArgsConstructor
@EntityListeners(InstitutionAuditListener.class)
public class Institution extends Auditable<String> {
@OneToMany(mappedBy = "institution", cascade = CascadeType.ALL)
@JsonManagedReference
private List<RegisteredProgram> registeredPrograms;
private String name;
}
RegisteredProgram
@Data
@NoArgsConstructor
@Entity
@EntityListeners(RegisteredProgramAuditListener.class)
public class RegisteredProgram extends Auditable<String> {
private String name;
@ManyToOne
@JoinColumn(name = "institution_id", nullable = false)
@JsonBackReference
private Institution institution;
@Embedded
private RegistrationRequirement registrationRequirement;
@OneToMany(mappedBy = "registeredProgram", cascade = CascadeType.ALL)
@JsonManagedReference
private List<Official> officialList;
}
Official
@Data
@Entity
@EntityListeners(OfficialAuditListener.class)
@NoArgsConstructor
public class Official extends Auditable<String> {
private String name;
private String position;
@ManyToOne
@JoinColumn(name = "REGISTERED_PROGRAM_ID", nullable = false)
@JsonBackReference
private RegisteredProgram registeredProgram;
}
Audit Log
@Entity
@Data
@NoArgsConstructor
public class AuditLog extends AuditBase {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
private Long entityId;
@Type(type = "yes_no")
private Boolean isDeleted = false;
@Column(columnDefinition = "json")
@Convert(converter = JpaConverterJson.class)
private String jsonObject;
}
When I attempted to save an instance of Official's object toString() value into AuditLog's jsonObject field, I encountered the error below:
java.lang.StackOverflowError: null
at java.base/java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:173) ~[na:na]
at java.base/java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:538) ~[na:na]
at java.base/java.lang.StringBuilder.append(StringBuilder.java:174) ~[na:na]
at com.tesda8.region8.program.registration.model.entities.RegisteredProgram.toString(RegisteredProgram.java:26) ~[classes/:na]
at java.base/java.lang.String.valueOf(String.java:2951) ~[na:na]
at java.base/java.lang.StringBuilder.append(StringBuilder.java:168) ~[na:na]
at java.base/java.util.AbstractCollection.toString(AbstractCollection.java:473) ~[na:na]
at org.hibernate.collection.internal.PersistentBag.toString(PersistentBag.java:622) ~[hibernate-core-5.4.21.Final.jar:5.4.21.Final]
at java.base/java.lang.String.valueOf(String.java:2951) ~[na:na]
at java.base/java.lang.StringBuilder.append(StringBuilder.java:168) ~[na:na]
at com.tesda8.region8.program.registration.model.entities.Institution.toString(Institution.java:23) ~[classes/:na]
at java.base/java.lang.String.valueOf(String.java:2951) ~[na:na]
at java.base/java.lang.StringBuilder.append(StringBuilder.java:168) ~[na:na]
at com.tesda8.region8.program.registration.model.entities.RegisteredProgram.toString(RegisteredProgram.java:26) ~[classes/:na]
I was wondering if there are any annotations in spring/hibernate that could prevent this issue to occur or is there any other way that i could save the object's (JSON) value to an object's column in spring? I was assuming that @JsonManagedReference
and @JsonBackReference
should've prevented this issue to happen.
UPDATE
- I already tried to used toString() method for objects through Lombok's
@Data
annotation - I also tried to use
String.valueOf(official)
回答1:
The problem is the fact you are using Lombok's @Data
annotation to generate the equals
, hashCode
and toString
method. This will lead to several issues/oddities (more info in this blog post I wrote).
Trying to use a Jackson @JsonBackReference
will ofcourse not solve the generating of the toString
method, this will only help with creating JSON using the Jackson library.
What happens is that your toString
of Institution
will call the toString
of the RegisteredProgram
. Which will call the toString
of Institution
again, which will call the toString
of RegisteredProgram
again which.... (well you probably get the drift).
Don't rely on the equals
, hashCode
and toString
methods from the @Data
functionality to Lombok as, as mentioned in the start, it will lead to issues. Instead write your own equals
, hashCode
and toString
or at least declare what needs to be part of those methods (the end result might even be that it is easier to write those methods by hand).
On the other hand, isn't storing the toString
value a bit weird in the first place?
Related Links
- https://deinum.biz/2019-02-13-Lombok-Data-Ojects-Arent-Entities/
- https://vladmihalcea.com/how-to-implement-equals-and-hashcode-using-the-jpa-entity-identifier/
回答2:
Cheers for M. Deinum's insights regarding my issue. https://stackoverflow.com/a/65388643/14806310
The root cause of my problem was indeed using toString()
by Lombok's @Data
to save the details of my entity to a field, which was problematic right off the bat. I did try to override the toString()
method of RegisteredProgram
, which then solved the StackOverflow error, but still my approach on saving the JSON object was weird.
So rather than saving the object to String
json column through toString()
, I created a new HashMap field entityAttributes
for my entity to persist a JSON object.
UPDATED AuditLog Entity
@Entity
@Data
@NoArgsConstructor
public class AuditLog extends AuditBase {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
private Long entityId;
@Type(type = "yes_no")
private Boolean isDeleted = false;
@Convert(converter = HashMapConverter.class)
private Map<String, Object> entityAttributes;
}
More details on how to save the JSON object can be seen through this link: https://www.baeldung.com/hibernate-persist-json-object
Also, moving forward, it really is recommended to create your own equals
, hashCode
and toString
method for your entities and don't rely on Lombok etc.
来源:https://stackoverflow.com/questions/65386561/stackoverflow-error-when-saving-an-objects-tostring-value-java-hibernate-spri