Детализированный объект Spring-Data на дочернем объекте
Наличие следующей тестовой установки с использованием CrudRepository от spring-data
@Test
public void testCreateEventsThatShareALocation() {
createFirstPlannedEvent();
createSecondPlannedEvent();
}
@Transactional
private void createSecondPlannedEvent() {
PlannedEvent plannedEvent = new PlannedEvent();
Location location = locationRepository.findByName(LOCATION_NAME);
plannedEvent.setLocation(location);
plannedEventRepository.save(plannedEvent);
}
@Transactional
public void createFirstPlannedEvent() {
PlannedEvent plannedEvent = new PlannedEvent();
Location location = createLocation(LOCATION_NAME);
plannedEvent.setLocation(location);
plannedEventRepository.save(plannedEvent);
}
public Location createLocation(String name) {
Location location = new Location();
location.setName(name);
location.setSomeOtherStuff....(); // Does NOT call the repository save method of Location (there is no repository for Location)
return location;
}
Когда я запускаю этот тест, я получаю, когда пытаюсь сохранить второй PlannedEvent:
Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: com.eventage.entities.Location
Когда я отлаживаю код, я замечаю, что первое создание PlannedEvent проходит нормально, но не удается во втором.
Там в методе сохранения SimpleJpaRepository
entityInformation.isNew(entity)
вернет true и выдаст указанное выше исключение при попытке сохранить 2-ю сущность.
Тот факт, что он видит сущность как новую, является нормальным, поскольку его значение id равно нулю.
Я не понимаю, как локация может быть отсоединена, так как я собираю ее перед передачей в PlannedEvent?
Может ли это быть из-за того, что и locationRepository, и запланированный, и репозиторий используют разные экземпляры entityManager?
Когда я ломаюсь в методе сохранения и вызываю.merge вместо.persist, он хорошо сохраняет второй PlannedEvent, почему это так?
1 ответ
Несмотря на то, что ваши методы снабжены комментариями Transactional, это не так.
Spring-транзакции основаны на прокси. Это означает, что Spring перехватывает вызовы от одного компонента Spring к другому компоненту Spring, благодаря прокси, и запускает / фиксирует транзакцию, если метод этого другого компонента является транзакционным.
Spring не может перехватывать вызовы методов от одного объекта к этому же объекту. Таким образом, у вас фактически есть одна транзакция для каждого обращения к вашим репозиториям. Вот почему ваше местоположение отделено.