Remove row from table throws ConstraintViolationException - spring-boot

Im having a problem when i want to delete the product from the database, deleting it, it should be removed from all the orders that contain that product. But when i try to do it this is the error i get:
"error_message": "Request processing failed; nested exception is org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [fkbjvki7e3gm7vrphs73g4x7d2g]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement"
This is my Order class:
#Entity
#Table(name="orders")
public class Order{
private #Id
#GeneratedValue
Long id;
#OneToMany(mappedBy = "order", cascade = CascadeType.ALL,orphanRemoval = true)
private List<ProductOrderDetails> orderedProducts = new ArrayList<>();
public void addProduct(Product product, int quantity) {
ProductOrderDetails orderedProduct = new ProductOrderDetails(this,product,quantity);
orderedProducts.add(orderedProduct);
product.getProductOrderDetails().add(orderedProduct);
totalOrderPrice+=product.getPrice()*quantity;
}
public void removeProduct(Product product,int quantity) {
ProductOrderDetails orderedProduct = new ProductOrderDetails( this, product,0);
product.getProductOrderDetails().remove(orderedProduct);
orderedProducts.remove(orderedProduct);
orderedProduct.setOrder(null);
orderedProduct.setProduct(null);
totalOrderPrice-=product.getPrice()*quantity;
}
}
This is my Product class
#Entity
#Table
public class Product {
private #Id
#GeneratedValue
Long id;
private String name;
#OneToMany(mappedBy = "order", cascade = CascadeType.MERGE,orphanRemoval = true)
private List<ProductOrderDetails> productOrderDetails = new ArrayList<>();
}
ProductOrderID
#Embeddable
public class ProdOrderId implements Serializable {
#Column(name = "order_id")
private Long orderId;
#Column(name = "product_id")
private Long productId;
}
Many to many column of Products and Orders
#Entity
#Table
public class ProductOrderDetails implements Serializable{
#EmbeddedId
#JsonIgnore
private ProdOrderId id;
#ManyToOne
#MapsId("orderId")
#JsonIgnore
Order order;
#ManyToOne
#MapsId("productId")
Product product;
private int quantity;
}
This is my controller method
#DeleteMapping("/{id}")
ResponseEntity<?> deleteProduct(#PathVariable Long id)
{
repository.deleteById(id);
return ResponseEntity.noContent().build();
}

I don't think this is doing what you think it's doing:
ProductOrderDetails orderedProduct = new ProductOrderDetails( this, product,0);
product.getProductOrderDetails().remove(orderedProduct);
If you debug your code or check the return value of remove you will find that it is returning false, which means nothing was removed.
You're just creating a new ProductOrderDetails and then trying to remove it from product.getProductOrderDetails(), but it doesn't exist in it. You need to find the right element to remove from that collection.

Related

JPA ManyToMany fails with "must have same number of columns as the referenced primary key"

I have three tables customer, product and sales. sales is the join table to store the customer's products as shown below:
customer, product and Sales tables
My Entities defined as described below:
Customer.java
#Entity
#Table(name="customer")
public class Customer {
#Id
#Column(name="c_id")
private String customerId;
#Column(name="customer_name")
private String customerName;
#ManyToMany
#JoinTable(
name = "sale",
joinColumns = #JoinColumn(name = "c_id"),
inverseJoinColumns = #JoinColumn(name = "p_id"))
private Set<Product> customerProducts = new HashSet<>();
}
Product.java
#Entity
#Table(name="product")
public class Product {
#Id
#Column(name="p_id")
private String productId;
#Column(name="product_name")
private String productName;
#Column(name="price")
private Double price;
// ... Setters & Getters
}
Sales.java
#Entity
#Table(name="sales")
public class Sales {
#EmbeddedId
private SalesPK salesId;
#Column(name="qty")
private Long qty;
// ... Setters & Getters
}
SalesPK.java
#Embeddable
public class SalesPK implements Serializable {
#Column(name = "c_id")
private String customerId;
#Column(name = "p_id")
private String productId;
public SalesPK() {}
public SalesPK(String customerId, String productId) {
this.customerId = customerId;
this.productId = productId;
}
}
CustomerRepository.java
#Repository
public interface CustomerRepository extends CrudRepository<Customer, String> {
#Query("select customer from Customer customer " +
"left join fetch customer.customerProducts " +
"where customer.customerName = :customerName")
public Customer getCustomerPurchasedProducts(String customerName);
}
My Spring boot application fail to start with following exception:
org.hibernate.MappingException: Foreign key (FK7wwx8x75009xqb1y0tawm8rty:SALES [p_id])) must have same number of
columns as the referenced primary key (SALES [c_id,p_id])
What am I missing here? I have followed the notes as described here in https://www.baeldung.com/jpa-many-to-many
UPDATE:
There is no issue with above solution, I have misspelled "sales" table in #ManyToMany declaration changing from "sale" to "sales" fixed the issue. Strange behavior why it didn't compline about missing table instead it complain about actual composite primary key definition.
Following code Fixed the issue:
#ManyToMany
#JoinTable(
name = "sales",
joinColumns = #JoinColumn(name = "c_id"),
inverseJoinColumns = #JoinColumn(name = "p_id"))
private Set<Product> customerProducts = new HashSet<>();
}
I would map these classes a bit differently:
#Entity
#Table(name="customer")
public class Customer {
#Id
#Column(name="c_id")
private String customerId;
#Column(name="customer_name")
private String customerName;
#OneToMany(mappedBy = "customer")
private Set<Sale> customerSales = new HashSet<>();
}
#Entity
#Table(name="product")
public class Product {
#Id
#Column(name="p_id")
private String productId;
#Column(name="product_name")
private String productName;
#Column(name="price")
private Double price;
}
#Entity
#Table(name="sales")
public class Sales {
#EmbeddedId
private SalesPK salesId;
#MapsId("customerId") // maps customerId attribute of embedded id
#ManyToOne
Customer customer;
#MapsId("productId") // maps productId attribute of embedded id
#ManyToOne
Product product;
#Column(name="qty")
private Long qty;
// ... Setters & Getters
}
#Embeddable
public class SalesPK implements Serializable {
#Column(name = "c_id")
private String customerId;
#Column(name = "p_id")
private String productId;
public SalesPK() {}
public SalesPK(String customerId, String productId) {
this.customerId = customerId;
this.productId = productId;
}
}

Unable to find Entity not found exception when using one-to-many bidirectional

When ever post the data using postman or run the program , facing below error:
Unable to find com.example.entity.Product with id 1; nested exception is javax.persistence.EntityNotFoundException: Unable to find com.example.entity.Product with id 1
Below is parent class:
#Entity
public class Customer {
#Id
#GeneratedValue
private Integer cust_Id;
private String cust_name;
private String city;
#OneToMany(mappedBy = "customer")
private List<Product> products;
}
Child class:
#Entity
public class Product {
#Id
private Integer pid;
private String pname;
#ManyToOne(fetch = FetchType.LAZY)
#NotFound(action = NotFoundAction.IGNORE)
#JoinColumn(name = "fk_cust_id")
private Customer customer;
}
#NotFound(action=NotFoundAction.IGNORE) results in EAGER loading for #ManyToOne(fetch = FetchType.LAZY) field. Refer here

Shared Primary Key between two Entities Not Working

I have created two Entities namely Teacher and Detail, the code snippet is shown below
Teacher.java
#Entity
#Table(name = "teacher")
public class Teacher implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private long id;
#Column(name = "name")
private String name;
#Column(name = "age")
private int age;
#OneToOne(mappedBy = "teacher", cascade = CascadeType.ALL)
private Detail detail;
public Teacher() {
}
public Teacher(String name, int age) {
this.name = name;
this.age = age;
}
//getter and setter
}
Detail.java
#Entity
#Table(name = "detail")
public class Detail implements Serializable {
#Id
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "id")
private Teacher teacher;
#Column(name = "subjects")
private String subjects;
public Detail() {
}
public Detail(String subjects) {
this.subjects = subjects;
}
//getter and setter
}
I am trying to achieve one to one mapping with the shared primary key concept
but when i execute the controller, only Teacher table is updating with the value
try {
Teacher teacher=new Teacher("xyz",23);
Detail detail=new Detail("Java,c,c++");
teacher.setDetail(detail);
session.beginTransaction();
session.save(teacher);
session.getTransaction().commit();
model.addAttribute("added", "data inserted");
session.close();
}
After executing only Teacher table is updated with the specified values.Detail table is still showing empty
It does not work exactly like that. You still need the id field in your Detail, so add:
#Id
private long id;
to your Deatail class.
And - as comment suggests - replace the #Id annotation in field Teacher to #MapsId. This way the id of Teacher is mapped to the id of Detail BUT ONLY if you also set the teacher to the detail - you always need to set both sides of relationship - like:
teacher.setDetail(detail);
detail.setTeacher(teacher);

Spring Boot : Error :Cannot call sendError() after the response has been committed

I am getting this error .
Cannot call sendError() after the response has been committed
Can someone help me figure out why?.
#Entity
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#OneToOne(
fetch = FetchType.LAZY,
cascade = CascadeType.ALL
)
#JoinColumn(name = "details_id")
private Details details;
//Getters and setters left out for brevity
}
#Entity
public class Details {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
private String description;
private float price;
private float discount;
#OneToOne(mappedBy = "details")
private Product product;
}
#RestController
public class ProductController {
#Autowired
ProductRepository productRepository;
#GetMapping("/getAllProducts")
public Iterable<Product> getAllProducts(){
return productRepository.findAll();
}
}
#RestController
public class DetialsController {
#Autowired
ProductRepository productRepository;
#Autowired
DetailsRepository detailsRepository;
#PostMapping("/details")
public Details addDetails(#RequestBody Details details) {
Product newProduct = new Product();
newProduct.setDetails(details);
productRepository.save(newProduct);
return detailsRepository.save(details);
}
}
I am able to make the POST call to /details; for adding details successfully. But when i make GET call to /getAllProducts, I am getting this error
Cannot call sendError() after the response has been committed
This is an issue with bidirectional relationships, as they hold references to each other, at deserialization, Jackson runs in an infinite loop. My first suggestion would be adding #JsonIgnore to one end of the relation.
#OneToOne(mappedBy = "details")
#JsonIgnore
private Product product;
Afterward, if that solved your issue, you can look over #JsonManagedReference/#JsonBackReference and #JsonIdentityInfo.
You can also look over this link for more insight
You can use this :
#Entity
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#JsonBackReference(value = "details_id")
#OneToOne(
fetch = FetchType.LAZY,
cascade = CascadeType.ALL
)
#JoinColumn(name = "details_id")
private Details details;
//Getters and setters left out for brevity
}
#Entity
public class Details {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
private String description;
private float price;
private float discount;
#JsonManagedReference(value = "details")
#OneToOne(mappedBy = "details",,cascade=CascadeType.ALL)
private Product product;
}

Record not inserted while using #ManyToOne mapping

I have 2 tables 'orders' and 'orderlines' and used bidirectional OneToMany mapping.When i save the order, record is successfully inserted into table 'orders'.But my 'orderlines' table is empty.No record is inserted.
This is the save operation code in Controller.
#RequestMapping(value = "ordersuccess", method = RequestMethod.POST)
public String processOrder(#ModelAttribute("order") Order order,
#ModelAttribute("cart") Cart cart,
BindingResult result) {
if (!result.hasErrors()) {
Set<OrderLine> orderLines = new HashSet<OrderLine>();
for(CartLine c : cart.getCartLines()) {
OrderLine line = new OrderLine();
line.setOrder(order);
line.setProduct(c.getProduct());
line.setProductPrice(c.getProduct().getPrice());
line.setTotalPrice(c.getPrice());
orderLines.add(line);
order.setOrderLines(orderLines);
}
orderService.save(order);
orderLineService.save(orderLine);
}
return "ordersuccess";
}
Can someone point me what wrong i am doing.
EDIT:
OrderLine.java
public class OrderLine {
#Id
#GeneratedValue
#Column(name="orderline_id")
private int orderline_id;
#ManyToOne
#JoinColumn(name = "order_id")
private Order order;
#ManyToOne(targetEntity = Product.class,
cascade = CascadeType.ALL,
fetch = FetchType.LAZY)
#JoinTable(
name="products",
joinColumns=#JoinColumn(name="product_id")
)
private Product product;
)
Order.java
public class Order {
#Id
#GeneratedValue
#Column(name="id")
private int id;
#OneToMany(mappedBy = "order")
private Set<OrderLine> orderLines;
//getter/setter
The orderLines object is created:
Set<OrderLine> orderLines = new HashSet<OrderLine>();
You then add lines to it:
orderLines.add(line);
But it never attributed to an order or sent to the service layer.
Also the OrderLine.product mapping should be like this
public class OrderLine {
#Id
#GeneratedValue
#Column(name="orderline_id")
private int orderline_id;
#ManyToOne
#JoinColumn(name = "order_id")
private Order order;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "product_id")
private Product product;
}
and Order.orderLines should have a cascade:
public class Order {
#Id
#GeneratedValue
#Column(name="id")
private int id;
#OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private Set<OrderLine> orderLines;
}
You then need to save the orderLines:
order.setOrderLines(orderLines);
and save the order:
orderService.save(order);
When order is saved it will cascade the orderlines and the associated product too.
If you have bidirectional associations don't forget to set both sides.

Resources