0

I have an error during an update transaction on my database using JpaRepository. I saw that there are many questions about this same error here but none of the solutions mentioned seem to solve my case. This is my main method:

 public View update(Long productCode, Long customerCode, UpdateForm form) {

        var entityToUpdate = repository.findByProductCodeAndCustomerCode(productCode, customerCode);

        var updatedObject = new Builder(entityToUpdate).build(form);
        var savedObject = repository.save(updatedObject); //error thrown here

        return converter.convertToView(savedObject);
    }

This is the Builder class implementation that appears above:

    public class Builder {

    private final Purchase entityToUpdate;

    public Builder(Optional<Purchase> entityToUpdate) {
        this.entityToUpdate = entityToUpdate.isPresent() ? entityToUpdate.get() : null;
    }

    public Purchase build(PurchaseUpdateForm form) {

        this.entityToUpdate.setDiscount(form.getDiscount());
        this.entityToUpdate.getId().setPurchaseBoxQuantity(form.getPurchaseBoxQuantity());

        return entityToUpdate;
    }
}

I tried to put an @Transactional in the update() and build() methods but the error persists. I also tried using saveAndFlush but nothing changed. Full log message:

Optimistic locking failed; nested exception is caused by: org.hibernate.StaleObjectStateException: 
Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): 
[PurchasePK@bb7039e8]

# Additional Information - Domain

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "TB_PURCHASE")
public class Purchase {
    
    @EmbeddedId
    private PurchasePK id;
    
    @NotNull
    @Column(name = "DISCOUNT")
    private BigDecimal discount;
}

@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of = { "product", "purchaseBoxQuantity" })
@Embeddable
public class PurchasePK {
    
    @NotNull
    @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinColumn(name = "CD_PRODUCT")
    @JoinColumn(name = "CD_CUSTOMER")
    private Product product;
    
    @NotNull
    @Column(name = "QT_BOXES")
    private Long purchaseBoxQuantity;
}
Liam Park
  • 414
  • 1
  • 9
  • 26

2 Answers2

3

The error you are seeing means Hibernate tried to update an entity, but the update statement actually updated 0 rows.

Hibernate assumes this is because the version attribute did not match, since for the purpose of optimistic locking all updates on entities with a version attribute have a where clause like the following.

WHERE id = ? and version = ?

The id = ? ensures that the correct row is updated. The version = ? ensures that the version hasn't changed. It does get changed with every update.

Since you don't have a version attribute in your entity, I'm somewhat surprised that you still get this error, but since you do, the only explanation is that the id does not match the one in the database.

So either the entity was deleted, or the id changed.

Jens Schauder
  • 77,657
  • 34
  • 181
  • 348
1

Without having the full source, I can just guess that the problem is on your embedded id, specifically I guess it has to be with

@ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)

I suggest you to add the following properties to your application properties file

spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true

so you can check how your entity's id is probably changed in a way you didn't expect to.

Javi Vazquez
  • 517
  • 6
  • 21