Code: Select all
rg.springframework.orm.ObjectOptimisticLockingFailureException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [app.bola.flywell.data.model.flight.FlightInstance#16f60701-d478-45ca-bbd4-59fc53aa4ca3]
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:325)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:244)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:560)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:343)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:160)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:165)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:223)
at jdk.proxy2/jdk.proxy2.$Proxy193.save(Unknown Source)
at app.bola.flywell.services.flightservice.FlyWellFlightInstanceService.createNew(FlyWellFlightInstanceService.java:52)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:359)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:380)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:727)
at app.bola.flywell.services.flightservice.FlyWellFlightInstanceService$$SpringCGLIB$$0.createNew()
at app.bola.flywell.services.flightservice.FlightInstanceServiceTest.createNewFlightInstance_NewFlightIsCreatedTest(FlightInstanceServiceTest.java:41)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
Caused by: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [ a p p . b o l a . f l y w e l l . d a t a . m o d e l . f l i g h t . F l i g h t I n s t a n c e # 1 6 f 6 0 7 0 1 - d 4 7 8 - 4 5 c a - b b d 4 - 5 9 f c 5 3 a a 4 c a 3 ] < b r / > a t o r g . h i b e r n a t e . e v e n t . i n t e r n a l . D e f a u l t M e r g e E v e n t L i s t e n e r . e n t i t y I s D e t a c h e d ( D e f a u l t M e r g e E v e n t L i s t e n e r . j a v a : 4 2 6 ) < b r / > a t o r g . h i b e r n a t e . e v e n t . i n t e r n a l . D e f a u l t M e r g e E v e n t L i s t e n e r . m e r g e ( D e f a u l t M e r g e E v e n t L i s t e n e r . j a v a : 2 1 4 ) < b r / > a t o r g . h i b e r n a t e . e v e n t . i n t e r n a l . D e f a u l t M e r g e E v e n t L i s t e n e r . d o M e r g e ( D e f a u l t M e r g e E v e n t L i s t e n e r . j a v a : 1 5 2 ) < b r / > a t o r g . h i b e r n a t e . e v e n t . i n t e r n a l . D e f a u l t M e r g e E v e n t L i s t e n e r . o n M e r g e ( D e f a u l t M e r g e E v e n t L i s t e n e r . j a v a : 1 3 6 ) < b r / > a t o r g . h i b e r n a t e . e v e n t . s e r v i c e . i n t e r n a l . E v e n t L i s t e n e r G r o u p I m p l . f i r e EventOnEachListener(EventListenerGroupImpl.java:138)
at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:875)
at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:846)
at org.hibernate.engine.spi.CascadingActions$6.cascade(CascadingActions.java:258)
at org.hibernate.engine.spi.CascadingActions$6.cascade(CascadingActions.java:248)
at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:570)
at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:492)
at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:253)
at org.hibernate.engine.internal.Cascade.cascadeCollectionElements(Cascade.java:604)
at org.hibernate.engine.internal.Cascade.cascadeCollection(Cascade.java:534)
at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:495)
at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:253)
at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:192)
at org.hibernate.event.internal.DefaultMergeEventListener.cascadeOnMerge(DefaultMergeEventListener.java:667)
at org.hibernate.event.internal.DefaultMergeEventListener.entityIsPersistent(DefaultMergeEventListener.java:285)
at org.hibernate.event.internal.DefaultMergeEventListener.merge(DefaultMergeEventListener.java:220)
at org.hibernate.event.internal.DefaultMergeEventListener.doMerge(DefaultMergeEventListener.java:152)
at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:136)
at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:89)
at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:127)
at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:854)
at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:840)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:320)
at jdk.proxy2/jdk.proxy2.$Proxy181.merge(Unknown Source)
at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:630)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:359)
at org.springframework.data.repository.core.support.RepositoryMethodInvoker$RepositoryFragmentMethodInvoker.lambda$new$0(RepositoryMethodInvoker.java:277)
at org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:170)
at org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:158)
at org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:515)
at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:284)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:752)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:174)
at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:149)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:69)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:380)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:138)
... 20 more
Ich verwende Spring Boot mit den folgenden Abhängigkeiten:
Code: Select all
spring-boot-starter-data-jpa
hibernate-core
Übergeordnete Klasse (FlyWellModel): Die Basisklasse für alle Modelle im System
Code: Select all
public class FlyWellModel {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private String id;
@Column(nullable = false, unique = true)
private String publicId;
@PrePersist
protected void onCreate() {
if (publicId == null || publicId.isEmpty()) {
publicId = UUID.randomUUID().toString();
}
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("id", id)
.add("publicId", publicId)
.toString();
}
}
Code: Select all
@Entity
@Table(name = "flight_instance")
public class FlightInstance extends FlyWellModel {
@Column(nullable = false)
@FlightNumberSequence(name = "flight_number_sequence", startWith = 1000, incrementBy = 3) , wodurch die Entität geändert wird, bevor die Transaktion abgeschlossen ist. Ich vermute, dass diese Änderung dazu führen könnte, dass Hibernate die Entität als „schmutzig“ behandelt, was zu der Ausnahme führt.
Interessanterweise funktioniert ein ähnliches Setup perfekt für den Flight[/b] Entität, die wie folgt aufgebaut ist:
[code]@Entity
public class Flight extends FlyWellModel {
private long duration;
private String arrivalCity;
private String departureCity;
private String displayImage;
@OneToOne(cascade = ALL)
private Airport departureAirport;
@OneToOne(cascade = ALL)
private Airport arrivalAirport;
@OneToMany(mappedBy = "flight", cascade = ALL, fetch = EAGER)
private Set instances = new LinkedHashSet();
public FlightInstance addFlightInstance(FlightInstance newInstance){
newInstance.setFlight(this);
instances.add(newInstance);
return newInstance;
}
}
Die Ausnahme tritt auf, wenn die Speichermethode für das FlightInstanceRepository aufgerufen wird die Service-Schicht:
Service-Methode:
Code: Select all
@Service
public class FlyWellFlightInstanceService {
@Autowired
private FlightInstanceRepository flightInstanceRepository;
@Override
@Transactional
@Retryable(retryFor = ObjectOptimisticLockingFailureException.class, maxAttempts = 5)
public FlightInstanceResponse createNew(FlightInstanceRequest request) {
Flight flight = flightRepository.findByPublicId(request.getFlightId())
.orElseThrow(() -> new EntityNotFoundException(Constants.ENTITY_NOT_FOUND.formatted("Flight")));
FlightInstance mappedInstance = mapper.map(request, FlightInstance.class);
mappedInstance.setFlight(flight);
mappedInstance.setStatus(SCHEDULED);
mappedInstance.setFlightSeat(new ArrayList());
FlightInstance savedInstance = flight.addFlightInstance(mappedInstance);
flightRepository.save(flight);
return toResponse(savedInstance);
}
private FlightInstanceResponse toResponse(FlightInstance instance) {
FlightInstanceResponse response = mapper.map(instance, FlightInstanceResponse.class);
response.setArrivalAirportName(instance.getFlight().getArrivalAirport().getName());
response.setDepartureAirportName(instance.getFlight().getDepartureAirport().getName());
return response;
}
- @PrePersist auf FlightInstance: The Das publicId-Feld wird zum Zeitpunkt der Persistenz ausgefüllt, wodurch die Entität während des Speichervorgangs geändert wird.
- Thread-Interferenz: Ich vermute, dass mehrere Threads im Spiel sind. Während ein Thread die Flight-Entität ändert, ändert möglicherweise ein anderer Thread gleichzeitig die zugehörige FlightInstance-Entität, was zu einem Konflikt führt.
- Ich vermute, dass das Problem mit dem publicId-Feld zusammenhängt, das von der FlyWellModel-Klasse geerbt wurde, insbesondere mit dem @PrePersist-Hook. Zum weiteren Debuggen habe ich mehrere Anpassungen vorgenommen:
- FlightInstance-Modell getrennt: Ich habe die Beziehung zwischen dem FlightInstance-Modell und FlyWellModel vollständig entfernt, um dies sicherzustellen Das FlightInstance-Modell erbt nicht mehr von FlyWellModel und verfügt über kein publicId-Feld. Es ist jedoch immer noch derselbe Fehler aufgetreten.
- Entität umbenannt: Ich vermute, dass das Problem auf widersprüchliche Namen zwischen Flight< zurückzuführen sein könnte /strong> und FlightInstance, ich habe FlightInstance in Instance umbenannt, aber es trat immer noch der gleiche Fehler auf.
- @PrePersist deaktiviert : Ich habe das auskommentiert onCreate()-Methode mit Annotation @PrePersist im FlyWell-Modell und ich habe die Methode auf das Flight-Modell übertragen, aber das Problem blieb bestehen.
Meine Frage
- Verursacht die @PrePersist-Änderung am Feld publicId die StaleObjectStateException? Wenn ja, wie geht man damit am besten um?
- Könnten mehrere Threads gleichzeitig die FlightInstance-Entität ändern, und wie kann ich dies in einem Spring Boot + Hibernate-Setup überprüfen oder entschärfen?< /li>
Warum funktioniert die Flight-Entität ohne Probleme, während die FlightInstance-Entität diese Ausnahme unter ähnlichen Bedingungen auslöst?
4. Wie kann ich dieses Problem beheben? damit die Die FlightInstance-Entität wird korrekt gespeichert, ohne dass StaleObjectStateException