admin管理员组文章数量:1312836
So I am trying to implement a short e-commerce kind of project using SpringBoot and everything. As per my model, every user
will have a cart
with a list of items (CartItems
in my case), and these items will consist of a particular product
.
I am adding only the relevant fields in these classes
CartItem.java
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CartItem {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "cart_item_id")
private Long id;
@ManyToOne
@JoinColumn(name = "cart_id")
private Cart cart;
@ManyToOne
@JoinColumn(name = "product_id")
private Product product;
}
Cart.java
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Cart {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "cart_id")
private Long id;
@OneToOne
@JoinColumn(name = "user_id")
private User user;
@OneToMany(mappedBy = "cart", cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE}, orphanRemoval = true)
private List<CartItem> cartItems = new ArrayList<>();
private Double totalPrice = 0.0;
}
Product.java
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "product_id")
private Long id;
@ManyToOne
@JoinColumn(name = "category_id")
private Category category;
@OneToMany(mappedBy = "product", cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE}, fetch = FetchType.EAGER)
private List<CartItem> products = new ArrayList<>();
}
Everything is working fine until I try to delete a product
from the cart
. With the endpoint, what I want should happen is that the cart
's total price should change, and the corresponding cartItem
that was holding that product
in that specific cart
should get deleted.
Below is the controller code -
@DeleteMapping("/carts/{cartId}/product/{productId}")
public ResponseEntity<String> deleteProductFromCart(@PathVariable Long cartId,
@PathVariable Long productId) {
String status = cartService.deleteProductFromCart(cartId, productId);
return new ResponseEntity<>(status, HttpStatus.OK);
}
Below is the service implementation -
@Transactional
@Override
public String deleteProductFromCart(Long cartId, Long productId) {
Cart cart = cartRepository.findById(cartId)
.orElseThrow(() -> new ResourceNotFoundException("Cart", "id", cartId));
CartItem cartItem = cartItemRepository.findCartItemByProductIdAndCartId(cartId, productId);
if (cartItem == null) {
throw new ResourceNotFoundException("Product", "id", productId);
}
cart.setTotalPrice(cart.getTotalPrice() -
(cartItem.getPrice() * cartItem.getQuantity()));
// Code #1
// cart.getCartItems().remove(cartItem);
// cartRepository.save(cart);
// Code #2
// cartItemRepository.delete(cartItem);
// Code #3
// cartItemRepository.deleteCartItemByProductIdAndCartId(cartId, productId);
return "Product " + cartItem.getProduct().getName() + " removed from the cart !!!";
}
The custom repository method -
@Modifying
@Query("DELETE FROM CartItem ci WHERE ci.cart.id = ?1 AND ci.product.id = ?2")
void deleteCartItemByProductIdAndCartId(Long cartId, Long productId);
Out of the 3 blocks of code, only the 3rd one seems to be deleting the cart item from the database. Why is that??
As per my understanding, code block 1 should work because of orphanRemoval = true
in the entity class and code block 2 should work because I am directly deleting the cartItem
that I fetched from the database.
I believe I am missing some fundamental thing. Please help!
So I am trying to implement a short e-commerce kind of project using SpringBoot and everything. As per my model, every user
will have a cart
with a list of items (CartItems
in my case), and these items will consist of a particular product
.
I am adding only the relevant fields in these classes
CartItem.java
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CartItem {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "cart_item_id")
private Long id;
@ManyToOne
@JoinColumn(name = "cart_id")
private Cart cart;
@ManyToOne
@JoinColumn(name = "product_id")
private Product product;
}
Cart.java
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Cart {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "cart_id")
private Long id;
@OneToOne
@JoinColumn(name = "user_id")
private User user;
@OneToMany(mappedBy = "cart", cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE}, orphanRemoval = true)
private List<CartItem> cartItems = new ArrayList<>();
private Double totalPrice = 0.0;
}
Product.java
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "product_id")
private Long id;
@ManyToOne
@JoinColumn(name = "category_id")
private Category category;
@OneToMany(mappedBy = "product", cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE}, fetch = FetchType.EAGER)
private List<CartItem> products = new ArrayList<>();
}
Everything is working fine until I try to delete a product
from the cart
. With the endpoint, what I want should happen is that the cart
's total price should change, and the corresponding cartItem
that was holding that product
in that specific cart
should get deleted.
Below is the controller code -
@DeleteMapping("/carts/{cartId}/product/{productId}")
public ResponseEntity<String> deleteProductFromCart(@PathVariable Long cartId,
@PathVariable Long productId) {
String status = cartService.deleteProductFromCart(cartId, productId);
return new ResponseEntity<>(status, HttpStatus.OK);
}
Below is the service implementation -
@Transactional
@Override
public String deleteProductFromCart(Long cartId, Long productId) {
Cart cart = cartRepository.findById(cartId)
.orElseThrow(() -> new ResourceNotFoundException("Cart", "id", cartId));
CartItem cartItem = cartItemRepository.findCartItemByProductIdAndCartId(cartId, productId);
if (cartItem == null) {
throw new ResourceNotFoundException("Product", "id", productId);
}
cart.setTotalPrice(cart.getTotalPrice() -
(cartItem.getPrice() * cartItem.getQuantity()));
// Code #1
// cart.getCartItems().remove(cartItem);
// cartRepository.save(cart);
// Code #2
// cartItemRepository.delete(cartItem);
// Code #3
// cartItemRepository.deleteCartItemByProductIdAndCartId(cartId, productId);
return "Product " + cartItem.getProduct().getName() + " removed from the cart !!!";
}
The custom repository method -
@Modifying
@Query("DELETE FROM CartItem ci WHERE ci.cart.id = ?1 AND ci.product.id = ?2")
void deleteCartItemByProductIdAndCartId(Long cartId, Long productId);
Out of the 3 blocks of code, only the 3rd one seems to be deleting the cart item from the database. Why is that??
As per my understanding, code block 1 should work because of orphanRemoval = true
in the entity class and code block 2 should work because I am directly deleting the cartItem
that I fetched from the database.
I believe I am missing some fundamental thing. Please help!
- The other 2 blocks of code seem to be doing nothing. Plus there is no exception thrown or any kind of red flag seen. – love_to_code Commented Feb 2 at 19:03
1 Answer
Reset to default 0The reason, why your code is not working as you expect - is probably because of Hibernate's entity lifecycle, persistence context, and how deletions are cascaded.
// Code #1
cart.getCartItems().remove(cartItem);
cartRepository.save(cart);
It is true, that orphanRemoval = true
should trigger CartItem
deletion from the database. But there can be cases, when the removal won't trigger deletion, for example, if cartItem
is not actually contained in cart.getCartItems()
. It can happen if Hibernate fetches collections lazily or because of differences in persistence context.
// Code #2
cartItemRepository.delete(cartItem);
When .delete(entity)
is called, the entity can be just marked for deletion, but the delete action will happen only at flush time. You can try to break the association explicitly with cartItem.setCart(null);
// Code #3
cartItemRepository.deleteCartItemByProductIdAndCartId(cartId, productId);
This is a JPQL update/delete query, it ignores entity states, associations, or persistence context issues. That is why it works always.
But be careful, JPQL query completely bypasses the Hibernate persistence lifecycle, it is better to be used only when you want an immediate, bulk deletion, but only when entity lifecycle doesn't matter. There can be some negative consequences of using JPQL queries in some cases:
- If you use a JPQL delete query, Hibernate doesn’t know that an entity has been deleted. This can lead to stale state issues where an entity still exists in memory but is actually deleted from the database.
- Hibernate provides entity lifecycle callbacks like
@PreRemove
and@PostRemove
that let you run logic before or after an entity is deleted. But JPQL query won't trigger this events. - If you delete with JPQL and later rollback the transaction, the entity remains deleted in the database.
So, it is always better to use JPA, except few specific cases (like bulk deletion, avoiding unnecessary fetching or for scheduled cleanup jobs).
I suggest you to find the right order for operations to trigger the deletion. For example, this way:
cart.getCartItems().remove(cartItem); // remove from parent collection
cartItem.setCart(null); // prevent Hibernate from reattaching it
cartRepository.save(cart); // persist changes
cartItemRepository.delete(cartItem); // ensure the entity is deleted if orphanRemoval fails
Hope, this will help you.
本文标签: javaJPAHibernate not updating the database after calling deleteStack Overflow
版权声明:本文标题:java - JPAHibernate not updating the database after calling delete - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741837797a2400320.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论