JPA возвращает только первый экземпляр объекта @ManyToOne - PullRequest
0 голосов
/ 15 сентября 2018

Задача

У меня есть два объекта, Deck и Card, которые имеют отношение многие ко многим. Сущность для таблицы соединения - DeckCard, содержащая встроенный идентификатор 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":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, чтобы предположить, почему это происходит или как это предотвратить.

Edit:

Я пытался изменить Deck.cards, DeckCardId.deck и DeckCardId.card на eager, и я получил тот же результат. Однако при отладке я заметил, что даже с FetchType.LAZY я получаю полный объект DeckCard.id.card в Eclipse, и только после сериализации List<Deck> JSON содержит идентификатор только для последующих появлений каждой карты. Может ли это быть проблема сериализации Джексона, а не проблема 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 голосов
/ 22 октября 2018

Я столкнулся с точно такой же проблемой на днях.Оказывается, это не ошибка JPA (вы можете видеть в отладчике, что он правильно отображает все экземпляры), а скорее сериализатор Джексона.

Вам нужно изменить или удалить JsonIdentityInfo из вашего класса и внезапно все экземплярыбудут отображены их отношения!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...