Недавно у меня возникли проблемы с сохранением объекта в режиме гибернации. Я хотел бы описать свое путешествие по хорошо известному примеру документирования микрофонов караоке-бара! Здесь у нас есть два микрофона (конечно, желтый к обязательному черному!) для нашего бара под названием Monster Karaoke. Сначала мы создаем микрофоны, затем панель и в конечном итоге сохраняем ее в соответствующем репозитории JPA.
Microphone blackMicrophone = Microphone.builder() .microphoneId(UUID.randomUUID()) .color("black") .build(); Microphone yellowMicrophone = Microphone.builder() .microphoneId(UUID.randomUUID()) .color("yellow") .build(); ListmicrophoneList = Arrays.asList(blackMicrophone, yellowMicrophone); KaraokeBar karaokeBar = KaraokeBar.builder() .barId(UUID.randomUUID()) .name("Monster Karaoke") .microphoneList(microphoneList) .build(); karaokeBarRepository.save(karaokeBar);
Объект караоке-бара содержит список объектов микрофона , который помечен как Hibernate @OneToMany
. Но при выполнении кода происходит следующее:
Request processing failed; nested exception is org.springframework.orm.jpa.JpaObjectRetrievalFailureException: Unable to find de.schmowser.radio.domain.Microphone with id fb133ab8-72ee-4bf4-ac5f-4701cb99e766;
Не удалось найти? Я вижу, что нам нужно каскадировать постоянство таким образом, чтобы наши великолепно раскрашенные микропроцессоры сохранялись, когда есть экземпляр караоке-бара. Но даже после добавления
@OneToMany(cascade = CascadeType.PERSIST) private ListmicrophoneList;
a javax.настойчивость. EntityNotFoundException
убеждает нас в том, что микрофоны все еще не зафиксированы для сохранения. И только сейчас я вспоминаю, что мы знаем кого-то, кто отвечает за сохранение любых объектов в базах данных. Это EntityManager внутри @PersistenceContext
. После его автоматического подключения и использования EntityManager.persist появляется следующее сообщение:
No EntityManager with actual transaction available for current thread - cannot reliably process 'persist' call
Да, мы должны аннотировать метод с помощью @Transactional
, обозначая, что все транзакционные вопросы обрабатываются в фоновом режиме автоматически! Кроме того, мы должны сначала определить объект на стороне владельца (караоке-бар), затем установить его в наших экземплярах микрофона, а затем настроить список в karaokeBar. В противном случае возникает это исключение.
org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation : de.schmowser.radio.domain.Microphone.karaokeBar -> de.schmowser.radio.domain.KaraokeBar
Такое ощущение, что мы близки к тому, чтобы сообщить нашим клиентам, какие микрофоны предлагает наш бар. Любопытно, что без команды Repository.save компилируется следующий фрагмент кода…
ListmicrophoneList = Arrays.asList(blackMicrophone, yellowMicrophone); karaokeBar.setMicrophoneList(microphoneList); entityManager.persist(karaokeBar); entityManager.persist(blackMicrophone); entityManager.persist(yellowMicrophone); karaokeBarRepository.save(karaokeBar);
…в то время как с сохранением этого не происходит. Включение последней строки вознаграждает нас за java.lang. Исключение UnsupportedOperationException
. Это на самом деле довольно трудно расшифровать. Так или иначе, Hibernate не любит ни нас, ни неизменяемые списки. Поэтому мы даем ему то, что он хочет: что-то модифицировать.
ListmicrophoneList = new ArrayList<>(); microphoneList.add(blackMicrophone); microphoneList.add(yellowMicrophone);
Гибернация милосердна и дает нам то, чего мы желаем!
Hibernate: insert into karaoke_bar (name, karaokebar_id) values (?, ?) Hibernate: insert into microphone (color, karaokebar_id, microphone_id) values (?, ?, ?) Hibernate: insert into microphone (color, karaokebar_id, microphone_id) values (?, ?, ?)
Это в любом случае близко к вашему опыту работы с гибернацией? Как вы справляетесь с этим штормом исключений – в целом и, возможно, более эффективно?
Оригинал: “https://dev.to/schmowser/onetomany-relations-in-hibernate-and-its-perils-1a41”