maxc5
maxc5

Reputation: 9

why does hibernate throw org.hibernate.TransientPropertyValueException with @Version field?

hibernate throws:

Caused by: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : api.models.SaleItem.product -> api.models.Product

but if i don't use version field and LockModeType.OPTIMISTIC, it works

i have the following classes

@Entity
@Table(name = "products")
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    @Column(unique = true, nullable = false)
    @NotNull(message = "name is required")
    @Size(min = 3, max = 50, message = "name must be between 3 and 50 characters")
    private String name;
    @Min(value = 1, message = "price not be less than 1")
    @Max(value = 1000000, message = "price should not be greater than 1000000")
    private float price;
    @Min(value = 0, message = "stock not be less than 0")
    @Max(value = 10000, message = "stock should not be greater than 10000")
    private int stock;
    @Min(value = 1, message = "minimum stock not be less than 1")
    @Max(value = 1000, message = "minimum stock should not be greater than 1000")
    @Column(name = "minimum_stock")
    private int minimumStock;
    private Status status;
    @ImageSize
    private String image;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "category_id", nullable = false)
    @NotNull(message = "category cannot be null")
    private Category category;
    @Version
        private Integer version;

    public Product() {
        this.status = Status.ACTIVE;    
    }
    
    public Product(Integer id) {
        this.id=id;
        this.status=Status.ACTIVE;
    }

        @PrePersist
    void onCreate() {
        for (SaleItem item : items)
            item.setSale(this);
        for (Payment payment : payments)
            payment.setSale(this);
        if(this.discount!=null)
            this.discount.setSale(this);
        if(this.surcharge!=null)
            this.surcharge.setSale(this);
    }
    ...
}

@Entity
@Table(name = "sales_items")
public class SaleItem {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="product_id")
    @NotNull(message="product cannot be null")
    private Product product;
    @Min(value = 1, message = "quantity not be less than 1")
        @Max(value = 10000, message = "quantity should not be greater than 10000")
    private int quantity;
    @Min(value = 0, message = "price not be less than 1")
        @Max(value = 1000000, message = "price should not be greater than 1000000")
    private float price;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "sale_id")
    @JsonIgnore
    private Sale sale;
        ...
    
}

repository methods:

public Sale save(Sale entity) {
        EntityManager em = emf.createEntityManager();
        try {
            em = emf.createEntityManager();
            em.getTransaction().begin();
            em.persist(entity);
            updateStock(entity.getItems(), em);
            em.getTransaction().commit();
            em.close();
            return entity;
        } catch (PersistenceException e) {
            em.getTransaction().rollback();
            e.printStackTrace();
        } finally {
            em.close();
        }
    }
private void updateStock(List<SaleItem> items, EntityManager em) {
        for (SaleItem item : items) {
            Product product = em.find(Product.class, item.getProduct().getId(),LockModeType.OPTIMISTIC);
            product.setStock(product.getStock() - item.getQuantity());
        }
    }

Controller method:

public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        Sale sale = mapper.readValue(Util.getPayload(request), Sale.class);
        List<String> errors = validate(sale);
        if (!errors.isEmpty())
            Util.sendAsJson(response, errors, mapper);
        else {
            sale.setUser(new User(Double.valueOf(request.getAttribute("id").toString()).intValue()));
            sale.setDate(new Date());
            Util.sendAsJson(response, saleRepo.save(sale), mapper);
        }

I have tried with other versions of hibernate but it does not work.

Upvotes: 0

Views: 55

Answers (1)

Daniel Stancu
Daniel Stancu

Reputation: 62

I think the issue resides in the below relation definition, on the SaleItem entity:

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="product_id")
@NotNull(message="product cannot be null")
private Product product;

You do not specify whether the Product entity to be persisted by persisting the SaleItem entity, so you have to add the cascade attribute to the @ManyToOne relationship, like cascade = CascadeType.ALL or CascadeType.PERSIST.

Upvotes: 0

Related Questions