Spring Boot: org.hibernate.StaleObjectStateException – Zeile wurde durch eine andere Transaktion aktualisiert oder gelösJava

Java-Forum
Guest
 Spring Boot: org.hibernate.StaleObjectStateException – Zeile wurde durch eine andere Transaktion aktualisiert oder gelös

Post by Guest »

Beim Versuch, eine FlightInstance-Entität in meiner Spring Boot-Anwendung zu speichern, stoße ich auf eine ObjectOptimisticLockingFailureException, die durch eine StaleObjectStateException verursacht wurde. Der vollständige Stack-Trace lautet wie folgt:

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
Mein Setup
Ich verwende Spring Boot mit den folgenden Abhängigkeiten:

Code: Select all

spring-boot-starter-data-jpa
hibernate-core
Die Entität FlightInstance erbt von einer übergeordneten Klasse namens FlyWellModel. Hier ist der relevante Code:
Ü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();
}
}
Untergeordnete Klasse (FlightInstance): Die FlightInstance-Entität verfügt über ein publicId-Feld, das mit a gefüllt wird @PrePersist-Annotation in der onCreate-Methode aus der Basisklasse FlyWellModel

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;
}

}
Das Problem
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;
}
Beobachtungen
  • @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.
Was ich versucht habe Bisher
  • 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.
Diese Änderungen ließen mich glauben, dass das Problem möglicherweise nicht direkt mit dem zusammenhängt publicId-Feld oder das @PrePersist-Logik. Jetzt bin ich noch verwirrter, da diese Änderungen den Fehler nicht behoben haben. Was könnte die zugrunde liegende Ursache sein und wie kann ich das Problem lösen?
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
jegliche Anleitung oder Einblicke auftritt. Wir würden uns sehr freuen! Danke!

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post