Детализированный объект 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 не может перехватывать вызовы методов от одного объекта к этому же объекту. Таким образом, у вас фактически есть одна транзакция для каждого обращения к вашим репозиториям. Вот почему ваше местоположение отделено.

Другие вопросы по тегам