Добрый день всем!
В своем проекте Spring я пытаюсь избавиться от довольно популярной, на мой взгляд, ошибки - org.hibernate.loader.MultipleBagFetchException
. Я хочу реализовать методы getOneClothesById(Long id)
и getAllClothes()
.
В @ Service
-слое, я хочу взять данные из Clothes
(size
, category
, color
) и отобразить их на экране и / или изменить их перед отправкой в другое место .
Я прочитал пару книг и даже зашел на 4-5 страницу в Google, но полагаю, что до сих пор не нашел лучшего решения.
Люди предлагают следующие подходы для решения проблемы:
Используйте Set<...>
вместо List<...>
Ссылки:
Мой комментарий: Я считаю, что это не очень хороший подход. Вот ссылка с объяснением:
- Используйте
Fetch.Eager
вместо Fetch.Lazy
или добавьте @LazyCollection(LazyCollectionOption.FALSE)
Ссылки: Используйте @OrderColumn
(раньше - @IndexColumn
)
Ссылки:
Мой комментарий: Это deasls (частично ) с проблемой, но согласно документации - это исключает вариант orderBy
Используйте подход Влада Михалчи: Ссылки:
Мой комментарий: я полагаю, что это лучшее решение, которое имеет дело с org.hibernate.LazyInitializationException
это его. Однако я хотел бы перенять его подход к реальности Spring Framework (2.2.6.RELEASE
).
То, что я хочу:
Я хочу найти лучшую подход к решению проблемы в рамках Spring Framework. Ответ после картинки с базой данных - это решение проблемы . Однако у меня есть следующие вопросы:
- Мне не нравится тот факт, что я использую
@Transactional
, чтобы избавиться от проблемы org.hibernate.LazyInitializationException: failed to lazily initialize ...
- Как правильно использовать этот подход в отдельный класс, чтобы использовать его повторно? Было бы правильным решением, если бы я создал класс
ClothesRepositoryFetch
с аннотацией @Repository
, а затем вызвал бы этот репозиторий в @Service
-слое. - Есть ли лучшее решение этой проблемы, используя только JPA ?
Моя база данных выглядит так:
Сервис одежды. java
@Transactional
public ResultDTO getOneClothesById(Long id){
ResultDTO resultDTO = new ResultDTO();
//Getting clothes with Category
Clothes clothes = entityManager
.createQuery(
"select distinct clothes " +
"from Clothes clothes " +
"left join fetch clothes.categories " +
"where clothes.id = :id", Clothes.class)
.setHint(QueryHints.PASS_DISTINCT_THROUGH, false)
.setParameter("id", id)
.getSingleResult();
//Getting clothes with Color
clothes = entityManager
.createQuery(
"select distinct clothes " +
"from Clothes clothes " +
"left join fetch clothes.colors color " +
"where clothes = :clothes", Clothes.class)
.setParameter("clothes", clothes)
.setHint(QueryHints.PASS_DISTINCT_THROUGH, false)
.getSingleResult();
//Getting clothes with Size
clothes = entityManager
.createQuery(
"select distinct clothes " +
"from Clothes clothes " +
"left join fetch clothes.sizes size " +
"where clothes = :clothes", Clothes.class)
.setParameter("clothes", clothes)
.setHint(QueryHints.PASS_DISTINCT_THROUGH, false)
.getSingleResult();
System.out.println(clothes.getCategories().size());
System.out.println(clothes.getColors().size());
System.out.println(clothes.getSizes().size());
if (clothes != null){
resultDTO.setResult(ResultForDTO.SUCCESS);
resultDTO.addClothesDTO(converterClothesToDTO(clothes));
} else {
resultDTO.setResult(ResultForDTO.ERROR);
resultDTO.setMessage("Невозможно найти одежду по такому ID");
}
return resultDTO;
}
Код
Одежда. java:
@Entity
@Table
@NoArgsConstructor
@Getter
@Setter
@ToString
public class Clothes {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column
private String name;
@Column
private Double price;
/*
Color: ManyToMany
*/
@ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
@JoinTable(name = "clother_color",
joinColumns = { @JoinColumn(name = "clother_id") },
inverseJoinColumns = { @JoinColumn(name = "color_id") })
private List<Color> colors = new ArrayList<>();
public void addColor(Color color) {
colors.add(color);
color.getClothes().add(this);
}
public void removeColor(Color color) {
colors.remove(color);
color.getClothes().remove(this);
}
/*
Size: ManyToMany
*/
@ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
@JoinTable(name = "clother_size",
joinColumns = { @JoinColumn(name = "clother_id") },
inverseJoinColumns = { @JoinColumn(name = "size_id") })
private List<Size> sizes = new ArrayList<>();
public void addSize(Size size) {
sizes.add(size);
size.getClothes().add(this);
}
public void removeSize(Size size) {
sizes.remove(size);
size.getClothes().remove(this);
}
/*
Category: ManyToMany
*/
@ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
@JoinTable(name = "clother_category",
joinColumns = { @JoinColumn(name = "clother_id") },
inverseJoinColumns = { @JoinColumn(name = "category_id") })
private List<Category> categories = new ArrayList<>();
public void addCategory(Category category) {
categories.add(category);
category.getClothes().add(this);
}
public void removeCategory(Category category) {
categories.remove(category);
category.getClothes().remove(this);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Clothes clothes = (Clothes) o;
return Objects.equals(id, clothes.id);
}
}
Категория. java:
Категория, размер, цвет имеют общую структуру, поэтому я представлю только один из них
@Entity
@Table
@NoArgsConstructor
@Getter
@Setter
public class Category {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column
private String name;
@ManyToMany(mappedBy="categories")
private List<Clothes> clothes = new ArrayList<>();
public void addClothes(Clothes clothes) {
this.clothes.add(clothes);
clothes.getCategories().add(this);
}
public void removeClothes(Clothes clothes) {
this.clothes.remove(clothes);
clothes.getCategories().remove(this);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Category category = (Category) o;
return Objects.equals(id, category.id);
}
}