У меня проблема при попытке получить все объекты «Discount», хранящиеся в моем приложении. «Скидка» - это объект, представленный следующим классом
@Entity
@NoArgsConstructor
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "store")
public class Discount {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// some fields like a description or a percentage providing info about a discount
@ManyToOne
@JoinColumn(name = "id_store")
private Store store;
@ManyToOne
@JoinColumn(name = "id_brand")
@JsonManagedReference
private Brand brand;
// getters, setters, equals and hashcode overriden below
Как видите, объект Discount может принадлежать Магазину или Бренду. Я гарантирую, что как в моем коде, так и в моей базе данных установлено только одно поле с триггером. В моем приложении Скидка может быть предоставлена как Брендом (поэтому мне не нужно создавать Скидку для каждого Магазина этого Бренда), так и конкретным Магазином может предоставить свою собственную Скидку. Мой класс Store выглядит так
@Entity
@NoArgsConstructor
public class Store extends Entite {
@OneToMany(mappedBy = "store", cascade = CascadeType.ALL)
private Set<Discount> discounts;
@ManyToOne
@JoinColumn(name = "id_brand", nullable = false)
private Brand brand;
// getters, setters, equals and hashcode overriden below
Как видите, Магазин имеет набор скидок и принадлежит бренду. Он расширяет класс Entite, потому что в моем приложении у меня также есть спортивные клубы, и они делятся некоторой информацией, но это не имеет отношения к моей проблеме. Наконец, вот класс Brand
@Entity
@NoArgsConstructor
public class Brand {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToMany(mappedBy = "brand", cascade = CascadeType.ALL)
private Set<Store> stores;
@OneToMany(mappedBy = "brand", cascade = CascadeType.ALL)
@JsonBackReference
private Set<Discount> discounts;
// getters, setters, equals and hashcode overriden below
Затем у класса «Мой бренд» есть набор магазинов и набор скидок.
Моя проблема в том, что я не могу получить всю скидку с помощью простого FindAll из моего interface DiscountRepository, расширяющего JpaRepository<Discount, Long>, потому что некоторые из скидок извлекаются внутри ассоциации. Ответ, который я получаю от метода FindAll, следующий
[
{
// discount fields such as percentage, ruling, etc
"store": {
"id": 75,
"name": "Nike Store",
"discounts": [
75
],
"brand": {
"name": "Nike",
"store": [
75
]
}
},
"brand": null
},
{
// discount fields such as percentage, ruling, etc
"store": null,
"brand": {
"name": "Uniqlo",
"stores": [
90
]
}
},
{
// discount fields such as percentage, ruling, etc
"store": null,
"brand": {
"name": "FNAC",
"stores": [
{
"id": 76,
// some store fields
// problem here because Discount are retrieved here and therefore not displayed in the list, only here
"discounts": [
{
// discount fields such as percentage, ruling, etc
// for an offer that isn't present in the original list returned
// by the find all method but only here, associated to this Store object
"store": 76,
"brand": null
}
],
"brand": {
"name": "FNAC",
"stores": [
76
]
}
}
]
}
},
76
]
Проблема здесь в том, что есть выборка Discount с Store, и поэтому эта скидка находится не в исходном списке, который возвращает метод, а в объекте Store, связанном со скидкой. Я попытался добавить FetchType.LAZY в свою ассоциацию «Магазин и скидка», но это не решило проблему. Следовательно, есть ли способ получить «только одноуровневую ассоциацию»? Я имею в виду, что я хотел бы получить свой объект Discount, но только с информацией о бренде и магазине, а не с магазином или брендом и всеми его связями (например, его набором скидок). Я надеюсь, что я был ясен, не стесняйтесь задавать мне любые вопросы.
Моя служба REST должна вернуть List<Discount>. Когда вы говорите «спроектируйте класс, соответствующий желаемой структуре», вы говорите, что я должен создать другой класс, имеющий только те поля, которые я хочу иметь? Но когда я получаю свое предложение, я хочу получить информацию о бренде и магазине, но не их ассоциации (которые содержат скидку, и это моя проблема). Кроме того, я не понимаю ваше последнее предложение, что вы имеете в виду, говоря «вы не можете справиться с тем, что делает [JsonIdentityInfo]»?
Разработка другого класса только для удаления данных, которые мне не нужны, потому что я не могу получить то, что хочу, - это неплохая практика? Я подумал об этом, когда решил использовать SQL-запрос для получения только тех полей, которые мне нужны, но на самом деле это не кажется действительно чистым, не так ли?
Если я правильно помню, я использовал аннотацию JsonIdendityInfo, потому что она позволяла мне предотвратить StackOverflowException из-за циклических ссылок. Однако я попытался удалить его, и теперь я отлично получаю все свои скидки, но все еще с их магазином и связанными с ними скидками (затем получаю их дважды). Я буду искать способ лениво получать скидки в магазине, чтобы перестать получать их несколько раз. Большое спасибо за вашу помощь
Ленивая загрузка не означает «без загрузки». Проектирование того, что вы хотите отправить, намного лучше, чем слепая отправка, где бы ни содержались ваши сущности, и прерывание работы всех клиентов ваших REST API всякий раз, когда вы что-то меняете на своем уровне сохраняемости. Контент JSON - это публичный контракт вашего REST API. Это не должно зависеть от того, как вы смоделировали свой уровень сохраняемости.
Поэтому, если я хорошо понимаю, мне следует разработать новый класс, содержащий: во-первых, всю информацию из скидки, которую я хочу получить, и во-вторых, информацию о магазине или информацию о бренде в зависимости от того, связана ли скидка с магазином или с брендом. . Должен ли я иметь атрибуты в моем новом классе, такие как storeName, storeAddress, storeCity,…? Поскольку эта информация уже присутствует в моем классе Store, я считаю ее избыточной, и разве этот дизайн не более подвержен поломке, поскольку у меня будут поля, присутствующие как в этом новом классе Discount, так и в классе Store и Adress (т. Е. Для Скидка, связанная с магазином)?
Я не могу сказать вам, что должна вернуть ваша веб-служба. Вы тот, кто должен это решить. Для чего нужна услуга «получить все скидки». Для чего его используют клиенты этого сервиса? Какая информация им нужна, чтобы достичь того, чего они хотят достичь?
При вызове этой услуги клиент должен получить список всех Скидок. Я хочу, чтобы каждый объект в моем списке содержал все поля со скидкой, поля магазина и поля адреса для этого магазина (адрес - это класс, а у магазина только один адрес). Вот что я хочу, чтобы моя служба вернулась, когда ее вызовут.
Затем создайте класс (или набор классов), точно соответствующий JSON, который вы хотите отправить обратно, и преобразуйте свои скидки в экземпляры этого класса: он будет загружать только то, что нужно загрузить, и отправлять то, что нужно отправить.
Итак, вы мне посоветуете создать DiscountDTO, которое будет содержать все поля Discount и дополнительную информацию, такую как String storeName, String storeAddress, String storeCity и так далее, верно? Затем этот класс будет содержать точно всю информацию, которая мне понадобится, и я не буду возвращать непосредственно Entities, поскольку это кажется плохой практикой из того, что я прочитал сегодня в других ответах.
Лучше было бы иметь StoreDTO в DiscountDTO.
Я подумал об этом в первую очередь, но, по логике, у меня должен быть List<Discount> в моем StoreDTO, не так ли?
Нет, поскольку вы, не, хотите отправить это в JSON, а DTO разработан в точном соответствии с тем, что вы хотите отправить в JSON.
Хорошо, но если в будущем мне придется получить весь свой магазин (или конкретный) с их / его скидками, не придется ли мне создавать StoreDTO с List<Discount?
да. Но затем вы создадите другие DTO, точно соответствующие JSON, который вы хотите отправить в этом другом, другом ответе. Или вы будете использовать те же DTO, но заполните их по-другому, решив оставить некоторые поля пустыми, потому что вы не хотите, чтобы они присутствовали в JSON.
Хорошо, тогда для меня это действительно ясно и имеет смысл. Большое спасибо за вашу помощь и ваше время.




Проблема не в загрузке. Это связано с тем, что вы возвращаете из службы REST, и с тем, как вы настраиваете сериализатор JSON. Точно определите, что должно содержаться в теле ответа, затем спроектируйте класс, соответствующий желаемой структуре, и преобразуйте свои скидки в экземпляры этого класса. Вы используете JsonIdentityInfo, но не можете справиться с тем, что он делает.