Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Boot 3.4.0 lets integration tests with JPA/Hibernate fail

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?

like image 578
kladderradatsch Avatar asked Nov 21 '25 00:11

kladderradatsch


2 Answers

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:

  • https://hibernate.atlassian.net/browse/HHH-1661

If you want to fix the test code, first show your code of MusterMetadatenGenerator.createDefaultMuster().

like image 174
Namo Avatar answered Nov 23 '25 13:11

Namo


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 insert whenever 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 OptimisticLockException is 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 @Id field, or

a non-primitive @Version field.

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.

like image 40
Andy Zhang Avatar answered Nov 23 '25 15:11

Andy Zhang



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!