使用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,为什么会这样?
答案 0 :(得分:1)
尽管您的方法是使用Transactional注释的,但它们并非如此。
Spring事务是基于代理的。这意味着Spring会拦截从一个Spring bean到另一个Spring bean的调用,这要归功于代理,并且启动/提交事务是另一个bean的方法是事务性的。
Spring不能拦截从一个对象到同一个对象的方法调用。因此,对于每个对存储库的调用,实际上只有一个事务。这就是你的位置分离的原因。