最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

spring data jpa - Hibernate throws 'ObjectOptimisticLockingFailureException' when saving a @OneToOne with @MapsI

programmeradmin0浏览0评论

I recently updated my project from Hibernate 5.4 to Hibernate 6.6, and now I’m encountering an error that wasn’t happening before.

I have two entities: ProductEntity and ProductProfitabilityEntity, which are part of the same model and share the same ID. These entities are replicated from another microservice, meaning they arrive fully populated, including their IDs. Because of this, there is no @GeneratedValue annotation on their ID fields, as the IDs are assigned externally and must remain unchanged.

  • ProductEntity has a one-to-one relationship with ProductProfitabilityEntity. ProductProfitabilityEntity uses @MapsId, ensuring its ID is always the same as the ProductEntity ID. Both entities reference each other:

    • ProductEntity has a profitability field referencing ProductProfitabilityEntity.
    • ProductProfitabilityEntity has a product field referencing ProductEntity.

ProductEntity:

package br.triersistemas.cloud.platform.desktop.entity.product;

import br.triersistemas.cloud.database.entity.AuditingExternalEntity;
import br.triersistemas.cloud.platform.desktop.entity.productprofitability.ProductProfitabilityEntity;
import jakarta.persistence.*;
import jakarta.validation.Valid;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.SuperBuilder;

import java.util.UUID;

@AllArgsConstructor
@NoArgsConstructor
@SuperBuilder
@Entity
@Table(name = "product")
public class ProductEntity extends AuditingExternalEntity {

    @Getter
    @Setter
    @Id
    @Column(name = "id")
    private UUID id;

    @Getter
    @Setter
    @Column(name = "product_code")
    private Long productCode;

    // Contains one ProductProfitabilityEntity
    @Valid
    @OneToOne(mappedBy = "product", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
    private ProductProfitabilityEntity profitability;
}

ProductProfitabilityEntity:

package br.triersistemas.cloud.platform.desktop.entity.productprofitability;

import br.triersistemas.cloud.database.entity.AuditingEntity;
import br.triersistemas.cloud.platform.desktop.entity.product.ProductEntity;
import jakarta.persistence.*;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.SuperBuilder;

import java.util.UUID;

@AllArgsConstructor
@NoArgsConstructor
@SuperBuilder
@Entity
@Table(name = "product_profitability")
public class ProductProfitabilityEntity extends AuditingEntity {

    @NotNull(message = "Unique identifier is required.")
    @Getter
    @Setter
    @Id
    @Column(name = "id")
    private UUID id;

    // Refers to the container ProductEntity
    // Using OneToOne with MapsId, so: ProductProfitabilityEntity.id = ProductEntity.id
    @Valid
    @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "id")
    @MapsId
    private ProductEntity product;
}

The Issue

When I try to save ProductEntity, I get the following error:

.springframework.orm.ObjectOptimisticLockingFailureException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [br.triersistemas.cloud.platform.desktop.entity.productprofitabilityentity.ProductProfitabilityEntity#fb01b3e0-f12e-4380-b82a-2f2882fadaf1]

My Understanding of the Problem:

Hibernate sees that ProductProfitabilityEntity already has an ID and tries to fetch it from the database to determine whether it should perform an update or an insert. However, since the entity does not exist yet, it likely throws this exception.

I didn't expect that, because there is no @GeneratedValue and the entity does not exist, Hibernate would insert it with the provided ID instead of failing.

If I manually set the id of ProductProfitabilityEntity to null before saving, everything works fine—Hibernate properly inserts it and maps the relationship. However, since my API receives fully populated replicated data (which used to work before), setting the ID to null is not an ideal solution.

What Changed in Hibernate 6.6?

Before the update, this issue didn’t occur. I am trying to understand

  1. What has changed in Hibernate 6.6 that is now causing this behavior?
  2. Is there a better solution than manually setting ProductProfitabilityEntity.id = null before saving ProductEntity?

I recently updated my project from Hibernate 5.4 to Hibernate 6.6, and now I’m encountering an error that wasn’t happening before.

I have two entities: ProductEntity and ProductProfitabilityEntity, which are part of the same model and share the same ID. These entities are replicated from another microservice, meaning they arrive fully populated, including their IDs. Because of this, there is no @GeneratedValue annotation on their ID fields, as the IDs are assigned externally and must remain unchanged.

  • ProductEntity has a one-to-one relationship with ProductProfitabilityEntity. ProductProfitabilityEntity uses @MapsId, ensuring its ID is always the same as the ProductEntity ID. Both entities reference each other:

    • ProductEntity has a profitability field referencing ProductProfitabilityEntity.
    • ProductProfitabilityEntity has a product field referencing ProductEntity.

ProductEntity:

package br.triersistemas.cloud.platform.desktop.entity.product;

import br.triersistemas.cloud.database.entity.AuditingExternalEntity;
import br.triersistemas.cloud.platform.desktop.entity.productprofitability.ProductProfitabilityEntity;
import jakarta.persistence.*;
import jakarta.validation.Valid;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.SuperBuilder;

import java.util.UUID;

@AllArgsConstructor
@NoArgsConstructor
@SuperBuilder
@Entity
@Table(name = "product")
public class ProductEntity extends AuditingExternalEntity {

    @Getter
    @Setter
    @Id
    @Column(name = "id")
    private UUID id;

    @Getter
    @Setter
    @Column(name = "product_code")
    private Long productCode;

    // Contains one ProductProfitabilityEntity
    @Valid
    @OneToOne(mappedBy = "product", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
    private ProductProfitabilityEntity profitability;
}

ProductProfitabilityEntity:

package br.triersistemas.cloud.platform.desktop.entity.productprofitability;

import br.triersistemas.cloud.database.entity.AuditingEntity;
import br.triersistemas.cloud.platform.desktop.entity.product.ProductEntity;
import jakarta.persistence.*;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.SuperBuilder;

import java.util.UUID;

@AllArgsConstructor
@NoArgsConstructor
@SuperBuilder
@Entity
@Table(name = "product_profitability")
public class ProductProfitabilityEntity extends AuditingEntity {

    @NotNull(message = "Unique identifier is required.")
    @Getter
    @Setter
    @Id
    @Column(name = "id")
    private UUID id;

    // Refers to the container ProductEntity
    // Using OneToOne with MapsId, so: ProductProfitabilityEntity.id = ProductEntity.id
    @Valid
    @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "id")
    @MapsId
    private ProductEntity product;
}

The Issue

When I try to save ProductEntity, I get the following error:

.springframework.orm.ObjectOptimisticLockingFailureException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [br.triersistemas.cloud.platform.desktop.entity.productprofitabilityentity.ProductProfitabilityEntity#fb01b3e0-f12e-4380-b82a-2f2882fadaf1]

My Understanding of the Problem:

Hibernate sees that ProductProfitabilityEntity already has an ID and tries to fetch it from the database to determine whether it should perform an update or an insert. However, since the entity does not exist yet, it likely throws this exception.

I didn't expect that, because there is no @GeneratedValue and the entity does not exist, Hibernate would insert it with the provided ID instead of failing.

If I manually set the id of ProductProfitabilityEntity to null before saving, everything works fine—Hibernate properly inserts it and maps the relationship. However, since my API receives fully populated replicated data (which used to work before), setting the ID to null is not an ideal solution.

What Changed in Hibernate 6.6?

Before the update, this issue didn’t occur. I am trying to understand

  1. What has changed in Hibernate 6.6 that is now causing this behavior?
  2. Is there a better solution than manually setting ProductProfitabilityEntity.id = null before saving ProductEntity?
Share Improve this question edited Mar 11 at 14:17 Nikolas Trapp asked Mar 11 at 14:09 Nikolas TrappNikolas Trapp 899 bronze badges 1
  • Fot to mention that, if i persist the ProductProfitabilityEntity before saving the Product, everything works fine too. – Nikolas Trapp Commented Mar 11 at 14:10
Add a comment  | 

1 Answer 1

Reset to default 0

Solved by removing @MapsId from ProductProfitabilityEntity.

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论