Не удается прочитать данные из сущности из-за исключения LazyInitializationException
Я должен показать некоторые данные в интерфейсе, который объединен из двух объектов. Эти две сущности имеют такие отношения:
@Entity
@Table(name="T_LOAD_ORDERS")
@Data
public class LoadOrders {
.
.
.
@ManyToOne(fetch=FetchType.EAGER)
@JoinColumn(name="SUPERVISOR")
private MdUser supervisor;
.
.
.
}
@Entity
@Table(name="T_MD_USER")
@Data
public class MdUser {
@Id
@Column(name="USER_ID")
private String userId;
.
.
.
@OneToMany(fetch=FetchType.LAZY)
@JoinColumn(name="USER_ID")
private List<OrderStates> orderStates;
@OneToMany(fetch=FetchType.LAZY)
@JoinColumn(name="SUPERVISOR")
private List<LoadOrders> loadOrders;
@OneToMany(fetch=FetchType.LAZY)
@JoinColumn(name="USER_ID")
private List<Staff> staffs;
}
Поэтому я должен получить данные из сущности LoadOrders и userId из MdUser. Я использую JPARepository из Spring, поэтому я вызываю findAll() для объекта LoadOrders. Пока это работает нормально. Но когда я добавляю эти несколько строк кода:
if (loadOrderEntity.getSupervisor() != null) {
MdUser user = loadOrderEntity.getSupervisor();
this.supervisor = user.getUserId();
} else {
this.supervisor = null;
}
Я получаю это исключение:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: de.XXXX.XX.XXXX.data.entity.MdUser.orderStates, could not initialize proxy - no Session
Что ж, я подумал, что я бы очень умно изменил названный fetchtype на eager, но, как показывает код, в этой сущности есть более ленивые выборки. Изменение их всех на нетерпеливые звучит для меня довольно глупо и тоже не работает:
Caused by: org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags: [de.fraport.bvd.mobisl.data.entity.MdUser.staffs, de.fraport.bvd.mobisl.data.entity.MdUser.orderStates]
Так что я могу сделать здесь?
РЕДАКТИРОВАТЬ
После намека Петра Подразы, что добавление @Transactional к методу, который вызывает данные, я изменил свой код, который читает из базы данных следующим образом:
@Transactional(readOnly=true)
@GetMapping("/dispo/orderViewList")
private String showOrderList(@RequestParam("page") Optional<Integer> page, Model model) {
page.ifPresent(p -> currentPage = p);
Page<LoadOrders> pagedList = orderRepository.findAll(PageRequest.of(currentPage - 1, INITIAL_PAGE_SIZE));
List<LoadOrder> orderList = service.createLoadOrderPage(pagedList);
model.addAttribute("page", orderList);
model.addAttribute("currentPage", currentPage);
model.addAttribute("totalPages", pagedList.getTotalPages());
return "/dispo/orderViewList";
}
orderRepository расширяет JPARepository, а метод findAll() является реализацией, предоставляемой Spring. Но все равно это не работает.
@EnableTransactionManagement предоставляется моим классом JPAConfig, который импортируется в основной класс приложения следующим образом:
@SpringBootApplication
@Import(value=JPAConfig.class)
public class WebApplication {
@Autowired
private JPAConfig config;
public static void main(String[] args) {
SpringApplication.run(WebApplication.class, args);
}
}
2 ответа
У вас есть два варианта здесь:
1) Продолжайте лениво загружать свои коллекции и исправляйте код, чтобы он работал внутри сеанса.
2) Измените тип выборки так, чтобы он загружался с нетерпением - этот подход потребует изменения типов коллекции внутри MdUser
класс от List
в Set
так как вы не можете одновременно получить несколько сумок. Подробнее о проблеме вы можете прочитать здесь: https://blog.eyallupu.com/2010/06/hibernate-exception-simultaneously.html
Сделать @Transactional
аннотацию работы, которую вы должны добавить @EnableTransactionManagement
аннотации в ваш файл AppConfig.
Говоря о Caused by: org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags: [de.fraport.bvd.mobisl.data.entity.MdUser.staffs, de.fraport.bvd.mobisl.data.entity.MdUser.orderStates]
Это происходит потому, что в терминах Hibernate вы на самом деле не используете List
но Bag
s, которые не являются упорядоченными коллекциями с возможными дубликатами. в MdUser
класс у вас есть два свойства, которые связаны с тем же свойством:
@OneToMany(fetch=FetchType.LAZY)
@JoinColumn(name="USER_ID")
private List<OrderStates> orderStates;
а также
@OneToMany(fetch=FetchType.LAZY)
@JoinColumn(name="USER_ID")
private List<Staff> staffs;
так в случае Eager
получение Hibernate не знает, как развивать их обоих как басгов. Чтобы избежать этой проблемы, есть как минимум два способа заставить Hibernate разрешать эти свойства как реальные списки Hibernate:
Добавить порядок для каждого объекта, т. Е. (При условии
Staff
имеет это свойство)@OneToMany(fetch=FetchType.EAGER) @JoinColumn(name="USER_ID") @OrderBy("POSITION") private List<Staff> staffs;
- замещать
List
сSet
, Если вы не нуждаетесь в заказе, используйтеSet
вместоList
и это автоматически заставит Hibernate распознавать Коллекции как Наборы Hibernate. Это позволит избежать двусмысленности при получении
Надеюсь, это поможет