休眠多对多和JSON序列化 [英] Hibernate Many to Many and JSON serialization

查看:142
本文介绍了休眠多对多和JSON序列化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Hybernate来保留一些具有多对多"关系的bean.这是两个豆子:

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;
    }

}

这可以正常工作,但是问题是我需要使用JSON对这些bean进行序列化/反序列化.这样做会导致无限递归,因为每个对象Email都会尝试序列化其所有对象Pratica,这反过来又会尝试序列化其所有Email,依此类推,直到出现StackOverflow异常.

我已经在网上和SO上进行了搜索,并且找到了针对此问题的几种解决方案,但是由于我的项目的配置,它们都不起作用或不适用.特别是,这是我尝试过的:

解决方案1:使用@JsonManagedReference/@JsonBackReference

这似乎是最建议的解决方案.但是,这仅适用于(据我所知)一对多或多对一关系,因为@JsonBackReference批注不支持集合(列表,数组,集合等).或者至少,这就是文档所说的,并且在尝试时我得到了一个似乎可以证实这一点的异常.我在网上找到了几个在List上使用此批注的示例,但对我来说却行不通.如果有人对此有任何线索,请详细说明.

解决方案2:不要使用@JsonIgnore

来保持关系的一侧

@JsonIgnore添加到一个字段中,如下所示:

@ManyToMany()
@JoinTable(name = "email_pratica", joinColumns = {
        @JoinColumn(name = "fk_email_id")
}, inverseJoinColumns = {
        @JoinColumn(name = "fk_pratica_id")
})
@JsonIgnore
private List<Pratica> pratiche;

打破无限循环,因此可以工作.这也适合我,因为我真的不需要Email对象上的Pratica列表,所以我从不使用它. 但是,这带来了另一个大问题:每当我将对象持久化到数据库中时,Pratica的列表将为空(因为在JSON序列化期间丢失了),因此Hibernate将从我的Join Table中删除关联记录. /p>

解决方案3:使用@JsonIdentityInfo
此解决方案需要Jackson 2.x,但是不幸的是,由于服务器无法控制的限制,我只能使用RESTeasy(resteasy-jackson-provider 2.3.6)提供的Jackson版本,其中提供了Jackson 1.x

解决方案4:使用自定义的序列化器/解串器
请参阅解决方案3.即使@JsonSerialize/@JsonDeserialize批注包含在Jackson 1.1中,似乎它们也未包含在RESTeasy提供的版本中,或者至少编译器找不到引用,所以我假设是这种情况.

那么,毕竟……对于我所拥有的限制,有没有解决此问题的解决方案?可以以非骇客的方式完成吗?如果不能,那么少乱"的非hacky方式是什么?

解决方案

您正在将数据库对象与json混合在一起,我认为这是主要问题,主要是设计缺陷.

当您要使用json以外的数据序列化数据时,必须更改持久性层.如果要更改持久性层,则也必须更改json内容.

更糟糕的是:假设您要对表示层进行单元测试-假设您在前端中使用json-在没有持久性层的情况下如何做到这一点?

所以我的答案是:分开您的担忧,将json与持久性脱钩.

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 Emails 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?

解决方案

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.

这篇关于休眠多对多和JSON序列化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆