We have updated from Spring Boot 3.3.6 to version 3.4.0. Now our integrationtests (Annotation @SpringBootTest) with Hibernate/JPA interactions (we're using spring-data-jpa) in it fail suddenly.
@Test
void retrieveAllFahrzeugbewegungenByMuster() throws Exception {
// Given
var musterEntity = MusterMetadatenGenerator.createDefaultMuster();
var musterEntitySaved = musterMetadatenRepository.saveAndFlush(musterEntity); // crashes here
...
}
The creation of the @Entity in createDefaultMuster() is essentially this here:
static <E extends BaseEntity> E createDefaultValues(
@NotNull final E source,
@NotNull final LocalDate datumAb,
@NotNull final LocalDate datumBis
) {
final Instant now = Instant.now();
source.setId(UUID.randomUUID());
source.setDatumAb(datumAb);
source.setDatumBis(datumBis);
source.setBearbeitetAm(now);
source.setErstelltAm(now);
return source;
}
The @Entity itself looks as below:
@Data
@NoArgsConstructor
@AllArgsConstructor
@MappedSuperclass
public class BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(updatable = false, nullable = false)
protected UUID id;
...
The exception is:
org.springframework.orm.ObjectOptimisticLockingFailureException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [de.muster.jpa.entity.MusterMetadaten#40add996-a1f9-43f7-a157-cf5692e5ea65] Caused by: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [de.muster.jpa.entity.MusterMetadaten#40add996-a1f9-43f7-a157-cf5692e5ea65]
With Spring Boot 3.4.0 comes Hibernate 6.6.x, before it was Hibernate 6.5.x. Is there maybe a breaking change?
Can you check the new entity's @Id property?
Spring Boot 3.4.0 was upgraded to Hibernate 6.6.2.Final #43100
There are changes of DefaultMergeEventListener.
Hibernate 6.5.3.Final (Boot 3.3.5)
if ( result == null ) {
//TODO: we should throw an exception if we really *know* for sure
// that this is a detached instance, rather than just assuming
//throw new StaleObjectStateException(entityName, id);
Hibernate 6.6.2.Final (Boot 3.4.0)
if ( result == null ) {
LOG.trace( "Detached instance not found in database" );
// we got here because we assumed that an instance
// with an assigned id and no version was detached,
// when it was really transient (or deleted)
final Boolean knownTransient = persister.isTransient( entity, source );
if ( knownTransient == Boolean.FALSE ) {
// we know for sure it's detached (generated id
// or a version property), and so the instance
// must have been deleted by another transaction
throw new StaleObjectStateException( entityName, id );
So you may meet message of "Row was already updated or deleted by another transaction".
This change can follow below issue:
If you want to fix the test code, first show your code of MusterMetadatenGenerator.createDefaultMuster().
You are creating an entity that appears with a given ID (you set the id in you createDefaultMuster() method), but the field is annotated with @GeneratedValue. However, starting from Hibernate 6.6 (the version used since Spring Boot 3.4.0), this is no longer allowed.
source.setId(UUID.randomUUID());
If the id field is provided, Hibernate assumes the entity already exists in the database. If no matching row is found, Hibernate interprets this as the entity having been deleted by another transaction, resulting in an OptimisticLockException.
Since your id field is configured as a generated value, it must remain unset for new entities to allow Hibernate to correctly handle its generation. To prevent errors, ensure the id field is reset to null before saving a new entity.
See also: https://docs.jboss.org/hibernate/orm/6.6/migration-guide/migration-guide.html#merge-versioned-deleted
Previously, merging a detached entity resulted in a SQL
insertwhenever there was no matching row in the database (for example, if the object had been deleted in another transaction). This behavior was unexpected and violated the rules of optimistic locking.An
OptimisticLockExceptionis now thrown when it is possible to determine that an entity is definitely detached, but there is no matching row. For this determination to be possible, the entity must have either:a generated
@Idfield, ora non-primitive
@Versionfield.For entities which have neither, it’s impossible to distinguish a new instance from a deleted detached instance, and there is no change from the previous behavior.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With