Reputation: 21
I'm reading a book "spring-boot up and running". And I have the following problem, I don't understand how to solve it.
org.springframework.orm.ObjectOptimisticLockingFailureException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.example.mysqlplanefinder.models.Aircraft#34]
Aircraft.java
package com.example.mysqlplanefinder.models;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import java.time.Instant;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Aircraft {
@Id
@GeneratedValue
private Long id;
private String callsign, squawk, reg, flightno, route, type, category;
private int altitude, heading, speed;
@JsonProperty("vert_rate")
private int vertRate;
@JsonProperty("selected_altitude")
private int selectedAltitude;
private double lat, lon, barometer;
@JsonProperty("polar_distance")
private double polarDistance;
@JsonProperty("polar_bearing")
private double polarBearing;
@JsonProperty("is_adsb")
private boolean isADSB;
@JsonProperty("is_on_ground")
private boolean isOnGround;
@JsonProperty("last_seen_time")
private Instant lastSeenTime;
@JsonProperty("pos_update_time")
private Instant posUpdateTime;
@JsonProperty("bds40_seen_time")
private Instant bds40SeenTime;
}
AircraftRepository.java
package com.example.mysqlplanefinder.repositories;
import com.example.mysqlplanefinder.models.Aircraft;
import org.springframework.data.repository.CrudRepository;
public interface AircraftRepository extends CrudRepository<Aircraft, Long> { }
PlanePoller.java
package com.example.mysqlplanefinder;
import com.example.mysqlplanefinder.models.Aircraft;
import com.example.mysqlplanefinder.repositories.AircraftRepository;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;
@EnableScheduling
@Component
@RequiredArgsConstructor
class PlanePoller {
@NonNull
private final AircraftRepository repository;
private WebClient client =
WebClient.create("http://localhost:7634/aircraft");
@Scheduled(fixedRate = 1000)
private void pollPlanes() {
repository.deleteAll();
client.get()
.retrieve()
.bodyToFlux(Aircraft.class)
.filter(plane -> !plane.getReg().isEmpty())
.toStream()
.forEach(repository::save);
repository.findAll().forEach(System.out::println);
}
}
I've tried to delete method .deleteAll()
and add Versions how was recommended Ai, but nothing helped.
Upvotes: 1
Views: 278
Reputation: 314
You are creating an entity that appears with a given ID (fetched from the web), 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.
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.
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, ora 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.
Upvotes: 1
Reputation: 21
The solution turned out to be too simple. What happened: The author of the book in the code in the Aircraft model made the id field as autogenerated (in my case, this was the problem, perhaps it was supposed to be like this, but I literally copied it from the book). After studying the forum a little, I came across a similar situation, where a person wrote that if the id comes from the other side, then a conflict of this type occurs. In particular, since my method sends a request to an external API every second, then the same object of my plane will be in different threads, and since hibernate "marks" what needs to be done with this object, it gives this ill-fated error. My solution was to add a field with the @Version annotation and remove @GeneratedValue from id .
Aircraft.java
@Id
private Long id;
@Version
private Long version;
Upvotes: 1