#java #spring-boot #hibernate #spring-data-jpa #many-to-many
#java #spring-boot #спящий режим #spring-data-jpa #многие ко многим
Вопрос:
У меня есть объект Product, который связан с категорией с отношением ManyToOne
public class Product {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "category", nullable = false)
@JsonIgnore
private Category category;
}
Теперь сущность категории имеет отношение «Многие ко многим» к сущности бренда.
public class Brand {
@ManyToMany(mappedBy = "productCompanyList")
@JsonIgnore
private List<Category> categories;
}
public class Category {
@ManyToMany
@Column(nullable = false)
@JsonIgnore
private List<Brand> Brands;
}
и, наконец, модель покупки относится к продукту
public class Purchase {
@OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
private Product product;
}
Когда я запускаю приведенный ниже код, он генерирует исключение переполнения стека, поскольку он рекурсивно извлекает категорию и бренд бесконечно.
List<Purchase> collect = purchaseList.stream()
.map(a -> {
Product p = this.Productrepo.getById(a.getProduct().getId()).orElse(null);
a.setProduct(p);
System.out.println(p);
return a;
})
.collect(Collectors.toList());
Пожалуйста, дайте мне знать, есть ли способ решить эту проблему, или мне придется реструктурировать свой код.
Комментарии:
1. Это такая вещь, которая создаст исключение StackOverflow даже в тех случаях, когда вы создаете простой POJO и не имеет никакого отношения к базе данных. Простой обходной путь для этих сценариев заключается в использовании
List<Identifier>
вместо.
Ответ №1:
Для ManyToMany требуется таблица сопоставления. Код должен выглядеть следующим образом
@Entity
public class Brand {
@ManyToMany(mappedBy = "brands")
@JsonIgnore
private List<Category> categories;
}
@Entity
public class Category {
@ManyToMany
@JoinTable(
name = "Category_Brand",
joinColumns = { @JoinColumn(name = "category_id") },
inverseJoinColumns = { @JoinColumn(name = "brand_id") }
)
@JsonIgnore
private List<Brand> brands;
}
mappedBy должен указывать на поле в другом объекте.
Ответ №2:
Это была первая проблема, с которой я столкнулся при сопоставлении отношений «Многие ко многим» с Hibernate, и, к сожалению, это не имеет ничего общего с сопоставлением сущностей.
Давайте на мгновение забудем о спящем режиме и проверим структуру классов.
Продукт, который имеет категорию private Category category;
Категория имеет список брендов List<Brand> Brands;
Бренд имеет список категорий List<Category> categories;
Когда вы это делаете System.out.println(p);
, это вызывает p.toString() Это означает, что он вызывает вызов category.toString()
, который затем вызывает вызов для Brands.toString()
каждого бренда, имеющего список категорий, так categories.toString()
называемый. Теперь каждая категория так называется список брендов brands.toString()
. И цикл продолжается.
Я понимаю, что вы использовали @JsonIgnore
, чтобы избежать этой проблемы при сериализации объекта, но при выполнении toString() выполняются рекурсивные вызовы.
Могут быть и другие способы решения этой проблемы, но ниже приведены два способа, которыми я ее решил:
- Переопределите
toString()
метод и исключите список
Вы можете явно переопределить toString()
метод и исключить список из состава строки, перенастроенной с помощью toString() по умолчанию
- Аннотируйте свои поля с
@ToString.Exclude
помощью lombok
Если вы используете lombok для генерации toString() , вы можете аннотировать эти списки, @ToString.Exclude
чтобы исключить их из числа строк, перенастроенных методом toString(), сгенерированным Lombok.
public class Brand {
@ToString.Exclude
@ManyToMany(mappedBy = "productCompanyList")
@JsonIgnore
private List<Category> categories;
}
public class Category {
@ManyToMany
@Column(nullable = false)
@ToString.Exclude
@JsonIgnore
private List<Brand> Brands;
}