问题
I'm using Hybernate
to persist some beans that have a Many-To-Many relationship. Here are the two beans:
Email.java
@Entity
@NamedQuery(name = "Email.findAll", query = "SELECT e FROM Email e")
public class Email implements Serializable {
private static final long serialVersionUID = 1L;
//... other fields ...
// bi-directional many-to-many association to Pratica
@ManyToMany()
@JoinTable(name = "email_pratica", joinColumns = {
@JoinColumn(name = "fk_email_id")
}, inverseJoinColumns = {
@JoinColumn(name = "fk_pratica_id")
})
private List<Pratica> pratiche;
public List<Pratica> getPratiche() {
return this.pratiche;
}
public void setPratiche(List<Pratica> pratiche) {
this.pratiche = pratiche;
}
}
Pratica.java
@Entity
@NamedQuery(name = "Pratica.findAll", query = "SELECT p FROM Pratica p")
public class Pratica extends AtstTable implements Serializable {
private static final long serialVersionUID = 1L;
//... other fields ...
// bi-directional many-to-many association to Email
@ManyToMany(mappedBy = "pratiche", fetch = FetchType.EAGER, cascade = {CascadeType.MERGE, CascadeType.PERSIST})
private List<Email> emails;
public List<Email> getEmails() {
return this.emails;
}
public void setEmails(List<Email> emails) {
this.emails = emails;
}
}
This works correctly, however the problem is that I then need to serialize/deserialize these beans using JSON
. Doing this causes an infinite recursion, since each object Email
tries to serialize all of its objects Pratica
, which in turn try to serialize all their Email
s and so on, until a StackOverflow
exception occours.
I've searched online and here on SO, and I've found several solutions to this problem, however none of them work or are applicable, due to the configuration of my project. In particular, here's what I've tried:
Solution 1: using @JsonManagedReference
/ @JsonBackReference
This seems to be the most suggested solution. However, this only works (as far as I can tell) for One-To-Many or Many-To-One relationships, since the @JsonBackReference
annotation does not support collections (Lists, Arrays, Sets, etc.). Or at least, this is what the documentation says and when trying I got an exception that seems to confirm this. I've found a couple of examples online that use this annotation on a List, but for me it didn't work. If anyone has any clue on this, please elaborate.
Solution 2: do not persist one side of the relationship using @JsonIgnore
Adding @JsonIgnore
to one of the fields, like this:
@ManyToMany()
@JoinTable(name = "email_pratica", joinColumns = {
@JoinColumn(name = "fk_email_id")
}, inverseJoinColumns = {
@JoinColumn(name = "fk_pratica_id")
})
@JsonIgnore
private List<Pratica> pratiche;
breaks the infinite loop, and thus works. It would also suit me, since I don't really need the list of Pratica
on the Email
objects, I never use it.
However, this introduces another big problem: whenever I persist the object back to the database the list of Pratica will be empty (since it was lost during JSON
serialization), and thus Hibernate will delete the association record from my Join Table.
Solution 3: using @JsonIdentityInfo
This solution needs Jackson 2.x
, but unfortunately, due to restrictions on the server that are beyond my control, I can only use the version of Jackson provided by RESTeasy
(resteasy-jackson-provider 2.3.6
), which provides a subset of Jackson 1.x
Solution 4: using a custom serializer/deserializer
See solution 3. Even tho the @JsonSerialize
/@JsonDeserialize
annotations are included in Jackson 1.1
, it seems they're not included in the version provided by RESTeasy, or at least the compiler can't find the references, so I assume this is the case.
So, after all this... is there a clean solution to this problem with the restrictions I have? Can it be done in a non-hacky way? And if it can't, what is the "less messy" non-hacky way?
回答1:
You're mixing database objects with json, that's in my opinion the main problem, the main design flaw.
When you want to serialize your data with something other than json, you have to change your persistency layer. If you want to change your persistency layer, you have to change your json stuff, too.
And worse: Let's say you want to unit test your presentation layer - assuming you use json in your frontend - how are you going to do that, without your persistency layer?
So my answer is: Separate your concerns, decouple json from persistency.
来源:https://stackoverflow.com/questions/41260579/hibernate-many-to-many-and-json-serialization