Reputation: 9
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
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