I'm having trouble updating an entity (Formation) in a ManyToMany relationship with another entity (Intervenant).
My integration test fails because of the following error:
.springframework.dao.InvalidDataAccessApiUsageException: detached
entity passed to persist: fr.foo.bar.model.Intervenant
The problem is that, according to the stacktrace, the exception is caught on a method that just retrieves data, so no call to save()
is made. I don't understand why this error appears.
Here is my test:
@BeforeEach
void setUp() {
Intervenant newIntervenant = new Intervenant();
newIntervenant.setIdnum("IDNUM_NEW");
newIntervenant.setIdentite("New Intervenant");
Formation formation = new Formation();
formation.setLibelle("Sample Formation");
formation.setCodeApogee("APOGEE123");
formation.setCodeVet("VET789");
formation.setIntervenants(Set.of(newIntervenant));
formationDto = formationMapper.toDto(formation);
}
@Test
@Transactional
void whenUpdateFormation_thenIntervenantsAreUpdated() throws Exception {
FormationDto createdFormationDto = formationService.create(formationDto);
formationRepository.flush();
IntervenantDto newIntervenantDto = new IntervenantDto(null, "New IDNUM", "New Name");
createdFormationDto.intervenants().add(newIntervenantDto);
Intervenant existingIntervenant = new Intervenant();
existingIntervenant.setIdnum("IDNUM_EXISTING");
existingIntervenant.setIdentite("Existing Intervenant");
existingIntervenant = intervenantRepository.saveAndFlush(existingIntervenant);
existingIntervenant = intervenantRepository.findById(existingIntervenant.getId()).orElseThrow();
createdFormationDto.intervenants().add(intervenantMapper.toDto(existingIntervenant));
FormationDto updatedFormationDto = formationService.update(createdFormationDto.id(), createdFormationDto); // detached entity passed to persist: fr.foo.bar.model.Intervenant
[...]
}
The exception occurs in the last line of the test, the one calling update()
Here is the update()
method:
@Override
@Transactional
public FormationDto update(Long id, FormationDto formationDto) throws Exception {
Formation formation = formationRepository
.findById(id)
.orElseThrow(() -> new EntityNotFoundException(String.format("La formation %d n'existe pas", id)));
Set<Intervenant> updatedIntervenants = new HashSet<>();
for (IntervenantDto intervenantDto : formationDto.intervenants()) {
if (intervenantDto.id() == null) {
IntervenantDto createdDto = intervenantService.create(intervenantDto);
updatedIntervenants.add(intervenantService.toEntity(createdDto));
} else {
Intervenant existing = intervenantService.getEntity(intervenantDto.id());
updatedIntervenants.add(existing);
}
}
formation.setIntervenants(updatedIntervenants);
Collection<FormationEtudiant> existingEtudiants = formationEtudiantService.findAllByFormation(formation); // detached entity passed to persist: fr.foo.bar.model.Intervenant
[...]
}
And the exception occurs when the formationEtudiantService.findAllByFormation()
method is called, as shown below:
@Override
public List<FormationEtudiant> findAllByFormation(Formation formation) {
return formationEtudiantRepository.findAllByFormation(formation);
}
And here is the repository:
public interface FormationEtudiantRepository extends JpaRepository<FormationEtudiant, FormationEtudiantId> {
List<FormationEtudiant> findAllByFormation(Formation formation);
}
Here's an extract from the Formation entity:
@Entity(name = "FormationEntity")
@Table(name = "formation")
public class Formation implements Identifiable<Long> {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", columnDefinition = "INT UNSIGNED not null")
private Long id;
@Column(name = "libelle", length = 100)
private String libelle;
@Column(name = "code_apogee", length = 100)
private String codeApogee;
@Column(name = "code_vet", length = 100)
private String codeVet;
@ManyToMany(cascade = {CascadeType.MERGE, CascadeType.PERSIST})
@JoinTable(name = "formation_intervenant",
joinColumns = @JoinColumn(name = "id_formation"),
inverseJoinColumns = @JoinColumn(name = "id_intervenant"))
private Set<Intervenant> intervenants = new LinkedHashSet<>();
public Set<Intervenant> getIntervenants() {
return intervenants;
}
public void setIntervenants(Set<Intervenant> intervenants) {
this.intervenants = intervenants;
}
}
And finally, the Intervenant entity:
@Entity(name = "IntervenantEntity")
@Table(name = "intervenant")
public class Intervenant implements Identifiable<Long> {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", columnDefinition = "INT UNSIGNED not null")
private Long id;
@Column(name = "idnum", nullable = false, length = 50)
private String idnum;
@Column(name = "identite", length = 100)
private String identite;
@Override
public Long getId() {
return id;
}
@ManyToMany(mappedBy = "intervenants")
private Set<Formation> formations = new LinkedHashSet<>();
public void setId(Long id) {
this.id = id;
}
public String getIdnum() {
return idnum;
}
public void setIdnum(String idnum) {
this.idnum = idnum;
}
public String getIdentite() {
return identite;
}
public void setIdentite(String identite) {
this.identite = identite;
}
public Set<Formation> getFormations() {
return formations;
}
public void setFormations(Set<Formation> formations) {
this.formations = formations;
}
}
It really drives me crazy... Can you help me ?
Edit: here's the stacktrace
.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist: fr.foo.bar.model.Intervenant
at .springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:290)
at .springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:233)
at .springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:550)
at .springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)
at .springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242)
at .springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:152)
at .springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at .springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:134)
at .springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at .springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
at .springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at .springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:218)
at jdk.proxy2/jdk.proxy2.$Proxy167.findAllByFormation(Unknown Source)
at fr.foo.bar.services.application.FormationEtudiantServiceImpl.findAllByFormation(FormationEtudiantServiceImpl.java:26)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
at java.base/java.lang.reflect.Method.invoke(Method.java:577)
at .springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343)
at .springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:699)
at fr.foo.bar.services.application.FormationEtudiantServiceImpl$$SpringCGLIB$$0.findAllByFormation(<generated>)
at fr.foo.bar.services.application.FormationServiceImpl.update(FormationServiceImpl.java:226)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
at java.base/java.lang.reflect.Method.invoke(Method.java:577)
at .springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343)
at .springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196)
at .springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at .springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:752)
at .springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123)
at .springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388)
at .springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
at .springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at .springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:752)
at .springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:703)
at fr.foo.bar.services.application.FormationServiceImpl$$SpringCGLIB$$0.update(<generated>)
at fr.foo.bar.FormationControllerIntegrationTests.whenUpdateFormation_thenIntervenantsAreUpdated(FormationControllerIntegrationTests.java:138)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
at java.base/java.lang.reflect.Method.invoke(Method.java:577)
at .junit.platformmons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:727)
at .junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
at .junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
at .junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:156)
at .junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:147)
at .junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:86)
at .junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103)
at .junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93)
at .junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
at .junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
at .junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
at .junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
at .junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:92)
at .junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:86)
at .junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:217)
at .junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at .junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:213)
at .junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:138)
at .junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68)
at .junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151)
at .junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at .junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at .junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at .junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at .junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at .junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at .junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at .junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
at .junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
at .junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at .junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at .junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at .junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at .junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at .junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at .junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at .junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
at .junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
at .junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at .junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at .junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at .junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at .junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at .junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at .junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at .junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
at .junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at .junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
at .junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:147)
at .junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:127)
at .junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:90)
at .junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:55)
at .junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:102)
at .junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:54)
at .junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
at .junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
at .junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
at .junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53)
at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:57)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:232)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)
Caused by: .hibernate.PersistentObjectException: detached entity passed to persist: fr.foo.bar.model.Intervenant
at .hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:121)
at .hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:118)
at .hibernate.internal.SessionImpl.persistOnFlush(SessionImpl.java:799)
at .hibernate.engine.spi.CascadingActions$8.cascade(CascadingActions.java:342)
at .hibernate.engine.spi.CascadingActions$8.cascade(CascadingActions.java:332)
at .hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:511)
at .hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:432)
at .hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:218)
at .hibernate.engine.internal.Cascade.cascadeCollectionElements(Cascade.java:545)
at .hibernate.engine.internal.Cascade.cascadeCollection(Cascade.java:475)
at .hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:435)
at .hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:218)
at .hibernate.engine.internal.Cascade.cascade(Cascade.java:151)
at .hibernate.event.internal.AbstractFlushingEventListener.cascadeOnFlush(AbstractFlushingEventListener.java:155)
at .hibernate.event.internal.AbstractFlushingEventListener.prepareEntityFlushes(AbstractFlushingEventListener.java:145)
at .hibernate.event.internal.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:79)
at .hibernate.event.internal.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:48)
at .hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:107)
at .hibernate.internal.SessionImpl.autoFlushIfRequired(SessionImpl.java:1385)
at .hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.lambda$new$0(ConcreteSqmSelectQueryPlan.java:100)
at .hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.withCacheableSqmInterpretation(ConcreteSqmSelectQueryPlan.java:305)
at .hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.performList(ConcreteSqmSelectQueryPlan.java:246)
at .hibernate.query.sqm.internal.QuerySqmImpl.doList(QuerySqmImpl.java:546)
at .hibernate.query.spi.AbstractSelectionQuery.list(AbstractSelectionQuery.java:363)
at .hibernate.query.sqm.internal.QuerySqmImpl.list(QuerySqmImpl.java:1032)
at .hibernate.query.Query.getResultList(Query.java:94)
at .springframework.data.jpa.repository.query.JpaQueryExecution$CollectionExecution.doExecute(JpaQueryExecution.java:127)
at .springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:90)
at .springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:148)
at .springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:136)
at .springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:136)
at .springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:120)
at .springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:164)
at .springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:143)
at .springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at .springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:77)
at .springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at .springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123)
at .springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388)
at .springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
at .springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at .springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137)
... 96 more
edit: here is the code called from intervenantService.toEntity()
@Override
public Intervenant toEntity(IntervenantDto intervenantDto) {
if ( intervenantDto == null ) {
return null;
}
Intervenant intervenant = new Intervenant();
intervenant.setId( intervenantDto.id() );
intervenant.setIdnum( intervenantDto.idnum() );
intervenant.setIdentite( intervenantDto.identite() );
return intervenant;
}
I'm having trouble updating an entity (Formation) in a ManyToMany relationship with another entity (Intervenant).
My integration test fails because of the following error:
.springframework.dao.InvalidDataAccessApiUsageException: detached
entity passed to persist: fr.foo.bar.model.Intervenant
The problem is that, according to the stacktrace, the exception is caught on a method that just retrieves data, so no call to save()
is made. I don't understand why this error appears.
Here is my test:
@BeforeEach
void setUp() {
Intervenant newIntervenant = new Intervenant();
newIntervenant.setIdnum("IDNUM_NEW");
newIntervenant.setIdentite("New Intervenant");
Formation formation = new Formation();
formation.setLibelle("Sample Formation");
formation.setCodeApogee("APOGEE123");
formation.setCodeVet("VET789");
formation.setIntervenants(Set.of(newIntervenant));
formationDto = formationMapper.toDto(formation);
}
@Test
@Transactional
void whenUpdateFormation_thenIntervenantsAreUpdated() throws Exception {
FormationDto createdFormationDto = formationService.create(formationDto);
formationRepository.flush();
IntervenantDto newIntervenantDto = new IntervenantDto(null, "New IDNUM", "New Name");
createdFormationDto.intervenants().add(newIntervenantDto);
Intervenant existingIntervenant = new Intervenant();
existingIntervenant.setIdnum("IDNUM_EXISTING");
existingIntervenant.setIdentite("Existing Intervenant");
existingIntervenant = intervenantRepository.saveAndFlush(existingIntervenant);
existingIntervenant = intervenantRepository.findById(existingIntervenant.getId()).orElseThrow();
createdFormationDto.intervenants().add(intervenantMapper.toDto(existingIntervenant));
FormationDto updatedFormationDto = formationService.update(createdFormationDto.id(), createdFormationDto); // detached entity passed to persist: fr.foo.bar.model.Intervenant
[...]
}
The exception occurs in the last line of the test, the one calling update()
Here is the update()
method:
@Override
@Transactional
public FormationDto update(Long id, FormationDto formationDto) throws Exception {
Formation formation = formationRepository
.findById(id)
.orElseThrow(() -> new EntityNotFoundException(String.format("La formation %d n'existe pas", id)));
Set<Intervenant> updatedIntervenants = new HashSet<>();
for (IntervenantDto intervenantDto : formationDto.intervenants()) {
if (intervenantDto.id() == null) {
IntervenantDto createdDto = intervenantService.create(intervenantDto);
updatedIntervenants.add(intervenantService.toEntity(createdDto));
} else {
Intervenant existing = intervenantService.getEntity(intervenantDto.id());
updatedIntervenants.add(existing);
}
}
formation.setIntervenants(updatedIntervenants);
Collection<FormationEtudiant> existingEtudiants = formationEtudiantService.findAllByFormation(formation); // detached entity passed to persist: fr.foo.bar.model.Intervenant
[...]
}
And the exception occurs when the formationEtudiantService.findAllByFormation()
method is called, as shown below:
@Override
public List<FormationEtudiant> findAllByFormation(Formation formation) {
return formationEtudiantRepository.findAllByFormation(formation);
}
And here is the repository:
public interface FormationEtudiantRepository extends JpaRepository<FormationEtudiant, FormationEtudiantId> {
List<FormationEtudiant> findAllByFormation(Formation formation);
}
Here's an extract from the Formation entity:
@Entity(name = "FormationEntity")
@Table(name = "formation")
public class Formation implements Identifiable<Long> {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", columnDefinition = "INT UNSIGNED not null")
private Long id;
@Column(name = "libelle", length = 100)
private String libelle;
@Column(name = "code_apogee", length = 100)
private String codeApogee;
@Column(name = "code_vet", length = 100)
private String codeVet;
@ManyToMany(cascade = {CascadeType.MERGE, CascadeType.PERSIST})
@JoinTable(name = "formation_intervenant",
joinColumns = @JoinColumn(name = "id_formation"),
inverseJoinColumns = @JoinColumn(name = "id_intervenant"))
private Set<Intervenant> intervenants = new LinkedHashSet<>();
public Set<Intervenant> getIntervenants() {
return intervenants;
}
public void setIntervenants(Set<Intervenant> intervenants) {
this.intervenants = intervenants;
}
}
And finally, the Intervenant entity:
@Entity(name = "IntervenantEntity")
@Table(name = "intervenant")
public class Intervenant implements Identifiable<Long> {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", columnDefinition = "INT UNSIGNED not null")
private Long id;
@Column(name = "idnum", nullable = false, length = 50)
private String idnum;
@Column(name = "identite", length = 100)
private String identite;
@Override
public Long getId() {
return id;
}
@ManyToMany(mappedBy = "intervenants")
private Set<Formation> formations = new LinkedHashSet<>();
public void setId(Long id) {
this.id = id;
}
public String getIdnum() {
return idnum;
}
public void setIdnum(String idnum) {
this.idnum = idnum;
}
public String getIdentite() {
return identite;
}
public void setIdentite(String identite) {
this.identite = identite;
}
public Set<Formation> getFormations() {
return formations;
}
public void setFormations(Set<Formation> formations) {
this.formations = formations;
}
}
It really drives me crazy... Can you help me ?
Edit: here's the stacktrace
.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist: fr.foo.bar.model.Intervenant
at .springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:290)
at .springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:233)
at .springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:550)
at .springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)
at .springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242)
at .springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:152)
at .springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at .springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:134)
at .springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at .springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
at .springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at .springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:218)
at jdk.proxy2/jdk.proxy2.$Proxy167.findAllByFormation(Unknown Source)
at fr.foo.bar.services.application.FormationEtudiantServiceImpl.findAllByFormation(FormationEtudiantServiceImpl.java:26)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
at java.base/java.lang.reflect.Method.invoke(Method.java:577)
at .springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343)
at .springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:699)
at fr.foo.bar.services.application.FormationEtudiantServiceImpl$$SpringCGLIB$$0.findAllByFormation(<generated>)
at fr.foo.bar.services.application.FormationServiceImpl.update(FormationServiceImpl.java:226)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
at java.base/java.lang.reflect.Method.invoke(Method.java:577)
at .springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343)
at .springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196)
at .springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at .springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:752)
at .springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123)
at .springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388)
at .springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
at .springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at .springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:752)
at .springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:703)
at fr.foo.bar.services.application.FormationServiceImpl$$SpringCGLIB$$0.update(<generated>)
at fr.foo.bar.FormationControllerIntegrationTests.whenUpdateFormation_thenIntervenantsAreUpdated(FormationControllerIntegrationTests.java:138)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
at java.base/java.lang.reflect.Method.invoke(Method.java:577)
at .junit.platformmons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:727)
at .junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
at .junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
at .junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:156)
at .junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:147)
at .junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:86)
at .junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103)
at .junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93)
at .junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
at .junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
at .junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
at .junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
at .junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:92)
at .junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:86)
at .junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:217)
at .junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at .junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:213)
at .junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:138)
at .junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68)
at .junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151)
at .junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at .junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at .junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at .junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at .junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at .junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at .junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at .junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
at .junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
at .junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at .junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at .junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at .junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at .junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at .junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at .junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at .junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
at .junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
at .junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at .junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at .junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at .junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at .junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at .junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at .junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at .junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
at .junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at .junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
at .junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:147)
at .junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:127)
at .junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:90)
at .junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:55)
at .junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:102)
at .junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:54)
at .junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
at .junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
at .junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
at .junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53)
at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:57)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:232)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)
Caused by: .hibernate.PersistentObjectException: detached entity passed to persist: fr.foo.bar.model.Intervenant
at .hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:121)
at .hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:118)
at .hibernate.internal.SessionImpl.persistOnFlush(SessionImpl.java:799)
at .hibernate.engine.spi.CascadingActions$8.cascade(CascadingActions.java:342)
at .hibernate.engine.spi.CascadingActions$8.cascade(CascadingActions.java:332)
at .hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:511)
at .hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:432)
at .hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:218)
at .hibernate.engine.internal.Cascade.cascadeCollectionElements(Cascade.java:545)
at .hibernate.engine.internal.Cascade.cascadeCollection(Cascade.java:475)
at .hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:435)
at .hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:218)
at .hibernate.engine.internal.Cascade.cascade(Cascade.java:151)
at .hibernate.event.internal.AbstractFlushingEventListener.cascadeOnFlush(AbstractFlushingEventListener.java:155)
at .hibernate.event.internal.AbstractFlushingEventListener.prepareEntityFlushes(AbstractFlushingEventListener.java:145)
at .hibernate.event.internal.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:79)
at .hibernate.event.internal.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:48)
at .hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:107)
at .hibernate.internal.SessionImpl.autoFlushIfRequired(SessionImpl.java:1385)
at .hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.lambda$new$0(ConcreteSqmSelectQueryPlan.java:100)
at .hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.withCacheableSqmInterpretation(ConcreteSqmSelectQueryPlan.java:305)
at .hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.performList(ConcreteSqmSelectQueryPlan.java:246)
at .hibernate.query.sqm.internal.QuerySqmImpl.doList(QuerySqmImpl.java:546)
at .hibernate.query.spi.AbstractSelectionQuery.list(AbstractSelectionQuery.java:363)
at .hibernate.query.sqm.internal.QuerySqmImpl.list(QuerySqmImpl.java:1032)
at .hibernate.query.Query.getResultList(Query.java:94)
at .springframework.data.jpa.repository.query.JpaQueryExecution$CollectionExecution.doExecute(JpaQueryExecution.java:127)
at .springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:90)
at .springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:148)
at .springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:136)
at .springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:136)
at .springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:120)
at .springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:164)
at .springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:143)
at .springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at .springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:77)
at .springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at .springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123)
at .springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388)
at .springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
at .springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at .springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137)
... 96 more
edit: here is the code called from intervenantService.toEntity()
@Override
public Intervenant toEntity(IntervenantDto intervenantDto) {
if ( intervenantDto == null ) {
return null;
}
Intervenant intervenant = new Intervenant();
intervenant.setId( intervenantDto.id() );
intervenant.setIdnum( intervenantDto.idnum() );
intervenant.setIdentite( intervenantDto.identite() );
return intervenant;
}
Share
Improve this question
edited Nov 25, 2024 at 8:40
Harkonnen
asked Nov 19, 2024 at 14:52
HarkonnenHarkonnen
8132 gold badges9 silver badges21 bronze badges
5
|
1 Answer
Reset to default 0Search methods trigger saving dirty (technical term) objects to db. Because those objects can be potentially result of search.
Second part of your puzzle is the fact that your Intervenant
is not saved.
You should explicitly attach entity to session (call persist
) in case you want to use it.
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1742421626a4440797.html
intervenantService.toEntity(createdDto)
. Does it setid
? – talex Commented Nov 20, 2024 at 14:23