Question

Having the following test setup using spring-data's 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;
}

When I run this test I'm getting, when trying to store the second PlannedEvent:

Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: com.eventage.entities.Location

When I debug through the code I notice that the first creation of a PlannedEvent goes fine but it fails within the second one.

There within the save method of the SimpleJpaRepository

entityInformation.isNew(entity) 

will return true and throw the above exception when trying to save the 2nd entity.

The fact that it sees the entity as a new one is normal since it's id value is null.

I don't understand how the location can be detached since I'm fetching it before assing it to the PlannedEvent ?

Might it be because both the locationRepository and the plannedEventRepository use a different instance of the entityManager ?

When I break within the save method and call .merge instead of .persist it stores the second PlannedEvent nicely, why is that ?

Was it helpful?

Solution

Despite the fact that your methods are annotated with Transactional, they are not.

Spring transactions are proxy-based. That means that Spring intercepts calls from one Spring bean to another Spring bean, thanks to proxies, and starts/commits a transaction is this other bean's method is transactional.

Spring can't intercept method calls from one object to this same object. So you actually have one transaction for each call to your repositories. That's why your Location is detached.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top