子对象上的spring-data detchated实体

时间:2014-04-25 22:16:58

标签: java spring jpa spring-data spring-data-jpa

使用spring-data的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

的save方法中
entityInformation.isNew(entity) 

将返回true并在尝试保存第二个实体时抛出上述异常。

它将实体视为新实体的事实是正常的,因为它的id值为null。

我不明白如何分配位置,因为我在将它分配到PlannedEvent之前取出它?

可能是因为locationRepository和plannedEventRepository都使用了entityManager的不同实例?

当我在save方法中打破并调用.merge而不是.persist时,它会很好地存储第二个PlannedEvent,为什么会这样?

1 个答案:

答案 0 :(得分:1)

尽管您的方法是使用Transactional注释的,但它们并非如此。

Spring事务是基于代理的。这意味着Spring会拦截从一个Spring bean到另一个Spring bean的调用,这要归功于代理,并且启动/提交事务是另一个bean的方法是事务性的。

Spring不能拦截从一个对象到同一个对象的方法调用。因此,对于每个对存储库的调用,实际上只有一个事务。这就是你的位置分离的原因。