org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException - spring-boot

This is my orderservice implementation for creating and saving orders here with the customerid I'm getting customer and customer has a cart and in the cart product list is there and for that product list, I should create an order.
public Order save(int custid) {
Optional<Customer> cust = customerRepo.findById(custid);//here customer is there and inside customer cart is there inside cart medicine list is there.
Cart ct= cust.get().getCart();//getting cart from customer
if(ct.getMedicineList().size()!=0) {//create order only if the medicine list is not empty. there are 8 fields **orderDate,dispatchDate,status,medicineList,totalCost,customer and orderId(GeneratedValue)** I can't set the orderId cuz it is auto generated.
LocalDate todaysDate = LocalDate.now();
LocalDate dispatchdate = todaysDate.plusDays(3);
List<Medicine> orderList= new ArrayList<Medicine>();
List<Medicine> cartList= new ArrayList<Medicine>();
cartList=ct.getMedicineList();
orderList.addAll(cartList);
Order ord = new Order();
ord.setCustomer(cust.get());
ord.setMedicineList(orderList);
ord.setDispatchDate(dispatchdate);
ord.setOrderDate(todaysDate);
ord.setStatus("Placed");
ord.setTotalCost((float)ct.getTotalAmount());
logger.info("Add order to the database");
return orderRepository.save(ord);
}
return null;
}
this is my order controller
#PostMapping("/order/{custid}")
public ResponseEntity<Order> addOrder(#Valid #PathVariable("custid") int custid) {
logger.info("Add order in database");
return new ResponseEntity<>(orderService.save(custid), HttpStatus.OK);
}
this is my medicine Entity
#Data
#RequiredArgsConstructor
#ToString
#Table(name = "medicine")
#Entity
public class Medicine {
#Id
#Column(name = "medicine_id", nullable = false)
#NonNull
#GeneratedValue
private int medicineId;
#NonNull
#Size(min = 3, message = "Minimum charecters in medicine name should be 3.")
#NotEmpty
#Column(unique = true, name = "medicine_name", nullable = false)
private String medicineName;
#NonNull
#Column(name = "medicine_cost", nullable = false)
private float medicineCost;
#NonNull
#Column(name = "mfd", nullable = false)
private LocalDate mfd;
#NonNull
#Column(name = "expiry_date", nullable = false)
private LocalDate expiryDate;
#NonNull
#Column(name = "medicine_quantity", nullable = false)
private int medicineQuantity = 1;
#NonNull
private String medicineCategory;
#NonNull
private String medicineDescription;
#JsonIgnore
#ManyToMany(cascade = CascadeType.ALL, mappedBy = "medicineList",fetch = FetchType.EAGER)
private List<Order> orderList;
}
this is my order Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
#ToString
public class Order {
#Id
#Column(name = "orderId")
#GeneratedValue
private int orderId;
#NonNull
private LocalDate orderDate;
#ManyToMany(targetEntity = Medicine.class, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "ord_med", joinColumns = { #JoinColumn(name = "ord_id") }, inverseJoinColumns = {
#JoinColumn(name = "med_id") })
private List<Medicine> medicineList = new ArrayList<>();
#NonNull
private LocalDate dispatchDate;
#NotEmpty
private float totalCost;
#NonNull
private String status;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name="c_ord_fk",referencedColumnName = "customerId")
#NonNull
private Customer customer;
}
here when i try to create order for the list inside cart it gives me [36m.m.m.a.ExceptionHandlerExceptionResolver[0;39m [2m:[0;39m Resolved [org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction]
i'm not sure but i think its because of the id but it is autogenerated. Idk how to create an order object for the list of products or is this the correct way to do it.

Actually I found the answer, all I have to do is when you use ManyToMany mapping the cascade type must be cascade = {CascadeType.MERGE,CascadeType.REFRESH} instead of {cascade = CascadeType.ALL} and it works fine. reference JPA: detached entity passed to persist: nested exception is org.hibernate.PersistentObjectException

Related

could not extract ResultSet; SQL [n/a];nested exception is org.hibernate.exception.SQLGrammarException While trying to fetch Department list

I have tried many way to solve this problem but Can't,
If I remove custome repository and use JpaRepository then it is woking fine without changing single digit code. I don't know why this is happening. When I'm trying to fetch department list then it is occuring error on postman
Please help me as soon as possible
Here is the my code please check it
College.java
#Entity
#Table(name = "colleges")
public class College {
#Id
#GeneratedValue(
strategy = GenerationType.IDENTITY
)
#Column(name = "college_id")
private Long id;
#Column(name = "college_name", nullable = false)
private String collegeName;
#Column(name = "address", nullable = false)
private String address;
#OneToMany(mappedBy = "college", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Department> departments = new HashSet<>();
#OneToMany(mappedBy = "faccollege", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Faculty> faculties = new HashSet<>();
//Getter setters
}
Department.java
#Entity
#Table(name = "departments")
public class Department {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "department_id")
private long id;
private String departmentName;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "college_id", nullable = false)
private College college;
#OneToMany(mappedBy = "department", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Faculty> faculties = new HashSet<>();
//Getter and Setters
}
DepartmentRepository
#Repository
#Transactional
public class DepartmentRepository{
#Autowired
SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sf) {
this.sessionFactory = sf;
}
public List<Department> findAll(){
Session session = this.sessionFactory.getCurrentSession();
List<Department> departmentList = session.createQuery("from Department").list();
return departmentList;
}
#Transactional
public Department save(Department department) {
Session session = this.sessionFactory.getCurrentSession();
session.save(department);
return department;
}
public Department findById(long id) {
Session session = this.sessionFactory.getCurrentSession();
Department department = (Department) session.get(Department.class, id);
return department;
}
}

JPA - How to update child object in OneToMany relationship?

I have a Customer class where each customer can have multiple Products. The class is as follow:
#Entity
#Table(name = "customer")
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "firstName")
private String firstName;
#Column(name = "lastName")
private String lastName;
#OneToMany(targetEntity=Product.class, mappedBy = "customer", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
#PrimaryKeyJoinColumn
#JsonManagedReference
private List<Product> products = new ArrayList<>();
//getters and setters here
}
and the Product class holds OneToOne relation with other Classes and it is as follows:
#Entity
#Table(name = "product")
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne(targetEntity=Customer.class, fetch = FetchType.LAZY)
#JoinColumn(name = "customer_id")
#JsonBackReference
private Customer customer;
#OneToOne(mappedBy = "product", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
#PrimaryKeyJoinColumn
#JsonManagedReference
private SomeType1 someType1;
#OneToOne(mappedBy = "product", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
#PrimaryKeyJoinColumn
#JsonManagedReference
private SomeType2 someType2;
#OneToOne(mappedBy = "product", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
#PrimaryKeyJoinColumn
#JsonManagedReference
private SomeType3 someType3;
#OneToOne(mappedBy = "product", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
#PrimaryKeyJoinColumn
#JsonManagedReference
private SomeType4 someType4;
//getters and setters here
}
I am trying to achieve following functionality with this:
Given Customer ID and Product ID, update the values in SomeType1, SomeType2, SomeType3 classes. I am getting the updated SomeType1, SomeType2, SomeType3 objects from UI and I want to update the values in DB. I already have PUT method in place for this.
Here's the PUT method:
#PutMapping(value = "customer/{id}/product/{product_id}")
#ResponseBody
public ResponseEntity<Product> updateProduct(#PathVariable final String id,
#PathVariable final String product_id, #RequestBody final Product product) {
Optional<Customer> customerInDb = customerService.getCustomerById(id);
if (!customerInDb.isPresent()) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND,
MessageFormat.format("Customer with id {0} does not exist.", id));
} else {
product.setId(Long.valueOf(product_id));
product.setCustomer(customerInDb.get());
Product savedProduct = customerService.createProduct(product);
return ResponseEntity.ok(savedProduct);
}
}
I am getting following error for this REST call:
javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [com.myapp.arg.entities.SomeType2#12]
What could be the reason for this?
createProduct method:
#Override
public Product createProduct(Product product) {
Product savedProduct = productRepository.save(product);
return savedProduct;
}
JSON input to the PUT method:
{
"id":9,
"someType1":{
"id":9,
"avg":20,
"total":20
},
"someType2":{
"id":9,
"circum":45.0,
"strength":45.0,
"totalNav":0.0
},
"someType3":{
"id":9,
"tensile":87,
"pull":128,
"push":56,
"upward":28.0
},
"measuredBy":"SJ",
"addedDate":"2021-05-23",
"type":"Prime"
}
you are using the same id for all of your entities. ID must be unique

How to set join for predicate

I have a entity for product:
package com.javaschool.entity;
import lombok.*;
import javax.persistence.*;
import java.util.Set;
#EqualsAndHashCode(of = {"id"})
#ToString(of = { "id", "quantity", "price", "model"})
#Entity
#Table(name = "products")
#Data
#NoArgsConstructor
#AllArgsConstructor
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "quantity")
private int quantity;
#Column(name = "price")
private int price;
#Column(name = "model")
private String model;
#Column(name = "is_active")
private boolean active;
#Column(name = "picture_url")
private String url;
#ManyToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "category_id")
private Category category;
#ManyToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "brand_id")
private Brand brand;
#ManyToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "season_id")
private Season season;
#ManyToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "color_id")
private Color color;
#ManyToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "material_id")
private Material material;
#ManyToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "size_id")
private Size size;
#ManyToMany(mappedBy = "productSet", fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Set<Order> orderSet;
}
I want to filter by category, season, color, brand and other related parameters
At the moment my filtering function looks like this. It works for parameters such as model, price, quantity. That is, for those that are data in this table and not from others. How can I filter by parameters that are taken from other tables?
#Override
public List<Product> findByParam(List<SearchCriteria> params) {
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Product> criteriaQuery = criteriaBuilder.createQuery(Product.class);
Root<Product> root = criteriaQuery.from(Product.class);
Predicate predicate = criteriaBuilder.conjunction();
ProductSearchQueryCriteriaConsumer productConsumer = new ProductSearchQueryCriteriaConsumer(predicate, criteriaBuilder, root);
params.stream().forEach(productConsumer);
predicate = productConsumer.getPredicate();
criteriaQuery.where(criteriaBuilder.equal(root.get(Product_.active), true),
predicate);
List<Product> result = entityManager.createQuery(criteriaQuery).getResultList();
return result;
}
I thought that you can make such a call and everything will work. But I was wrong.
List<SearchCriteria> params = new ArrayList<SearchCriteria>();
params.add(new SearchCriteria("season_id", ":", "3"));
List<ProductDto> productDtoList = productService.getProductsByParam(params);
My SearchCriteria
#Data
#AllArgsConstructor
#NoArgsConstructor
public class SearchCriteria {
private String key;
private String operation;
private Object value;
}
Need to make this:
List<SearchCriteria> params = new ArrayList<SearchCriteria>();
params.add(new SearchCriteria("category", ":", categoryRepository.findById(1)));
That is, in the searchcriteria for the value object, pass an object of this class to filter by

Could not write JSON: Infinite recursion

I am getting StackOverflow recursion error when I run query in Postman or Browser .
When i run says:
.w.s.m.s.DefaultHandlerExceptionResolver : Could not write JSON: Infinite recursion (StackOverflowError); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError)
Here is the model classes :
#Entity
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
private String title;
#NotNull
private String description;
#NotNull
private double price;
#OneToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "category_id", nullable = false)
private Category category;
private boolean isSealed;
#OneToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "currency_id", nullable = false)
private Currency currency;
#OneToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "user_id", nullable = false)
private User user;
#Nullable
#OneToMany(mappedBy = "product",
cascade = CascadeType.ALL, orphanRemoval = true)
private List<Images> images;
private Date createdDate = new Date();
#OneToOne(fetch = FetchType.LAZY,
cascade = CascadeType.ALL,
mappedBy = "product")
private View view;
#OneToOne(fetch = FetchType.LAZY,cascade=CascadeType.ALL)
#JoinColumn(name="type_id")
private Type type;
private Long viewCount; }
#Entity public class Images{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String imagePath;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "product_id")
private Product product; }
#Entity public class User implements UserDetails, Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotEmpty
private String fullName;
#NotEmpty
#Email
#Column(unique = true)
private String email;
#NotNull
#Column(unique = true)
private int phoneNumber;
#NotEmpty
#Size(min = 5)
private String password;
private Date createAt = new Date();
#Nullable
private String picPath;
#Nullable
private String token;
#ManyToMany
#JoinTable(name = "user_roles", joinColumns = {#JoinColumn(
name = "user_id")},
inverseJoinColumns = {#JoinColumn(name = "role_id")})
private List<Role> roles;
#OneToOne(fetch = FetchType.LAZY,
cascade = CascadeType.ALL,
mappedBy = "user")
private Product product;
#OneToOne(fetch = FetchType.LAZY,
cascade = CascadeType.ALL,
mappedBy = "user")
private View view; }
#Entity
public class Currency{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String code;
private String currency;
private String region_country;
#OneToOne(mappedBy = "currency", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Product product; }
#Entity
public class Category {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String imagePath;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY,
mappedBy = "category")
private Product product;
#OneToMany(mappedBy = "category", fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
private Set<Brand> brands; }
#Entity public class Brand {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "category_id", nullable = false)
private Category category; }
#Entity public class View {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "user_id", nullable = false)
private User user;
#OneToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "product_id", nullable = false)
private Product product; }
#Entity public class Type {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
private String name;
#OneToOne(fetch = FetchType.LAZY,
cascade = CascadeType.ALL,
mappedBy = "type")
private Product product; }
#Id
private String role;
#ManyToMany(mappedBy = "roles")
private List<User> users;
}
More than one of your entities have each other in themselves.
For example, Product has an object of User, and User has an object of Product.
To solve this, you have to write
#JsonBackReference(value = "user-product")
private User user;
in the Product class,
and
#JsonManagedReference(value = "user-product")
private Product product;
In the user class.
Do it in every field and for every class that call each other.
Also, Check this out
JPA: Having lists on both ends without infinite loop
You have cycles in your data model. For example, Product holds Images and Images point back to Products.
This works in an object oriented world, because only pointer references are stored in those fields.
When serialized, however, the actual object is written out as json text. Your Product prints the Images object which in turn prints the Product object which again prints the Image object and so on.
You need to decide how you want to represent your json, map your database model into simple plain old java object and use this for serializations. These POJOs are often called View Model or Transport Objects.

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