JPA仅返回@ManyToOne对象的第一个实例

时间:2018-09-15 18:57:48

标签: java json spring hibernate jpa

问题

我有两个对象DeckCard,它们具有多对多关系。联接表的实体为DeckCard,其中包含一个嵌入式ID DeckCardId,以便于在不创建无限循环的情况下从卡组内部对象访问卡。 (所有代码都在下面。)

当我使用JpaRepository.findAll()检索所有牌组时,我正确地收到了所有牌组的列表,每个牌组都有一个cards属性,其中包含DeckCards列表。深入研究DeckCard.id.card时,我希望看到完整的Card对象及其所有属性,如下所示:

[
    {
        "id":1,
        "name":"Deck 1",
        "cards":[
            {
                "id":{
                    "deck":1,
                    "card":{
                        "id":1,
                        "name":"Card 1"
                    }
                },
                ...
            },
            {
                "id":{
                    "deck":1,
                    "card":{
                        "id":2,
                        "name":"Card 2"
                    }
                },
                ...
            }
        ]
    },
    {
        "id":2,
        "name":"Deck 2",
        "cards":[
            {
                "id":{
                    "deck":2,
                    "card":{
                        "id":1,
                        "name":"Card 1"
                    }
                },
                ...
            },
            {
                "id":{
                    "deck":2,
                    "card":{
                        "id":3,
                        "name":"Card 3"
                    }
                },
                ...
            }
        ]
    },
    {
        "id":3,
        "name":"Deck 3",
        "cards":[
            {
                "id":{
                    "deck":3,
                    "card":{
                        "id":3,
                        "name":"Card 3"
                    }
                },
                ...
            },
            {
                "id":{
                    "deck":3,
                    "card":{
                        "id":4,
                        "name":"Card 4"
                    }
                },
                ...
            }
        ]
    },
]

对于每个Card的首次出现,这都是事实;但是,对于该卡在其他牌组中的所有后续出现,我得到的只是该卡的ID:

[
    {
        "id":1,
        "name":"Deck 1",
        "cards":[
            {
                "id":{
                    "deck":1,
                    "card":{
                        "id":1,
                        "name":"Card 1"
                    }
                },
                ...
            },
            {
                "id":{
                    "deck":1,
                    "card":{
                        "id":2,
                        "name":"Card 2"
                    }
                },
                ...
            }
        ]
    },
    {
        "id":2,
        "name":"Deck 2",
        "cards":[
            {
                "id":{
                    "deck":2,
                    "card":1 // I expected this to be the full model of Card 1
                },
                ...
            },
            {
                "id":{
                    "deck":2,
                    "card":{
                        "id":3,
                        "name":"Card 3"
                    }
                },
                ...
            }
        ]
    },
    {
        "id":3,
        "name":"Deck 3",
        "cards":[
            {
                "id":{
                    "deck":3,
                    "card":3 // I expected this to be the full model of Card 3
                },
                ...
            },
            {
                "id":{
                    "deck":3,
                    "card":{
                        "id":4,
                        "name":"Card 4"
                    }
                },
                ...
            }
        ]
    },
]

我猜想这与默认设置有关,以避免不必要的重复,但是我在JPA文档中没有发现任何建议来说明为什么会发生这种情况或如何防止这种情况发生。

编辑:

我尝试将Deck.cardsDeckCardId.deckDeckCardId.card更改为渴望,但得到相同的结果。但是我在调​​试中确实注意到,即使使用FetchType.LAZY,我也可以在Eclipse中获得完整的DeckCard.id.card对象,并且只有在List<Deck>序列化之后,JSON才包含用于随后出现的每张卡。可能是Jackson序列化问题,而不是JPA问题?

代码

Deck.java

@Entity
@JsonIdentityInfo(
    generator = ObjectIdGenerators.PropertyGenerator.class,
    property = "id",
    scope = Deck.class
)
public class Deck {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;

    private String name;

    @OneToMany(
        fetch = FetchType.LAZY,
        mappedBy = "id.deck",
        cascade = CascadeType.ALL
    )
    private List<DeckCard> cards;

    // standard getters & setters
}

DeckCard.java

@Entity
@AssociationOverrides({
    @AssociationOverride(name = "id.deck", joinColumns = @JoinColumn(name = "deck_id")),
    @AssociationOverride(name = "id.card", joinColumns = @JoinColumn(name = "card_id"))
})
public class DeckCard implements Serializable {

    @EmbeddedId
    private DeckCardId id = new DeckCardId();

    private int quantity;

    public DeckCard() {}

    // standard getters & setters for id & quantity

    @Transient
    public Deck getDeck() {
        return getId().getDeck();
    }

    public void setDeck(Deck deck) {
        getId().setDeck(deck);
    }

    @Transient
    public Card getCard() {
        return getId().getCard();
    }

    public void setCard(Card card) {
        getId().setCard(card);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (o == null || getClass() != o.getClass())
            return false;

        DeckCard that = (DeckCard) o;
        if (getId() != null ? !getId().equals(that.getId()) : that.getId() != null)
            return false;

        return true;
    }

    @Override
    public int hashCode() {
        return getId() != null ? getId().hashCode() : 0;
    }
}

DeckCardId.java

@Embeddable
public class DeckCardId implements Serializable {

    private static final long serialVersionUID = -6470278480687272622L;

    @ManyToOne(fetch = FetchType.LAZY)
    @JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
    private Deck deck;

    @ManyToOne(fetch = FetchType.LAZY)
    @JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
    private Card card;

    // standard getters & setters

    @Override
    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (o == null || getClass() != o.getClass())
            return false;

        DeckCardId that = (DeckCardId) o;
        if (deck != null ? !deck.equals(that.deck) : that.deck != null)
            return false;
        if (card != null ? !card.equals(that.card) : that.card != null)
            return false;

        return true;
    }

    @Override
    public int hashCode() {
        int result = (deck != null ? deck.hashCode() : 0);
        return 31 * result + (card != null ? card.hashCode() : 0);
    }
}

解决方案

虽然我最初并未发布Card类的代码,但选择的答案使我意识到它包含与JsonIdentityInfo相同的Deck注释:

@Entity
@JsonIdentityInfo(
    generator = ObjectIdGenerators.PropertyGenerator.class,
    property = "id",
    scope = Card.class
)
public class Card {

    // rest of class

通过从Card中删除此注释,每个Card中的第一个之后的所有实例将全部序列化,而不是通过引用进行序列化。

1 个答案:

答案 0 :(得分:1)

前几天,我遇到了完全相同的问题。事实证明,这不是JPA的错(您可以在调试器中看到它正确映射了所有实例),而是Jackson序列化器。

您需要从您的课程中修改或删除JsonIdentityInfo,突然所有实例都将映射它们的关系!