Имея следующую тестовую настройку с использованием весенних данных CrudRepository
@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 и выкинет вышеописанное исключение при попытке сохранить второй объект.
Тот факт, что он видит объект как новый, является нормальным, поскольку его значение id равно null.
Я не понимаю, как можно отделить местоположение, так как я извлекаю его, прежде чем передать его в PlannedEvent?
Может быть, потому, что и locationRepository, и планируемыйEventRepositor используют другой экземпляр entityManager?
Когда я нарушаю метод сохранения и вызываю.merge вместо.perist, он сохраняет второй PlannedEvent красиво, почему?
Несмотря на то, что ваши методы аннотируются с Transactional, это не так.
Весенние транзакции основаны на прокси. Это означает, что Spring перехватывает вызовы от одного Spring bean к другому Spring bean, благодаря прокси, и запускает/совершает транзакцию, этот другой метод bean-транзакции является транзакционным.
Spring не может перехватывать вызовы метода с одного объекта на этот же объект. Таким образом, у вас действительно есть одна транзакция для каждого звонка в ваши репозитории. Вот почему ваше местоположение отключено.